home *** CD-ROM | disk | FTP | other *** search
/ GEMini Atari / GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso / files / telecomm / ka9q_src / ftpserv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-07-28  |  16.2 KB  |  598 lines

  1. /* FTP Server state machine - see RFC 959 */
  2.  
  3. #define    LINELEN        128    /* Length of command buffer */
  4.  
  5. #include <stdio.h>
  6. #include "global.h"
  7. #include "mbuf.h"
  8. #include "netuser.h"
  9. #include "timer.h"
  10. #include "tcp.h"
  11. #include "ftp.h"
  12.  
  13. /* Command table */
  14. static char *commands[] = {
  15.     "user",
  16. #define    USER_CMD    0
  17.     "acct",
  18. #define    ACCT_CMD    1
  19.     "pass",
  20. #define    PASS_CMD    2
  21.     "type",
  22. #define    TYPE_CMD    3
  23.     "list",
  24. #define    LIST_CMD    4
  25.     "cwd",
  26. #define    CWD_CMD        5
  27.     "dele",
  28. #define    DELE_CMD    6
  29.     "name",
  30. #define    NAME_CMD    7
  31.     "quit",
  32. #define    QUIT_CMD    8
  33.     "retr",
  34. #define    RETR_CMD    9
  35.     "stor",
  36. #define    STOR_CMD    10
  37.     "port",
  38. #define    PORT_CMD    11
  39.     "nlst",
  40. #define    NLST_CMD    12
  41.     "pwd",
  42. #define    PWD_CMD        13
  43.     "xpwd",            /* For compatibility with 4.2BSD */
  44. #define    XPWD_CMD    14
  45.     "mkd ",
  46. #define    MKD_CMD        15
  47.     "xmkd",            /* For compatibility with 4.2BSD */
  48. #define    XMKD_CMD    16
  49.     "xrmd",            /* For compatibility with 4.2BSD */
  50. #define    XRMD_CMD    17
  51.     "rmd ",
  52. #define    RMD_CMD        18
  53.     NULLCHAR
  54. };
  55.  
  56. /* Response messages */
  57. static char banner[] = "220 %s FTP version %s ready at %s\r\n";
  58. static char badcmd[] = "500 Unknown command\r\n";
  59. static char givepass[] = "331 Enter PASS command\r\n";
  60. static char logged[] = "230 Logged in\r\n";
  61. static char typeok[] = "200 Type OK\r\n";
  62. static char deleok[] = "250 File deleted\r\n";
  63. static char mkdok[] = "200 MKD ok\r\n";
  64. static char delefail[] = "550 Delete failed\r\n";
  65. static char pwdmsg[] = "257 \"%s\" is current directory\r\n";
  66. static char badtype[] = "501 Unknown type \"%s\"\r\n";
  67. static char badport[] = "501 Bad port syntax\r\n";
  68. static char unimp[] = "502 Command not yet implemented\r\n";
  69. static char bye[] = "221 Goodbye!\r\n";
  70. static char nodir[] = "553 Can't read directory \"%s\"\r\n";
  71. static char cantopen[] = "550 Can't read file \"%s\"\r\n";
  72. static char sending[] = "150 Opening data connection for %s %s\r\n";
  73. static char cantmake[] = "553 Can't create \"%s\"\r\n";
  74. static char portok[] = "200 Port command okay\r\n";
  75. static char rxok[] = "226 File received OK\r\n";
  76. static char txok[] = "226 File sent OK\r\n";
  77. static char noperm[] = "550 Permission denied\r\n";
  78. static char noconn[] = "425 Data connection refused\r\n";
  79. static char notlog[] = "530 Please log in with USER and PASS\r\n";
  80.  
  81. static struct tcb *ftp_tcb;
  82.  
  83. /* Start up FTP service */
  84. ftp_start(argc,argv)
  85. int argc;
  86. char *argv[];
  87. {
  88.     struct socket lsocket;
  89.     void ftpscr(),ftpscs();
  90.  
  91.     lsocket.address = ip_addr;
  92.     if(argc < 2)
  93.         lsocket.port = FTP_PORT;
  94.     else
  95.         lsocket.port = atoi(argv[1]);
  96.  
  97.     ftp_tcb = open_tcp(&lsocket,NULLSOCK,TCP_SERVER,0,ftpscr,NULLVFP,ftpscs,0,(char *)NULL);
  98. }
  99. ftp_stop()
  100. {
  101.     if(ftp_tcb != NULLTCB)
  102.         close_tcp(ftp_tcb);
  103. }
  104. /* FTP Server Control channel State change upcall handler */
  105. static
  106. void
  107. ftpscs(tcb,old,new)
  108. struct tcb *tcb;
  109. char old,new;
  110. {
  111.     extern char hostname[],version[];
  112.     struct ftp *ftp,*ftp_create();
  113.     void ftp_delete();
  114.     char *inet_ntoa();
  115.     long t;
  116.     char *cp,*cp1;
  117.  
  118.     switch(new){
  119. /* Setting QUICKSTART piggybacks the server's banner on the SYN/ACK segment;
  120.  * leaving it unset waits for the three-way handshake to complete before
  121.  * sending the banner. Piggybacking unfortunately breaks some old TCPs,
  122.  * so its use is not (yet) recommended.
  123. */
  124. #ifdef    QUICKSTART
  125.     case SYN_RECEIVED:
  126. #else
  127.     case ESTABLISHED:
  128. #endif
  129.         if((ftp = ftp_create(LINELEN)) == NULLFTP){
  130.             /* No space, kill connection */
  131.             close_tcp(tcb);
  132.             return;
  133.         }
  134.         ftp->control = tcb;        /* Downward link */
  135.         tcb->user = (char *)ftp;    /* Upward link */
  136.  
  137.         /* Set default data port */
  138.         ftp->port.address = tcb->conn.remote.address;
  139.         ftp->port.port = FTPD_PORT;
  140.  
  141.         /* Note current directory */
  142.         log(tcb,"open FTP");
  143.         time(&t);
  144.         cp = ctime(&t);
  145.         if((cp1 = index(cp,'\n')) != NULLCHAR)
  146.             *cp1 = '\0';
  147.         tprintf(ftp->control,banner,hostname,version,cp);
  148.         break;        
  149.     case CLOSE_WAIT:
  150.         close_tcp(tcb);
  151.         break;
  152.     case CLOSED:
  153.         log(tcb,"close FTP");
  154.         if((ftp = (struct ftp *)tcb->user) != NULLFTP)
  155.             ftp_delete(ftp);
  156.         /* Check if server is being shut down */
  157.         if(tcb == ftp_tcb)
  158.             ftp_tcb = NULLTCB;
  159.         del_tcp(tcb);
  160.         break;
  161.     }
  162. }
  163.  
  164. /* FTP Server Control channel Receiver upcall handler */
  165. static
  166. void
  167. ftpscr(tcb,cnt)
  168. struct tcb *tcb;
  169. int16 cnt;
  170. {
  171.     register struct ftp *ftp;
  172.     char c;
  173.     struct mbuf *bp;
  174.     void ftpcommand();
  175.  
  176.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  177.         /* Unknown connection, just kill it */
  178.         close_tcp(tcb);
  179.         return;
  180.     }
  181.     switch(ftp->state){
  182.     case COMMAND_STATE:
  183.         /* Assemble an input line in the session buffer. Return if incomplete */
  184.         recv_tcp(tcb,&bp,0);
  185.         while(pullup(&bp,&c,1) == 1){
  186.             switch(c){
  187.             case '\r':    /* Strip cr's */
  188.                 continue;
  189.             case '\n':    /* Complete line; process it */
  190.                 ftp->buf[ftp->cnt] = '\0';
  191.                 ftpcommand(ftp);
  192.                 ftp->cnt = 0;
  193.                 break;
  194.             default:    /* Assemble line */
  195.                 if(ftp->cnt != LINELEN-1)
  196.                     ftp->buf[ftp->cnt++] = c;
  197.                 break;
  198.             }
  199.         }
  200.         /* else no linefeed present yet to terminate command */
  201.         break;
  202.     case SENDING_STATE:
  203.     case RECEIVING_STATE:
  204.         /* Leave commands pending on receive queue tntil
  205.          * present command is done
  206.          */
  207.         break;
  208.     }
  209. }
  210.  
  211. /* FTP server data channel connection state change upcall handler */
  212. void
  213. ftpsds(tcb,old,new)
  214. struct tcb *tcb;
  215. char ol
  216. char ol
  217. char ol
  218. char ol
  219. char ol    close_tcp(tcb);
  220.         if(ftp->state == RECEIVING_STATE){
  221.             /* End of file received on incoming file */
  222. #ifdef    CPM
  223.             if(ftp->type == ASCII_TYPE)
  224.                 putc(CTLZ,ftp->fp);
  225. #endif
  226.             if(ftp->fp != stdout)
  227.                 fclose(ftp->fp);
  228.             ftp->fp = NULLFILE;
  229.             ftp->state = COMMAND_STATE;
  230.             if(current != NULLSESSION && current->cb.ftp == ftp){
  231.                 printf("Get complete, %lu bytes received\n",
  232.                     tcb->rcv.nxt - tcb->irs - 2);
  233.                 fflush(stdout);
  234.             }
  235.         }
  236.         break;
  237.     case CLOSED:
  238.         ftp->data = NULLTCB;
  239.         del_tcp(tcb);
  240.         break;
  241.     }
  242. }
  243. /* Send a message on the control channel */
  244. /*VARARGS*/
  245. static
  246. int
  247. sndftpmsg(ftp,fmt,arg)
  248. struct ftp *ftp;
  249. char *fmt;
  250. char *arg;
  251. {
  252.     struct mbuf *bp;
  253.     int16 len;
  254.  
  255.     len = strlen(fmt) + strlen(arg) + 10;    /* fudge factor */
  256.     if((bp = alloc_mbuf(len)) == NULLBUF){
  257.         printf(nospace);
  258.         return 1;
  259.     }
  260.     sprintf(bp->data,fmt,arg);
  261.     bp->cnt = strlen(bp->data);
  262.     send_tcp(ftp->control,bp);
  263.     return 0;
  264. }
  265. otlog);
  266.             return;
  267.         }
  268.     }
  269.     arg = &cmd[strlen(*cmdp)];
  270.     while(*arg == ' ')
  271.         arg++;
  272.  
  273.     /* Execute specific command */
  274.     switch(cmdp-commands){
  275.     case USER_CMD:
  276.         if((ftp->username = malloc((unsigned)strlen(arg)+1)) == NULLCHAR){
  277.             close_tcp(ftp->control);
  278.             break;
  279.         }
  280.         strcpy(ftp->username,arg);
  281.         tprintf(ftp->control,givepass);
  282.         break;
  283.     case TYPE_CMD:
  284.         switch(arg[0]){
  285.         case 'A':
  286.         case 'a':    /* Ascii */
  287.             ftp->type = ASCII_TYPE;
  288.             tprintf(ftp->control,typeok);
  289.             break;
  290.         case 'B':
  291.         case 'b':    /* Binary */
  292.         case 'I':
  293.         case 'i':    /* Image */
  294.             ftp->type = IMAGE_TYPE;
  295.             tprintf(ftp->control,typeok);
  296.             break;
  297.         default:    /* Invalid */
  298.             tprintf(ftp->control,badtype,arg);
  299.             break;
  300.         }
  301.         break;
  302.     case QUIT_CMD:
  303.         tprintf(ftp->control,bye);
  304.         close_tcp(ftp->control);
  305.         break;
  306.     case RETR_CMD:
  307.         /* Disk operation; return ACK now */
  308.         tcp_output(ftp->control);
  309.         file = pathname(ftp->cd,arg);
  310.         if(ftp->type == IMAGE_TYPE)
  311.             mode = binmode[READ_BINARY];
  312.         else
  313.             mode = "r";
  314.         if(!permcheck(ftp,RETR_CMD,file)){
  315.              tprintf(ftp->control,noperm);
  316.         } else if((ftp->fp = fopen(file,mode)) == NULLFILE){
  317.             tprintf(ftp->control,cantopen,file);
  318.         } else {
  319.             log(ftp->control,"RETR %s\\%s",ftp->cd,arg); /* DG2KK: was "7" */
  320.             dport.address = ip_addr;
  321.             dport.port = FTPD_PORT;
  322.             ftp->state = SENDING_STATE;
  323.             tprintf(ftp->control,sending,"RETR",arg);
  324.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  325.              0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(char *)ftp);
  326.         }
  327.         free(file);
  328.         break;
  329.     case STOR_CMD:
  330.         /* Disk operation; return ACK now */
  331.         tcp_output(ftp->control);
  332.         file = pathname(ftp->cd,arg);
  333.         if(ftp->type == IMAGE_TYPE)
  334.             mode = binmode[WRITE_BINARY];
  335.         else
  336.             mode = "w";
  337.         if(!permcheck(ftp,STOR_CMD,file)){
  338.              tprintf(ftp->control,noperm);
  339.             free(file);
  340.              break;
  341.         } else if((ftp->fp = fopen(file,mode)) == NULLFILE){
  342.             tprintf(ftp->control,cantmake,file);
  343.         } else {
  344.             log(ftp->control,"STOR %s\\%s",ftp->cd,arg); /* DG2KK */
  345.             dport.address = ip_addr;
  346.             dport.port = FTPD_PORT;
  347.             ftp->state = RECEIVING_STATE;
  348.             tprintf(ftp->control,sending,"STOR",arg);
  349.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  350.              0,ftpdr,NULLVFP,ftpsds,ftp->control->tos,(char *)ftp);
  351.         }
  352.         free(file);
  353.         break;
  354.     case PORT_CMD:
  355.         if(pport(&ftp->port,arg) == -1){
  356.             tprintf(ftp->control,badport);
  357.         } else {
  358.             tprintf(ftp->control,portok);
  359.         }
  360.         break;
  361. #ifndef CPM
  362.     case LIST_CMD:
  363.         /* Disk operation; return ACK now */
  364.         tcp_output(ftp->control);
  365.  
  366.         file = pathname(ftp->cd,arg);
  367.         if(!permcheck(ftp,RETR_CMD,file)){
  368.              tprintf(ftp->control,noperm);
  369.         } else if((ftp->fp = dir(file,1)) == NULLFILE){
  370.             tprintf(ftp->control,nodir,file);
  371.         } else {
  372.             dport.address = ip_addr;
  373.             dport.port = FTPD_PORT;
  374.             ftp->state = SENDING_STATE;
  375.             tprintf(ftp->control,sending,"LIST",file);
  376.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  377.              0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(char *)ftp);
  378.         }
  379.         free(file);
  380.         break;
  381.     case NLST_CMD:
  382.         /* Disk operation; return ACK now */
  383.         tcp_output(ftp->control);
  384.  
  385.         file = pathname(ftp->cd,arg);
  386.         if(!permcheck(ftp,RETR_CMD,file)){
  387.              tprintf(ftp->control,noperm);
  388.         } else if((ftp->fp = dir(file,0)) == NULLFILE){
  389.             tprintf(ftp->control,nodir,file);
  390.         } else {
  391.             dport.address = ip_addr;
  392.             dport.port = FTPD_PORT;
  393.             ftp->state = SENDING_STATE;
  394.             tprintf(ftp->control,sending,"NLST",file);
  395.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  396.              0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(char *)ftp);
  397.         }
  398.         free(file);
  399.         break;
  400.     case CWD_CMD:
  401.         tcp_output(ftp->control);    /* Disk operation; return ACK now */
  402.  
  403.         file = pathname(ftp->cd,arg);
  404.         if(!permcheck(ftp,RETR_CMD,file)){
  405.              tprintf(ftp->control,noperm);
  406.             free(file);
  407. #if ( MSDOS || ATARI_ST )
  408.         /* Don'tcha just LOVE %%$#@!! MS-DOS? */
  409.         } else if(strcmp(file,"\\") == 0 || access(file,0) == 0){
  410. #else
  411.         } else if(access(file,0) == 0){    /* See if it exists */
  412. #endif
  413.             /* Succeeded, record in control block */
  414.             free(ftp->cd);
  415.             ftp->cd = file;
  416.             tprintf(ftp->control,pwdmsg,file);
  417.         } else {
  418.             /* Failed, don't change anything */
  419.             tprintf(ftp->control,nodir,file);
  420.             free(file);
  421.         }
  422.         break;
  423.     case XPWD_CMD:
  424.     case PWD_CMD:
  425.         tprintf(ftp->control,pwdmsg,ftp->cd);
  426.         break;
  427. #else
  428.     case LIST_CMD:
  429.     case NLST_CMD:
  430.     case CWD_CMD:
  431.     case XPWD_CMD:
  432.     case PWD_CMD:
  433. #endif
  434.     case ACCT_CMD:        
  435.         tprintf(ftp->control,unimp);
  436.         break;
  437.     case DELE_CMD:
  438.         file = pathname(ftp->cd,arg);
  439.         if(!permcheck(ftp,DELE_CMD,file)){
  440.              tprintf(ftp->control,noperm);
  441.         } else if(unlink(file) == 0){
  442.             tprintf(ftp->control,deleok);
  443.         } else {
  444.             tprintf(ftp->control,delefail);
  445.         }
  446.         free(file);
  447.         break;
  448.     case PASS_CMD:
  449.         tcp_output(ftp->control);    /* Send the ack now */
  450.         ftplogin(ftp,arg);            
  451.         break;
  452. #ifndef    CPM
  453.     case XMKD_CMD:
  454.     case MKD_CMD:
  455.         file = pathname(ftp->cd,arg);
  456.         if(!permcheck(ftp,MKD_CMD,file)){
  457.             tprintf(ftp->control,noperm);
  458.         } else if(mkdir(file,0777) == 0){
  459.             tprintf(ftp->control,mkdok);
  460.         } else {
  461.             tprintf(ftp->control,cantmake);
  462.         }
  463.         free(file);
  464.         break;
  465.     case XRMD_CMD:
  466.     case RMD_CMD:
  467.         file = pathname(ftp->cd,arg);
  468.         if(!permcheck(ftp,RMD_CMD,file)){
  469.              tprintf(ftp->control,noperm);
  470.         } else if(rmdir(file) == 0){
  471.             tprintf(ftp->control,deleok);
  472.         } else {
  473.             tprintf(ftp->control,delefail);
  474.         }
  475.         free(file);
  476.         break;
  477.     }
  478. #endif
  479. }
  480. static
  481. int
  482. pport(sock,arg)
  483. struct socket *sock;
  484. char *arg;
  485. {
  486.     int32 n;
  487.     int atoi(),i;
  488.  
  489.     n = 0;
  490.     for(i=0;i<4;i++){
  491.         n = atoi(arg) + (n << 8);
  492.         if((arg = index(arg,',')) == NULLCHAR)
  493.             return -1;
  494.         arg++;
  495.     }
  496.     sock->address = n;
  497.     n = atoi(arg);
  498.     if((arg = index(arg,',')) == NULLCHAR)
  499.         return -1;
  500.     arg++;
  501.     n = atoi(arg) + (n << 8);
  502.     sock->port = n;
  503.     return 0;
  504. }
  505. /* Attempt to log in the user whose name is in ftp->username and password
  506.  * in pass
  507.  */
  508. static
  509. ftplogin(ftp,pass)
  510. struct ftp *ftp;
  511. char *pass;
  512. {
  513.     char buf[80],*cp,*cp1;
  514.     FILE *fp;
  515.     int anony = 0;
  516.  
  517.     if((fp = fopen(userfile,"r")) == NULLFILE){
  518.         /* Userfile doesn't exist */
  519.         tprintf(ftp->control,noperm);
  520.         return;
  521.     }
  522.     while(fgets(buf,sizeof(buf),fp),!feof(fp)){
  523.         if(buf[0] == '#')
  524.             continue;    /* Comment */
  525.         if((cp = index(buf,' ')) == NULLCHAR)
  526.             /* Bogus entry */
  527.             continue;
  528.         *cp++ = '\0';        /* Now points to password */
  529.         if(strcmp(ftp->username,buf) == 0)
  530.             break;        /* Found user name */
  531.     }
  532.     if(feof(fp)){
  533.         /* User name not found in file */
  534.         fclose(fp);
  535.         tprintf(ftp->control,noperm);
  536.         return;
  537.     }
  538.     fclose(fp);
  539.     /* Look for space after password field in file */
  540.     if((cp1 = index(cp,' ')) == NULLCHAR){
  541.         /* Invalid file entry */
  542.         tprintf(ftp->control,noperm);
  543.         return;
  544.     }
  545.     *cp1++ = '\0';    /* Now points to path field */
  546.     if(strcmp(cp,"*") == 0)
  547.         anony = 1;    /* User ID is password-free */
  548.     if(!anony && strcmp(cp,pass) != 0){
  549.         /* Password required, but wrong one given */
  550.         tprintf(ftp->control,noperm);
  551.         return;
  552.     }
  553.     if((cp = index(cp1-' ')) == NULLCHAR){
  554.         /* Permission field missing */
  555.         tprintf(ftp->control,noperm);
  556.         return;
  557.     }
  558.     *cp++ = '\0';    /* now points to permission field */
  559.  
  560.     /* Set up current directory and path prefix */
  561.     ftp->cd = malloc((unsigned)strlen(cp1)+1);
  562.     ftp->path = malloc((unsigned)strlen(cp1)+1);
  563.     strcpy(ftp->cd,cp1);
  564.     strcpy(ftp->path,cp1);
  565.     
  566.     /* And finally setally setally setally setally set            return 1;
  567.         return 0;
  568.     case DELE_CMD:
  569.     case RMD_CMD:
  570.         /* User must have permission to (over)write files */
  571.         if(ftp->perms & FTP_WRITE)
  572.             return 1;
  573.         return 0;
  574.     case STOR_CMD:
  575.     case MKD_CMD:
  576.         /* User must have permission to (over)write files, or permission
  577.          * to create them if the file doesn't already exist
  578.          */
  579.         if(ftp->perms & FTP_WRITE)
  580.             return 1;
  581. #ifdef LATTICE
  582.         /* DG2KK: Lattice's access() doesn't work.
  583.          */
  584.         fp = fopen(file,"r");
  585.         if(fp == NULLFILE && (ftp->perms & FTP_CREATE)) {
  586. #else
  587.         if(access(file,2) == -1 && (ftp->perms & FTP_CREATE)) {
  588. #endif
  589.             return 1;
  590.         }
  591. #ifdef LATTICE
  592.         fclose(fp);
  593. #endif
  594.         return 0;
  595.     }
  596.     return 0;    /* "can't happen" -- keep lint happy */
  597. }
  598.