根据RFC 959实现的一个简易版本,用户名和密码都只是写在代码中,也只提供了PORT模式而没有PASV模式。实现了基本的登录,上传下载以及目录的变更(用leapftp,浏览器的ftp://均测试过),后续会继续写一些其他功能,尽量做成一个像样的ftpserver,但新日志只贴新函数就好,不想把一个日志搞太大。


#include "unpheader.h"
char *env_init[] ={"LC_ALL=en_US",NULL};

struct ftpuser{
 char dataip[20];
 int dataport;
 int loginok;
 int userok;
};

static int listenport=8021;
void init_ftpuser(struct ftpuser *myftp)
{
 memset(myftp->dataip,'�',20);
 myftp->dataport = 0;
 myftp->loginok = 0;
 myftp->userok = 0;
}

void do_stor(int connfd,char *storfile,struct ftpuser *myftp)
{
 char *storstart = "150 Opening data connection.rn";
 char *storend = "226 Transfer completed.rn";
 umask(S_IWGRP | S_IWOTH);
 int filefd = open(storfile,O_RDWR | O_CREAT,RWRWRW);
 write(connfd,storstart,strlen(storstart));
 int datasockfd = getconnection(myftp->dataip,myftp->dataport);
 char buf[MAXLINE];
 int n=0;
 while((n = read(datasockfd,buf,MAXLINE)) > 0)
 {
   buf[n]='�';
   write(filefd,buf,n);
 }
 close(datasockfd);
 write(connfd,storend,strlen(storend));
 return;
}

void do_retr(int connfd,char *retrfile,struct ftpuser *myftp)
{
 char *retrstart = "150 Opening data connection.rn";
 char *retrend = "226 Transfer completed.rn";
 char *retrnofile = "503 no such file.rn"; 
 int filefd;
 if( (filefd = open(retrfile,O_RDONLY) ) != -1)
 {
  write(connfd,retrstart,strlen(retrstart));
  int datasockfd = getconnection(myftp->dataip,myftp->dataport);
  char buf[MAXLINE];
  int n=0;
  while((n = read(filefd,buf,MAXLINE)) > 0)
  {
   buf[n]='�';
   write(datasockfd,buf,n);
  }
  close(datasockfd);
  write(connfd,retrend,strlen(retrend));
 }
 else
  write(connfd,retrnofile,strlen(retrnofile));
 return;
}

void do_mode(int connfd,char *modetype)
{
 char *modeok = "200 stream mode is okrn";
 char *modefail = "500 only stream mode is supportrn";
 if(strncmp(modetype,"S",1) == 0)
  write(connfd,modeok,strlen(modeok));
 else
  write(connfd,modefail,strlen(modefail));
 return;
}

void do_type(int connfd,char *type)
{
 char *settypeI = "200 type set to I.rn";
 char *settypeA = "200 type set to A.rn";
 char *seterror = "500 parm error.rn";
 if(strncasecmp(type,"I",1) == 0)
  write(connfd,settypeI,strlen(settypeI));
 else if(strncasecmp(type,"A",1) == 0)
  write(connfd,settypeA,strlen(settypeA));
 else
  write(connfd,seterror,strlen(seterror));
 return ;
}

void do_pwd(int connfd,struct ftpuser *myftp)
{
 char output[MAXLINE];
 memset(output,'�',MAXLINE);
 char currdir[MAXLINE];
 memset(currdir,'�',MAXLINE);
 getcwd(currdir,MAXLINE);
 snprintf(output,MAXLINE,"257 "%s"rn",currdir);
 write(connfd,output,strlen(output));
 return;
}

void do_noop(int connfd)
{
 write(connfd,"200 NOOP ok.rn",14);
 return;
}

void do_cwd(int connfd,char *postfix,char *rootdir)
{
 char *cwdok = "250 Directory successfully changed.rn";
 char *cwderror = "500 Fail to change directory.rn";
 char currdir[MAXLINE];
 memset(currdir,'�',MAXLINE);
 getcwd(currdir,MAXLINE);
 if( strcmp(rootdir,currdir) == 0 && strcmp(postfix,"..") == 0)
  write(connfd,cwderror,strlen(cwderror));
 else if(chdir(postfix) >= 0)
  write(connfd,cwdok,strlen(cwdok));
 else
  write(connfd,cwderror,strlen(cwderror));
 return ;
}

void do_quit(int connfd)
{
 char *quitok="221 Goodbye.rn";
 write(connfd,quitok,strlen(quitok));
 return ;
}

void do_user(int connfd,char *username,struct ftpuser* myftp)
{
 char *nameok="331 please specify the passwordrn";
 char *nameerror = "430 username errorrn";
 char buf[MAXLINE];
 if(strncmp(username,"yourname",6) == 0)
 {
  write(connfd,nameok,strlen(nameok));
  myftp->userok = 1;
 }
 else
  write(connfd,nameerror,strlen(nameerror));
 return;
}

void do_pass(int connfd,char *userpass,struct ftpuser* myftp)
{
 char *passerror = "430 password errorrn";
 char *loginok = "230 Login Successfulrn";
 char *nouser = "530 No User Specifiedrn";
 if(strncmp(userpass,"yourpass",8) == 0)
 {
  if(myftp->userok == 1)
  {
   write(connfd,loginok,strlen(loginok));
   myftp->loginok = 1;
  }
  else
   write(connfd,nouser,strlen(nouser));
 }
 else
  write(connfd,passerror,strlen(passerror));
 return;
}

void do_pasv(int connfd)
{
 char *notsupport="500 passive mode not supported.rn";
 write(connfd,notsupport,strlen(notsupport));
 return;
}

void do_list(int connfd,struct ftpuser* myftp)
{
 char *noport = "425 User Port Firstrn";
 char *opening = "150 Here comes the directory listing.rn";
 char *transferok = "226 Transfer complete.rn";
 if( myftp->dataport != 0)
 {
  write(connfd,opening,strlen(opening));
  pid_t pid;
  if((pid = fork()) == 0)
  {
   close(connfd);
   int datasockfd = getconnection(myftp->dataip,myftp->dataport); 
   dup2(datasockfd,1);
   close(datasockfd);
   execle("/bin/ls","ls","-lan","--time-style=+%b %d %H:%M",(char *)0,env_init);
  }
  else
  {
   waitpid(pid,NULL,0);
   write(connfd,transferok,strlen(transferok));  
  }
 }
 else
 {
  write(connfd,noport,strlen(noport));
 }
}

void do_port(int connfd,char *ipport,struct ftpuser* myftp)
{
 char *portok="200 PORT Command successful.rn";
 char temp[MAXLINE];
 strncpy(temp,ipport,strlen(ipport));
 memset(myftp->dataip,'�',20);
 strcat(myftp->dataip,strtok(temp,","));
 int i=0;
 while(i<3)
 {
  strcat(myftp->dataip,".");
  strcat(myftp->dataip,strtok(NULL,","));
  i++;
 }
 myftp->dataport = atoi(strtok(NULL,",")) * 256 ;
 myftp->dataport = myftp->dataport + atoi(strtok(NULL,","));
 write(connfd,portok,strlen(portok));
 return;
}

void handler(int connfd,char* filedir)
{
 int n;
 char command[MAXLINE];
 char rootdir[MAXLINE];
 memset(rootdir,'�',MAXLINE);
 getcwd(rootdir,MAXLINE);
 struct ftpuser myftp;
 init_ftpuser(&myftp);
 char *welcome="220 welcome to myFTP!rn";
 write(connfd,welcome,strlen(welcome));
 while(( n = read(connfd,command,MAXLINE))>0)
 {
  errno = 0;
  if( command[n-2] == 'r')
   command[n-2] = '�';
  if( command[n-1]== 'n')
   command[n-1] = '�';
  printf("get command: %sn",command);
  if( strncmp(command,"USER ",5) == 0)
   do_user(connfd,command + 5,&myftp);
  else if( strncmp(command,"PASS ",5) == 0)
   do_pass(connfd,command + 5,&myftp);
  else if(strncmp(command,"LIST",4) == 0) //list file
   do_list(connfd,&myftp);
  else if(strncmp(command,"CWD ",4) == 0)  //change dir
   do_cwd(connfd,command + 4,rootdir);
  else if(strncmp(command,"PWD",3) == 0)
   do_pwd(connfd,&myftp);
  else if(strncmp(command,"PORT ",5) == 0)
   do_port(connfd,command + 5,&myftp);
  else if(strncmp(command,"PASV",4) == 0)
   do_pasv(connfd);
  else if(strncmp(command,"MODE ",5) == 0)
   do_mode(connfd,command + 5);
  else if(strncmp(command,"TYPE ",5) == 0)
   do_type(connfd,command + 5);
  else if(strncmp(command,"RETR ",5) == 0)
   do_retr(connfd,command + 5,&myftp);
  else if(strncmp(command,"STOR ",5) == 0)
   do_stor(connfd,command + 5,&myftp);
  else if(strncmp(command,"CDUP",4) == 0)  //CDUP
   do_cwd(connfd,"..",rootdir);
  else if(strncmp(command,"QUIT",4) == 0)
  {
   do_quit(connfd);
   break;
  }
  else
   write(connfd,"500 unknown command.rn",22);
 }
 close(connfd);
 return;
}

int main(int argc,char *argv[])
{
 int listenfd,connfd;
 struct sockaddr_in cliaddr;
 socklen_t clilen;
 pid_t handlerpid;
 listenfd = initlisten(listenport);
 chdir(argv[1]);
 printf("server has started,listen port is %dn",listenport);
 while(1)
 {
  if((connfd = accept(listenfd,(struct sockaddr*)&cliaddr,&clilen)) == -1)
  {
   printf("accept error! %s",strerror(errno));
   return errno;
  }
  printf("get a connectionn");
  if((handlerpid = fork()) == 0)
  {
   handler(connfd,argv[1]);
   exit(0);
  }
  close(connfd);
 }
 return 0;
}

以后贴的程序可能会一直用这个东西,所以先贴上来,就三个函数,不自动重启被信号中断的系统调用的signal_intr,建立连接sockfd的getconnection(ip,port)和建立倾听sockfd的initlisten(port)。


#ifndef _UNP_H
#define _UNP_H
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/select.h>
#include <fcntl.h>
#define MAXLINE 1024
#define RWRWRW (S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP | S_IWOTH | S_IROTH)
#define RWXRWXRWX (S_IWUSR | S_IRUSR | S_IXUSR | S_IWGRP | S_IRGRP | S_IXGRP | S_IWOTH | S_IROTH | S_IXOTH)
typedef void (*sighandler_t)(int);

sighandler_t signal_intr(int signo,sighandler_t func)
{
 struct sigaction act,oact;
 act.sa_handler = func;
 sigemptyset(&act.sa_mask);
 act.sa_flags = 0;
#ifdef SA_INTERRUPT
 act.sa_flags |= SA_INTERRUPT;
#endif
 if(sigaction(signo,&act,&oact) < 0)
  return (SIG_ERR);
 return (oact.sa_handler);
}

int initlisten(int port)
{
 int listenfd;
 struct sockaddr_in servaddr;
 if( (listenfd = socket(AF_INET,SOCK_STREAM,0))< 0)
 {
  printf("socket error! %s\n",strerror(errno));
  return -1;
 }
 bzero(&servaddr,sizeof(struct sockaddr_in));
 servaddr.sin_family = AF_INET;
 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
 servaddr.sin_port = htons(port);
 if( -1 == (bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))) )
 {
  printf("bind error! %s,%d\n",strerror(errno),port);
  return -1;
 }
 if( -1 == (listen(listenfd,1024)))
 {
  printf("listen error! %s,%d\n",strerror(errno),port);
  return -1;
 }
 return listenfd;
}

