www.pudn.com > tftp001.rar > TFTPD.C
#include#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tftpdfunc.h" #define TLMEOUT 4 struct transfer; static void tftp(struct tftphdr*tp,int size); static void snderr(int error); static void sndfile(struct transfer *pf); static void recvfile(struct transfer*pf); static int check_file(const char*,int); static struct sockaddr_in s_in={AF_INET}; static int peer; static int rexmtval=TLMEOUT; static int maxtimeout=5*TLMEOUT; #define PACKSIZE BLOCKSIZE+4 static char buf[PACKSIZE]; static char ackbut[PACKSIZE]; static struct sockaddr_in from; static size_t fromlen; #define MAXARG 4 static char dirs[MAXARG+1];/*客户可以访问的目录*/ int main(int argc,char**argv) { struct tftphdr*tp; int n=0; int on=1; argc--;argv++; if(argc= =0)dirs[0]=″/tftpboot″; /*默认可访问的目录*/ while(argc<0 && n 0){ n=i; fromlen=k; } }else{/*如fork成功,则跳出for循环*/ break; } }/*for*/ if(pif<0){/*循环10次仍无法生成子进程,进程终止*/ syslog(LOG_ERR,″fork :%m/n″); exit(1); }else if(pid!=0){ exit(0); } if(!getuid()||!geteuid()){ struct passwd *pwd=getpwnam(″nobody″); if(pwd)setuid(pwd->pw_uid); else setuid(32765);/*设置一较大的UID*/ } from.sin_family=AF_INT; alarm(0); close(0); close(1); Peer=socket(AF_INET.SOCK_DGRAM,0); if(peer<0){ syslog(LOG_ERR,″socket:%m/n″); exit(1); } if(bind(peer,(struct sockaddr*)&s_in,sizeof(s_in))<0){ syslog(LOG_ERR,″bind:%m/n″); exit(1); } if(connect(peer,(struct sockaddr*)&from,sizef(from))<0) { syslog(LOG_ERR,″connect:%m/n″); exit(1); } Tp=(struct tftphdr*)buf; Tp-th_opcode=ntohs(tp-th_opcode); if(tp-th_opcode= =RRQ||tp->th_opcode= =WRQ) { tftp(tp,n); exit(1); } struct transfer{ const char*f_mode;/*文件传输格式*/ int(*f_validate)(const char,int);/*验证函数*/ void(*f_send)(struct transfer*);/*发送函数*/ void(*f_recv)(struct transfer*);/*接收函数*/ }transfer[]={ {″netasci″, check_file, sndfile, recvfile,1}, {″octet″, check_file, sndfile, recvfile,0}, {0} /*最后一个元素值为0,以便于查接*/ }; /*处理请求数据报*/ static void tftp(struct tftphdr*tp,int size) { char*cp; int first=1,ecode; struct transfer*pf; char*filemane,*mode=NULL; filemane=cp=tp->th_stuff; again: while(cp f_mode;pf++) if(strcmp(pf->f_mode,mode)= =0) break; if(pf->f_mode= =0){ snderr(EBADOP); exit(1); } ecode=(*pf->f_validate)(filename,tp->th_opcode); if(ecode){ snderr(ecode); exit(1); }/*ecode等于0,文件允许访问*/ if(tp->th_opcode= =WRQ) (*pf->f_recv)(pf); else (*pf->f_send)(pf); exit(0); } } FILE*file; static int check_file(const char*filename,int mode) { struct stat stbuf; int fd; const char *cp; char **dirp; syslog(LOG_ERR,″tftpd:trying to get file:%s|n″,filename); if(*filename != ′/′){/*文件名不含绝对路径*/ syslog(LOG_ERR,″tftpd:serving file %s\n″,dirs[0]); chdir(dirs[0]); }else{ for(dirp=dirs;*dirp;dirp++) if(strncmp(filename,*dirp,strlen(*dirp))==0) break; if(*dirp==0&&dirp!=dirs) return(EACCESS); } /*文件名filename里包含有允许访问的路径*/ if(!strncmp(filename,″../″,3)) return EACCESS; for(cp=filename+1;*cp;cp++) if(*cp==′. ′&&strncmp(cp-1,″/../″,4)==0 ) return(EACCESS); if(stat(filename,&stbuf)<0) return(errno==ENOENT?ENOTFOUND:EACCESS); if(mode==RRQ){ if(stbuf.st_mode&(S_IREAD>>6)==0) return(EACCESS); }else{ if((stbuf.st_mode&(S_IREAD>>6))==0) return(EACCESS); } fd=open(filename,mode==RRQ?0:1); if(fd<0) return(errno+100); file=fdopen(fd,(mode==RRQ)?″r″:″w″); if(file==NULL){ return errno++100; } return(0); } int timeout; sigjmp_buf timeoutbuf; static void timer(int signum) { (void)signum; timeout += rexmtval; if(timeout>=naxtimeout) exit(1); siglongjmp(timeoutbuf,1);/*跳到断点处执行*/ } /*用于发送文件的函数*/ static void sndfile(struct transfer*pf) { struct tftphdr*dp; struct tftphdr*ap; /*ACK数据报报头*/ int block=1; int size, n; signal(SIGALRM,timer); /*设置定时器*/ dp=read_init(); /*取得一块缓冲区用于存放读出的文件块*/ ap=(struct tftphdr*)ackbuf; do{ size=my_read(file,*dp,pf->f_convert);/*读文件块到缓冲区里*/ if(size<0){ snderr(errno+100); goto abort; } dp->th_opcode=htons((u_short)DATA); dp->th_block=htons((u_short)block); timeout=-; (void)sigsetjmp(timeoutbuf,1); /*设置断点*/ send_data: if(send(peer,dp,size+4,0)!=size+4){ syslog(LOG_ERR,″tftpd:write:%m\n″); goto abort; } read_block(file,pf->f_convert); for(;;){ alarm(rexmtval); /*启动定时器*/ n=recv(peer,ackbuf,sizeof(ackbuf),0); alarm(0); /*收到数据报,关闭定时器*/ if(n<0){ syslog(LOG_ERR,″tftpd:read:%m\n″); goto abort; } ap->th_opcode=ntohs(u_short)ap->th_opcode; ap->th_block=ntohs((u_short)ap->th_bolck); if(ap->th_opcode==ERROR) goto abort; if(ap->th_opcode==ACK){ if(ap->th_block==block){ break; } /*同步*/ (void)synch(peer); if(ap->th_block==(block-1)){ goto send_data; /*返回重发*/ } } }/*for*/ block++; }while (size==BLOCKSIZE); abort; (void )fclose(file); } Static void justquit(int signum) { (void)signum; eixt(0); } /*接收文件*/ static void recvfile(struct transfer *pf) { struct tftphdr *dp; struct tftphdr ap; /*ACK缓冲区*/ int block=0; int n, size; signal(SIGALRM,timer); dp=write_init(); /*取得一块缓冲区用于存放接收到的文件块*/ ap=(struct tftphdr *)adkbuf; do{ timeout=0; ap->th_opcode=htons(u_short)ACK; ap->th_block=htons((u_short)block); block++; (void)sigsetjmp(timeoutbuf,1); send_ack; if(send(peer,ackbuf,4,0)!=4){ syslog(LOG_ERR,″tftpd:write:%m\n″); goto abort; } write_block(file,pf->f_convert); for(;;){ alarm(rexmtval); n=recv(peer,dp,PACKSIZE,0); /*接收文件块*/ alarm(0); if(n<0) syslog(LOG_ERR,″tftpd:read:%m\n″); goto abort; } dp->th_opcode=ntohs(u_shot)dp->th_opcode; dp->th_block=ntohs(u_short)dp->th_block; if(dp->th_opcode==ERROR) goto abort; if(dp->th_opcode==DATA){ if(dp->th_block==block){ break; /*normal*/ } /*同步*/ (void)synch(peer); if(dp->th_block==(block-1){ goto send_ack; /*重发*/ } size =my+write(file,&dp,n-4,pf->f_convert); if(size!=(n-4)) if(size<0) snderr(errno+100); else snderr(ENOSPACE); goto abort; } }while(size==BLOCKSIZE); write_block(file,pf->f_convert); (void)fclose(file); /*关闭文件*/ ap->th_opcode=htons(u_short)ACK); ap->th_block=htons(u_short)(block); (void)send(peer,ackbuf,4,0); /*发送最后一个文件块的ACK*/ signal(SIGALRM,justquit); /*设置定时器*/ alarm(rexmtval); n=recv(peer,buf ,sizeof(buf),0); /*超时将退出*/ alarm(0); if(n>=4 &&dp->th_block){ /*客户端重发了最后一文件块,南非要发ACK*/ (void)send(peer,ackbuf,4,0); } abort; return; } struct errmsg{ int e_code; const char*e_msg; }errmsgs[]={ {EUNDEF, ″Undefined error code″}, {ENOTFOUND, ″File not found″}, {EACCESS, ″Access violation″}, {ENOSPACE , ″Disk full or allocation exceeded″}, {EBADOP, ″Illegal TFTP operation″}, {EBADID, ″Unknown transfer ID″}, {EEXISTS, ″File already exists″}, {ENOUSER, ″No such user″}, {-1, 0}, }; /*此函数用于发送一个ERROR数据报*/ static void snderr(int error) { struct tftphdr*tp; int length; struct errmsg *pe; tp=(struct tftphdr*)buf; tp->th_opcode=htons(u_short)ERROR; tp->th_code=htons(u_short)error; for(pe=errmsgs;pe->e_code>=0;pe++) if(pe->e_code==error)/*该错误号已定义*/ break; if(pe->e_code<0){ pe->e_msg=strerror(error-100); tp->th_code=EUNDEF; /*置错误号为"UNDEF"*/ } strcpy(tp->th_msg,pe-e_msg); length=strlen(pe->e_msg); tp->th_msg[length]= ′\0′; length +=5; if(send(peer,buf,length,0)!=length) syslog(LOG_ERR,″snderr:%m\n″); }