ftpserv.c
上传用户:hepax88
上传日期:2007-01-03
资源大小:1101k
文件大小:16k
源码类别:

TCP/IP协议栈

开发平台:

Visual C++

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