home *** CD-ROM | disk | FTP | other *** search
/ Media Share 9 / MEDIASHARE_09.ISO / hamradio / s920603.zip / FTPSERV.C < prev    next >
C/C++ Source or Header  |  1992-06-03  |  16KB  |  659 lines

  1. /* Internet FTP Server
  2.  * Copyright 1991 Phil Karn, KA9Q
  3.  */
  4. #include <stdio.h>
  5. #include <ctype.h>
  6. #include <time.h>
  7. #ifdef    __TURBOC__
  8. #include <io.h>
  9. #include <dir.h>
  10. #endif
  11. #include "global.h"
  12. #include "mbuf.h"
  13. #include "proc.h"
  14. #include "socket.h"
  15. #include "dirutil.h"
  16. #include "commands.h"
  17. #include "files.h"
  18. #include "ftp.h"
  19. #include "ftpserv.h"
  20.  
  21. static void ftpserv __ARGS((int s,void *unused,void *p));
  22. static int pport __ARGS((struct sockaddr_in *sock,char *arg));
  23. static void ftplogin __ARGS((struct ftpserv *ftp,char *pass));
  24. static int sendit __ARGS((struct ftpserv *ftp,char *command,char *file));
  25. static int recvit __ARGS((struct ftpserv *ftp,char *command,char *file));
  26.  
  27. /* Command table */
  28. static char *commands[] = {
  29.     "user",
  30.     "acct",
  31.     "pass",
  32.     "type",
  33.     "list",
  34.     "cwd",
  35.     "dele",
  36.     "name",
  37.     "quit",
  38.     "retr",
  39.     "stor",
  40.     "port",
  41.     "nlst",
  42.     "pwd",
  43.     "xpwd",            /* For compatibility with 4.2BSD */
  44.     "mkd ",
  45.     "xmkd",            /* For compatibility with 4.2BSD */
  46.     "xrmd",            /* For compatibility with 4.2BSD */
  47.     "rmd ",
  48.     "stru",
  49.     "mode",
  50.     NULLCHAR
  51. };
  52.  
  53. /* Response messages */
  54. static char banner[] = "220 %s FTP version %s ready at %s\n";
  55. static char badcmd[] = "500 Unknown command\n";
  56. static char binwarn[] = "100 Warning: type is ASCII and %s appears to be binary\n";
  57. static char unsupp[] = "500 Unsupported command or option\n";
  58. static char givepass[] = "331 Enter PASS command\n";
  59. static char logged[] = "230 Logged in\n";
  60. static char typeok[] = "200 Type %s OK\n";
  61. static char only8[] = "501 Only logical bytesize 8 supported\n";
  62. static char deleok[] = "250 File deleted\n";
  63. static char mkdok[] = "200 MKD ok\n";
  64. static char delefail[] = "550 Delete failed: %s\n";
  65. static char pwdmsg[] = "257 \"%s\" is current directory\n";
  66. static char badtype[] = "501 Unknown type \"%s\"\n";
  67. static char badport[] = "501 Bad port syntax\n";
  68. static char unimp[] = "502 Command not yet implemented\n";
  69. static char bye[] = "221 Goodbye!\n";
  70. static char nodir[] = "553 Can't read directory \"%s\": %s\n";
  71. static char cantopen[] = "550 Can't read file \"%s\": %s\n";
  72. static char sending[] = "150 Opening data connection for %s %s\n";
  73. static char cantmake[] = "553 Can't create \"%s\": %s\n";
  74. static char writerr[] = "552 Write error: %s\n";
  75. static char portok[] = "200 Port command okay\n";
  76. static char rxok[] = "226 File received OK\n";
  77. static char txok[] = "226 File sent OK\n";
  78. static char noperm[] = "550 Permission denied\n";
  79. static char noconn[] = "425 Data connection reset\n";
  80. static char lowmem[] = "421 System overloaded, try again later\n";
  81. static char notlog[] = "530 Please log in with USER and PASS\n";
  82. static char userfirst[] = "503 Login with USER first.\n";
  83. static char okay[] = "200 Ok\n";
  84.  
  85. static int Sftp = -1;    /* Prototype socket for service */
  86.  
  87. /* Start up FTP service */
  88. int
  89. ftpstart(argc,argv,p)
  90. int argc;
  91. char *argv[];
  92. void *p;
  93. {
  94.     struct sockaddr_in lsocket;
  95.     int s;
  96.     FILE *network;
  97.  
  98.     if(Sftp != -1){
  99.         /* Already running! */
  100.         return 0;
  101.     }
  102.     psignal(Curproc,0);    /* Don't keep the parser waiting */
  103.     chname(Curproc,"FTP listener");
  104.  
  105.     lsocket.sin_family = AF_INET;
  106.     lsocket.sin_addr.s_addr = INADDR_ANY;
  107.     if(argc < 2)
  108.         lsocket.sin_port = IPPORT_FTP;
  109.     else
  110.         lsocket.sin_port = atoi(argv[1]);
  111.  
  112.     Sftp = socket(AF_INET,SOCK_STREAM,0);
  113.     bind(Sftp,(char *)&lsocket,sizeof(lsocket));
  114.     listen(Sftp,1);
  115.     for(;;){
  116.         if((s = accept(Sftp,NULLCHAR,(int *)NULL)) == -1)
  117.             break;    /* Service is shutting down */
  118.  
  119.         network = fdopen(s,"r+t");
  120.         if(availmem() != 0){
  121.             fprintf(network,lowmem);
  122.             fclose(network);
  123.         } else {
  124.             /* Spawn a server */
  125.             newproc("ftpserv",2048,ftpserv,s,(void *)network,NULL,0);
  126.         }
  127.     }
  128.     return 0;
  129. }
  130. static void
  131. ftpserv(s,n,p)
  132. int s;    /* Socket with user connection */
  133. void *n;
  134. void *p;
  135. {
  136.     struct ftpserv ftp;
  137.     char **cmdp,buf[512],*arg,*cp,*cp1,*file,*mode;
  138.     long t;
  139.     int i;
  140.     struct sockaddr_in socket;
  141.  
  142.     memset((char *)&ftp,0,sizeof(ftp));    /* Start with clear slate */
  143.     ftp.control = (FILE *)n;
  144.     fclose(stdin);
  145.     stdin = fdup(ftp.control);
  146.     fclose(stdout);
  147.     stdout = fdup(ftp.control);
  148.  
  149.     sockowner(fileno(ftp.control),Curproc);        /* We own it now */
  150.     /* Set default data port */
  151.     i = SOCKSIZE;
  152.     getpeername(fileno(ftp.control),(char *)&socket,&i);
  153.     socket.sin_port = IPPORT_FTPD;
  154.     ASSIGN(ftp.port,socket);
  155.  
  156.     log(fileno(ftp.control),"open FTP");
  157.     time(&t);
  158.     cp = ctime(&t);
  159.     if((cp1 = strchr(cp,'\n')) != NULLCHAR)
  160.         *cp1 = '\0';
  161.     fprintf(ftp.control,banner,Hostname,Version,cp);
  162. loop:    fflush(ftp.control);
  163.     if((fgets(buf,sizeof(buf),ftp.control)) == NULLCHAR){
  164.         /* He closed on us */
  165.         goto finish;
  166.     }
  167.     if(strlen(buf) == 0){
  168.         /* Can't be a legal FTP command */
  169.         fprintf(ftp.control,badcmd);
  170.         goto loop;
  171.     }    
  172.     rip(buf);
  173. #ifdef    UNIX
  174.     /* Translate first word to lower case */
  175.     for(cp = buf;*cp != ' ' && *cp != '\0';cp++)
  176.         *cp = tolower(*cp);
  177. #else
  178.     /* Translate entire buffer to lower case */
  179.     for(cp = buf;*cp != '\0';cp++)
  180.         *cp = tolower(*cp);
  181. #endif
  182.     /* Find command in table; if not present, return syntax error */
  183.     for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
  184.         if(strncmp(*cmdp,buf,strlen(*cmdp)) == 0)
  185.             break;
  186.     if(*cmdp == NULLCHAR){
  187.         fprintf(ftp.control,badcmd);
  188.         goto loop;
  189.     }
  190.     /* Allow only USER, PASS and QUIT before logging in */
  191.     if(ftp.cd == NULLCHAR || ftp.path == NULLCHAR){
  192.         switch(cmdp-commands){
  193.         case USER_CMD:
  194.         case PASS_CMD:
  195.         case QUIT_CMD:
  196.             break;
  197.         default:
  198.             fprintf(ftp.control,notlog);
  199.             goto loop;
  200.         }
  201.     }
  202.     arg = &buf[strlen(*cmdp)];
  203.     while(*arg == ' ')
  204.         arg++;
  205.  
  206.     /* Execute specific command */
  207.     switch(cmdp-commands){
  208.     case USER_CMD:
  209.         free(ftp.username);
  210.         ftp.username = strdup(arg);
  211.         fprintf(ftp.control,givepass);
  212.         break;
  213.     case TYPE_CMD:
  214.         switch(arg[0]){
  215.         case 'A':
  216.         case 'a':    /* Ascii */
  217.             ftp.type = ASCII_TYPE;
  218.             fprintf(ftp.control,typeok,arg);
  219.             break;
  220.         case 'l':
  221.         case 'L':
  222.             while(*arg != ' ' && *arg != '\0')
  223.                 arg++;
  224.             if(*arg == '\0' || *++arg != '8'){
  225.                 fprintf(ftp.control,only8);
  226.                 break;
  227.             }
  228.             ftp.type = LOGICAL_TYPE;
  229.             ftp.logbsize = 8;
  230.             fprintf(ftp.control,typeok,arg);
  231.             break;
  232.         case 'B':
  233.         case 'b':    /* Binary */
  234.         case 'I':
  235.         case 'i':    /* Image */
  236.             ftp.type = IMAGE_TYPE;
  237.             fprintf(ftp.control,typeok,arg);
  238.             break;
  239.         default:    /* Invalid */
  240.             fprintf(ftp.control,badtype,arg);
  241.             break;
  242.         }
  243.         break;
  244.     case QUIT_CMD:
  245.         fprintf(ftp.control,bye);
  246.         goto finish;
  247.     case RETR_CMD:
  248.         file = pathname(ftp.cd,arg);
  249.         switch(ftp.type){
  250.         case IMAGE_TYPE:
  251.         case LOGICAL_TYPE:
  252.             mode = READ_BINARY;
  253.             break;
  254.         case ASCII_TYPE:
  255.             mode = READ_TEXT;
  256.             break;
  257.         }
  258.         if(!permcheck(ftp.path,ftp.perms,RETR_CMD,file)){
  259.              fprintf(ftp.control,noperm);
  260.         } else if((ftp.fp = fopen(file,mode)) == NULLFILE){
  261.             fprintf(ftp.control,cantopen,file,sys_errlist[errno]);
  262.         } else {
  263.             log(fileno(ftp.control),"RETR %s",file);
  264.             if(ftp.type == ASCII_TYPE && isbinary(ftp.fp)){
  265.                 fprintf(ftp.control,binwarn,file);
  266.             }
  267.             sendit(&ftp,"RETR",file);
  268.         }
  269.         free(file);
  270.         break;
  271.     case STOR_CMD:
  272.         file = pathname(ftp.cd,arg);
  273.         switch(ftp.type){
  274.         case IMAGE_TYPE:
  275.         case LOGICAL_TYPE:
  276.             mode = WRITE_BINARY;
  277.             break;
  278.         case ASCII_TYPE:
  279.             mode = WRITE_TEXT;
  280.             break;
  281.         }
  282.         if(!permcheck(ftp.path,ftp.perms,STOR_CMD,file)){
  283.              fprintf(ftp.control,noperm);
  284.         } else if((ftp.fp = fopen(file,mode)) == NULLFILE){
  285.             fprintf(ftp.control,cantmake,file,sys_errlist[errno]);
  286.         } else {
  287.             log(fileno(ftp.control),"STOR %s",file);
  288.             recvit(&ftp,"STOR",file);
  289.         }
  290.         free(file);
  291.         break;
  292.     case PORT_CMD:
  293.         if(pport(&ftp.port,arg) == -1){
  294.             fprintf(ftp.control,badport);
  295.         } else {
  296.             fprintf(ftp.control,portok);
  297.         }
  298.         break;
  299. #ifndef CPM
  300.     case LIST_CMD:
  301.         file = pathname(ftp.cd,arg);
  302.         if(!permcheck(ftp.path,ftp.perms,RETR_CMD,file)){
  303.              fprintf(ftp.control,noperm);
  304.         } else if((ftp.fp = dir(file,1)) == NULLFILE){
  305.             fprintf(ftp.control,nodir,file,sys_errlist[errno]);
  306.         } else {
  307.             sendit(&ftp,"LIST",file);
  308.         }
  309.         free(file);
  310.         break;
  311.     case NLST_CMD:
  312.         file = pathname(ftp.cd,arg);
  313.         if(!permcheck(ftp.path,ftp.perms,RETR_CMD,file)){
  314.              fprintf(ftp.control,noperm);
  315.         } else if((ftp.fp = dir(file,0)) == NULLFILE){
  316.             fprintf(ftp.control,nodir,file,sys_errlist[errno]);
  317.         } else {
  318.             sendit(&ftp,"NLST",file);
  319.         }
  320.         free(file);
  321.         break;
  322.     case CWD_CMD:
  323.         file = pathname(ftp.cd,arg);
  324.         if(!permcheck(ftp.path,ftp.perms,RETR_CMD,file)){
  325.              fprintf(ftp.control,noperm);
  326.             free(file);
  327. #ifdef    MSDOS
  328.         /* Don'tcha just LOVE %%$#@!! MS-DOS? */
  329.         } else if(strcmp(file,"/") == 0 || access(file,0) == 0){
  330. #else
  331.         } else if(access(file,0) == 0){    /* See if it exists */
  332. #endif
  333.             /* Succeeded, record in control block */
  334.             free(ftp.cd);
  335.             ftp.cd = file;
  336.             fprintf(ftp.control,pwdmsg,file);
  337.         } else {
  338.             /* Failed, don't change anything */
  339.             fprintf(ftp.control,nodir,file,sys_errlist[errno]);
  340.             free(file);
  341.         }
  342.         break;
  343.     case XPWD_CMD:
  344.     case PWD_CMD:
  345.         fprintf(ftp.control,pwdmsg,ftp.cd);
  346.         break;
  347. #else
  348.     case LIST_CMD:
  349.     case NLST_CMD:
  350.     case CWD_CMD:
  351.     case XPWD_CMD:
  352.     case PWD_CMD:
  353. #endif
  354.     case ACCT_CMD:        
  355.         fprintf(ftp.control,unimp);
  356.         break;
  357.     case DELE_CMD:
  358.         file = pathname(ftp.cd,arg);
  359.         if(!permcheck(ftp.path,ftp.perms,DELE_CMD,file)){
  360.              fprintf(ftp.control,noperm);
  361.         } else if(unlink(file) == 0){
  362.             log(fileno(ftp.control),"DELE %s",file);
  363.             fprintf(ftp.control,deleok);
  364.         } else {
  365.             fprintf(ftp.control,delefail,sys_errlist[errno]);
  366.         }
  367.         free(file);
  368.         break;
  369.     case PASS_CMD:
  370.         if(ftp.username == NULLCHAR)
  371.             fprintf(ftp.control,userfirst);
  372.         else
  373.             ftplogin(&ftp,arg);            
  374.         break;
  375. #ifndef    CPM
  376.     case XMKD_CMD:
  377.     case MKD_CMD:
  378.         file = pathname(ftp.cd,arg);
  379.         if(!permcheck(ftp.path,ftp.perms,MKD_CMD,file)){
  380.             fprintf(ftp.control,noperm);
  381. #ifdef    UNIX
  382.         } else if(mkdir(file,0777) == 0){
  383. #else
  384.         } else if(mkdir(file) == 0){
  385. #endif
  386.             log(fileno(ftp.control),"MKD %s",file);
  387.             fprintf(ftp.control,mkdok);
  388.         } else {
  389.             fprintf(ftp.control,cantmake,file,sys_errlist[errno]);
  390.         }
  391.         free(file);
  392.         break;
  393.     case XRMD_CMD:
  394.     case RMD_CMD:
  395.         file = pathname(ftp.cd,arg);
  396.         if(!permcheck(ftp.path,ftp.perms,RMD_CMD,file)){
  397.              fprintf(ftp.control,noperm);
  398.         } else if(rmdir(file) == 0){
  399.             log(fileno(ftp.control),"RMD %s",file);
  400.             fprintf(ftp.control,deleok);
  401.         } else {
  402.             fprintf(ftp.control,delefail,sys_errlist[errno]);
  403.         }
  404.         free(file);
  405.         break;
  406.     case STRU_CMD:
  407.         if(tolower(arg[0]) != 'f')
  408.             fprintf(ftp.control,unsupp);
  409.         else
  410.             fprintf(ftp.control,okay);
  411.         break;
  412.     case MODE_CMD:
  413.         if(tolower(arg[0]) != 's')
  414.             fprintf(ftp.control,unsupp);
  415.         else
  416.             fprintf(ftp.control,okay);
  417.         break;
  418.     }
  419. #endif
  420.     goto loop;
  421. finish:
  422.     log(fileno(ftp.control),"close FTP");
  423.     /* Clean up */
  424.     fclose(ftp.control);
  425.     if(ftp.data != NULLFILE)
  426.         fclose(ftp.data);
  427.     if(ftp.fp != NULLFILE)
  428.         fclose(ftp.fp);
  429.     free(ftp.username);
  430.     free(ftp.path);
  431.     free(ftp.cd);
  432. }
  433.  
  434. /* Shut down FTP server */
  435. int
  436. ftp0(argc,argv,p)
  437. int argc;
  438. char *argv[];
  439. void *p;
  440. {
  441.     close_s(Sftp);
  442.     Sftp = -1;
  443.     return 0;
  444. }
  445. static
  446. int
  447. pport(sock,arg)
  448. struct sockaddr_in *sock;
  449. char *arg;
  450. {
  451.     int32 n;
  452.     int i;
  453.  
  454.     n = 0;
  455.     for(i=0;i<4;i++){
  456.         n = atoi(arg) + (n << 8);
  457.         if((arg = strchr(arg,',')) == NULLCHAR)
  458.             return -1;
  459.         arg++;
  460.     }
  461.     sock->sin_addr.s_addr = n;
  462.     n = atoi(arg);
  463.     if((arg = strchr(arg,',')) == NULLCHAR)
  464.         return -1;
  465.     arg++;
  466.     n = atoi(arg) + (n << 8);
  467.     sock->sin_port = n;
  468.     return 0;
  469. }
  470.  
  471. /* Attempt to log in the user whose name is in ftp->username and password
  472.  * in pass
  473.  */
  474. static void
  475. ftplogin(ftp,pass)
  476. struct ftpserv *ftp;
  477. char *pass;
  478. {
  479.     char *path;
  480.     int anony = 0;
  481.  
  482.     path = mallocw(200);
  483.     if((ftp->perms = userlogin(ftp->username,pass,&path,200,&anony))
  484.        == -1){
  485.         fprintf(ftp->control,noperm);
  486.         free(path);
  487.         return;
  488.     }
  489.     /* Set up current directory and path prefix */
  490. #if    defined(AMIGAGONE)
  491.     ftp->cd = pathname("", path);
  492.     ftp->path = strdup(ftp->cd);
  493.     free(path);
  494. #else
  495.     ftp->cd = path;
  496.     ftp->path = strdup(path);
  497. #endif
  498.  
  499.     fprintf(ftp->control,logged);
  500.     if(!anony)
  501.         log(fileno(ftp->control),"%s logged in",ftp->username);
  502.     else
  503.         log(fileno(ftp->control),"%s logged in, ID %s",ftp->username,pass);
  504. }
  505.  
  506. #ifdef    MSDOS
  507. /* Illegal characters in a DOS filename */
  508. static char badchars[] = "\"[]:|<>+=;,";
  509. #endif
  510.  
  511. /* Return 1 if the file operation is allowed, 0 otherwise */
  512. int
  513. permcheck(path,perms,op,file)
  514. char *path;
  515. int perms;
  516. int op;
  517. char *file;
  518. {
  519. #ifdef    MSDOS
  520.     char *cp;
  521. #endif
  522.  
  523.     if(file == NULLCHAR || path == NULLCHAR)
  524.         return 0;    /* Probably hasn't logged in yet */
  525. #ifdef    MSDOS
  526.     /* Check for characters illegal in MS-DOS file names */
  527.     for(cp = badchars;*cp != '\0';cp++){
  528.         if(strchr(file,*cp) != NULLCHAR)
  529.             return 0;    
  530.     }
  531. #endif
  532. #ifndef MAC
  533.     /* The target file must be under the user's allowed search path */
  534.     if(strncmp(file,path,strlen(path)) != 0)
  535.         return 0;
  536. #endif
  537.  
  538.     switch(op){
  539.     case RETR_CMD:
  540.         /* User must have permission to read files */
  541.         if(perms & FTP_READ)
  542.             return 1;
  543.         return 0;
  544.     case DELE_CMD:
  545.     case RMD_CMD:
  546.         /* User must have permission to (over)write files */
  547.         if(perms & FTP_WRITE)
  548.             return 1;
  549.         return 0;
  550.     case STOR_CMD:
  551.     case MKD_CMD:
  552.         /* User must have permission to (over)write files, or permission
  553.          * to create them if the file doesn't already exist
  554.          */
  555.         if(perms & FTP_WRITE)
  556.             return 1;
  557.         if(access(file,2) == -1 && (perms & FTP_CREATE))
  558.             return 1;
  559.         return 0;
  560.     }
  561.     return 0;    /* "can't happen" -- keep lint happy */
  562. }
  563. static int
  564. sendit(ftp,command,file)
  565. struct ftpserv *ftp;
  566. char *command;
  567. char *file;
  568. {
  569.     long total;
  570.     struct sockaddr_in dport;
  571.     int s;
  572.  
  573.     s = socket(AF_INET,SOCK_STREAM,0);
  574.     dport.sin_family = AF_INET;
  575.     dport.sin_addr.s_addr = INADDR_ANY;
  576.     dport.sin_port = IPPORT_FTPD;
  577.     bind(s,(char *)&dport,SOCKSIZE);
  578.     fprintf(ftp->control,sending,command,file);
  579.     fflush(ftp->control);
  580.     if(connect(s,(char *)&ftp->port,SOCKSIZE) == -1){
  581.         fclose(ftp->fp);
  582.         ftp->fp = NULLFILE;
  583.         close_s(s);
  584.         ftp->data = NULLFILE;
  585.         fprintf(ftp->control,noconn);
  586.         return -1;
  587.     }
  588.     ftp->data = fdopen(s,"r+");
  589.     /* Do the actual transfer */
  590.     total = sendfile(ftp->fp,ftp->data,ftp->type,0);
  591.  
  592.     if(total == -1){
  593.         /* An error occurred on the data connection */
  594.         fprintf(ftp->control,noconn);
  595.         shutdown(fileno(ftp->data),2);    /* Blow away data connection */
  596.         fclose(ftp->data);
  597.     } else {
  598.         fprintf(ftp->control,txok);
  599.     }
  600.     fclose(ftp->fp);
  601.     ftp->fp = NULLFILE;
  602.     fclose(ftp->data);
  603.     ftp->data = NULLFILE;
  604.     if(total == -1)
  605.         return -1;
  606.     else
  607.         return 0;
  608. }
  609. static int
  610. recvit(ftp,command,file)
  611. struct ftpserv *ftp;
  612. char *command;
  613. char *file;
  614. {
  615.     struct sockaddr_in dport;
  616.     long total;
  617.     int s;
  618.  
  619.     s = socket(AF_INET,SOCK_STREAM,0);
  620.     dport.sin_family = AF_INET;
  621.     dport.sin_addr.s_addr = INADDR_ANY;
  622.     dport.sin_port = IPPORT_FTPD;
  623.     bind(s,(char *)&dport,SOCKSIZE);
  624.     fprintf(ftp->control,sending,command,file);
  625.     fflush(ftp->control);
  626.     if(connect(s,(char *)&ftp->port,SOCKSIZE) == -1){
  627.         fclose(ftp->fp);
  628.         ftp->fp = NULLFILE;
  629.         close_s(s);
  630.         ftp->data = NULLCHAR;
  631.         fprintf(ftp->control,noconn);
  632.         return -1;
  633.     }
  634.     ftp->data = fdopen(s,"r+");
  635.     /* Do the actual transfer */
  636.     total = recvfile(ftp->fp,ftp->data,ftp->type,0);
  637.  
  638. #ifdef    CPM
  639.     if(ftp->type == ASCII_TYPE)
  640.         putc(CTLZ,ftp->fp);
  641. #endif
  642.     if(total == -1) {
  643.         /* An error occurred while writing the file */
  644.         fprintf(ftp->control,writerr,sys_errlist[errno]);
  645.         shutdown(fileno(ftp->data),2);    /* Blow it away */
  646.         fclose(ftp->data);
  647.     } else {
  648.         fprintf(ftp->control,rxok);
  649.         fclose(ftp->data);
  650.     }
  651.     ftp->data = NULLFILE;
  652.     fclose(ftp->fp);
  653.     ftp->fp = NULLFILE;
  654.     if(total == -1)
  655.         return -1;
  656.     else
  657.         return 0;
  658. }
  659.