int getconnection(char *ip,int port)
{
 int sockfd,reuse=1;
 struct sockaddr_in cliaddr;
 if( (sockfd = socket(AF_INET,SOCK_STREAM,0))< 0)
 {
  printf("socket error! %s\n",strerror(errno));
  return -1;
 }
 if( setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(int)) < 0)
 {
  printf("set reuseaddr error! %s\n",strerror(errno));
  return -1;
 }
 bzero(&cliaddr,sizeof(struct sockaddr_in));
 cliaddr.sin_family = AF_INET;
 cliaddr.sin_port = htons(port);
 if ( inet_pton(AF_INET,ip,&cliaddr.sin_addr) <= 0)
 {
  printf("inet_pton error for %s,%s\n",ip,strerror(errno));
  return -1;
 }

 if(connect(sockfd,(struct sockaddr*)&cliaddr,sizeof(cliaddr)) < 0)
 {
  printf("connect error for %s\n",strerror(errno));
  return -1;
 }

 return sockfd;
}

#endif

常见的linux对read,write等系统调用被信号中断会自动设置重启,通过sigaction设置不自动重启。


typedef void (*sighandler_t)(int);

sighandler_t signal_intr(int signo,sighandler_t func)
{
 struct sigaction act,oact;
 act.sa_handler = func;
 sigemptyset(&act.sa_mask);
 act.sa_flags = 0;
#ifdef SA_INTERRUPT
 act.sa_flags |= SA_INTERRUPT;
#endif
 if(sigaction(signo,&act,&oact) < 0)
  return (SIG_ERR);
 return (oact.sa_handler);
}

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

#define RWRWRW (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)

int main(void)
{
 pid_t pid;
 if((pid = fork()) == 0)
 {
  umask( S_IWGRP | S_IWOTH );
  int fd = open("testls",O_RDWR | O_CREAT,RWRWRW);
  dup2(fd,1);
  close(fd);
  execl("/bin/ls","ls",(char *)0);
 }
 else
 {
  waitpid(pid);
  return 0;
 }
}