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