thttpd源码分析
chroot
最近多了个看源码的嗜好
main函数已经分析好了,找时间分离好代码,待续...
thttpd Web Server模块

thttpd Web Server#include #include #include #include #include #include #include #include #include #include #include #include #include typedef union { struct sockaddr sa; struct sockaddr_in sa_in; #ifdef USE_IPV6 struct sockaddr_in6 sa_in6; struct sockaddr_storage sa_stor; #endif }httpd_sockaddr; typedef struct { char* server_hostname; char* binding_hostname; unsigned short port; int listen4_fd; int listen6_fd; }httpd_server; #define SPARE_FDS 10 #define FDW_READ 0 #define FDW_WRITE 1 #ifndef MIN #define MIN(a,b) ((a)= fdw->nfiles ) { //syslog( LOG_ERR, "too many fds in select_add_fd!" ); return; } select_fds[nselect_fds] = fd; switch ( rw ) { case FDW_READ: FD_SET( fd, &master_rfdset ); break; case FDW_WRITE: FD_SET( fd, &master_wfdset ); break; default: break; } if ( fd > maxfd ) maxfd = fd; select_fdidx[fd] = nselect_fds; ++nselect_fds; } #else #ifdef HAVE_POLL #endif #endif int FdWatcher::fdwatch_get_nfiles() { int i; //FdWatcher* fd=fdw; //返回一个进程能打开的最大文件数 fd->nfiles=getdtablesize(); //如果可以使用rlimit,则使用rlimit获取更精确的limit struct rlimit rl; if( getrlimit(RLIMIT_NOFILE,&rl)==0 ) { if( rl.rlim_max== RLIM_INFINITY) rl.rlim_cur=8192; else if (rl.rlim_max>rl.rlim_cur) rl.rlim_cur=rl.rlim_max; //尝试设置limit为最大,如果不行就放弃 setrlimit(RLIMIT_NOFILE,&rl); fd->nfiles=rl.rlim_cur; } #if defined(HAVE_SELECT) && !( defined(HAVE_POLL) || defined(HAVE_DEVPOLL) || defined(HAVE_KQUEUE) ) //如果使用select,则nfile不能超过FD_SETSIZE fd->nfiles= MIN(fd->nfiles,FD_SETSIZE); #endif //然后初始化和fdwatch相关的数据成员 fd->nwatches=0; fd->fd_rw=(int*)malloc(sizeof(int)*fd->nfiles); fd->fd_data=(void**)malloc(sizeof(void*)*fd->nfiles); if(fd->fd_rw==(int*)0 || fd->fd_data==(void**)0 ) return -1; for( i=0;infiles;++i) fd->fd_rw[i]=-1; } void FdWatcher::fdwatch_add_fd(int fd, void* client_data,int rw) { if(fd=nfiles || fd_rw[fd]!=-1 ) { //error return; } //根据预定义的处理方式选择不同的ADD_FD方法 ADD_FD(fd,rw); //使用fd_rw和fd_data两个bitmap数组,对应slot上存放对应fd的rw标志和cd fd_rw[fd]=rw; fd_data[fd]=client_data; } /** fdwatcher **/ #endif /** tool function **/ static void lookup_hostname( httpd_sockaddr* sa4P, size_t sa4_len, int* gotv4P, httpd_sockaddr* sa6P, size_t sa6_len, int* gotv6P ) { #ifdef USE_IPV6 //... #else //IPV4 //hostent结构体,gethostbyname返回值类型,数据成员包括(主机规范名,主机别名,主机ip地址类型,ip地址长度,网络字节序的主机ip地址) struct hostent *he; //表示不使用v6版本 *gotv6P=0; (void)memset(sa4P,0,sa4_len); sa4P->sa.sa_family=AF_INET; //没有指定监听hostname,则绑定到所有接口 if( hostname==(char*)0 ) sa4P->sa_in.sin_addr.s_addr=htonl(INADDR_ANY); else { //如果指定了hostname,那要通过gethostbyname获取ip等信息。 sa4P->sa_in.sin_addr.s_addr=inet_addr(hostname); if( (int)sa4P->sa_in.sin_addr.s_addr==-1) { he=gethostbyname(hostname); if(he == (struct hostent*)0 ) { //error exit(1); } if( he->h_addrtype!=AF_INET) { //error exit(1); } (void) memmove(&sa4P->sa_in.sin_addr.s_addr , he->h_addr, he->h_length); } } sa4P->sa_in.sin_port=htons(port); *gotv4P=1; #endif } long tmr_mstimeout(struct timeval* nowP) { return 0; } /** tool function **/ /** httpd_initialize **/ #ifdef SERVER_NAME_LIST static char* hostname_map( char* hostname ) { int len, n; static char* list[] = { SERVER_NAME_LIST }; len = strlen( hostname ); for ( n = sizeof(list) / sizeof(*list) - 1; n >= 0; --n ) if ( strncasecmp( hostname, list[n], len ) == 0 ) if ( list[n][len] == '/' ) /* check in case of a substring match */ return &list[n][len + 1]; return (char*) 0; } #endif /* SERVER_NAME_LIST */ static int initialize_listen_socket(httpd_sockaddr* sap) {} static void free_httpd_server(httpd_server* hs) {} httpd_server* httpd_initialize(char *hostname,httpd_sockaddr* sa4p, httpd_sockaddr *sa6p,unsigned short port) { httpd_server *hs=(httpd_server*)malloc(sizeof(httpd_server)); static char ghnbuf[256]; if(hs==(httpd_server*)0) { printf("out of memory for allocating httpd_server \n"); return (httpd_server*)0; } //在此之前有调用gethostbyname等操作 if(hostname!=(char*)0) { //设置hs的bindinghostname和serverhostname hs->binding_hostname=strdup(hostname); if( hs->binding_hostname==(char*)0 ) { printf("out of memory copying hostname\n"); return (httpd_server*)0; } hs->server_hostname=hs->binding_hostname; } else { hs->binding_hostname=(char*)0; hs->server_hostname=(char*)0; //得到主机名 if(gethostname(ghnbuf,sizeof(ghnbuf))server_hostname=hostname_map(ghnbuf); #endif if ( hs->server_hostname==(char*)0) { #ifdef SERVER_NAME hs->server_hostname=SERVER_NAME; #else if(ghnbuf[0]!='\0') hs->server_hostname=ghnbuf; #endif } } hs->port=port; //initialize_listen_socket,就是socket bind listen if( sa6p == (httpd_sockaddr*)0) hs->listen6_fd=-1; else hs->listen6_fd=initialize_listen_socket(sa6p); if( sa4p == (httpd_sockaddr*)0) hs->listen4_fd=-1; else hs->listen4_fd=initialize_listen_socket(sa4p); if(hs->listen4_fd==-1 && hs->listen6_fd==-1) { free_httpd_server(hs); return (httpd_server*)0; } } /** httpd_initialize **/ int main() { hostname=0; port=51423; struct timeval tv; //初始化sockaddr_in httpd_sockaddr sa4; httpd_sockaddr sa6; int gotv4, gotv6; lookup_hostname(&sa4,sizeof(sa4),&gotv4,&sa6,sizeof(sa6),&gotv6); //计算系统允许的fd数,初始化fdwatch,io复用模式相关的 FdWatcher *fdw=FdWatcher::getFdWatcherInstance(); max_connects=fdw->fdwatch_get_nfiles(); if(max_connects < 0) { printf("fdwatch init failure\n"); exit(1); } max_connects -= SPARE_FDS; //真正做network相关的。做hs初始化和socket,bind,listen hs=httpd_initialize(hostname,gotv4?&sa4:(httpd_sockaddr*)0,gotv6?&sa6:(httpd_sockaddr*)0,port); if(hs != (httpd_server*)0) { if (hs->listen4_fd!=-1) //注册监听fd fdw->fdwatch_add_fd(hs->listen4_fd,(void*)0,FDW_READ); if (hs->listen6_fd!=-1) fdw->fdwatch_add_fd(hs->listen6_fd,(void*)0,FDW_READ); } int num_ready; (void) gettimeofday( &tv, (struct timezone*) 0 ); while( (!terminate)||num_connects>0 ) { num_ready=fdw->fdwatch(tmr_mstimeout(&tv)); if(num_ready0 //accept并加入listen set if ( hs != (httpd_server*) 0 && hs->listen6_fd != -1 && fdw->fdwatch_check_fd( hs->listen6_fd ) ) { // if ( handle_newconnect( &tv, hs->listen6_fd ) ) continue; } if ( hs != (httpd_server*) 0 && hs->listen4_fd != -1 && fdw->fdwatch_check_fd( hs->listen4_fd ) ) { // if ( handle_newconnect( &tv, hs->listen4_fd ) ) continue; } /* while */ //tmr_run(&tv); /* if( !terminate) { terminate=1; if (hs!=(httpd_server*)0) { if (hs->listen4_fd!=-1) fdw->fdwatch_del_fd(hs->listen4_fd); if (hs->listen6_fd!=-1) fdw->fdwatch_del_fd(hs->listen6_fd); // httpd_unlisten(hs); } } */ }//end while //shut_down(); exit(0); }
Chroot安全模块


1 /* 2 chroot是一个系统调用,将程序的可见文件视图限制到当前目录及其下面的其他目录。这样其他远程user就无法访问初始目录外的文件。 3 子进程也会有这样的属性,所以CGI文件也会有这样的效果。 4 但是只有root才可以调用chroot,所以这意味程序只能由root启动,但是thttpd中最后root可以转换为其他user,也保证了这点。 5 1.限制被CHROOT的使用者所能执行的程序 6 2.防止使用者存取某些特定档案,如/etc/passwd。 7 3.防止入侵者/bin/rm -rf /。 8 4.提供Guest服务以及处罚恶意的使用者。 9 5.增进系统的安全。 10 文章:http://hi.baidu.com/alsrt/blog/item/de74f8389c36a32796ddd8be.html 11 http://hi.baidu.com/alsrt/blog/item/04ccce43711280159213c6bd.html 12 http://blog.csdn.net/hfw_1987/article/details/5362078 13 */ 14 #include 15 #include 16 #include 17 #include 18 #include 19 #include 20 #include 21 #include 22 #include 23 #include 24 25 #define DEFAULT_USER "nobody" 26 #ifndef MAXPATHLEN 27 #define MAXPATHLEN 1024 28 #endif 29 30 void printCwd(char* cwd) 31 { 32 memset(cwd,0,sizeof(cwd)); 33 (void) getcwd(cwd,sizeof(cwd)-1); 34 if(cwd[strlen(cwd)-1]!='/') 35 (void) strcat(cwd,"/"); 36 printf("%s\n",cwd); 37 } 38 int main() 39 { 40 char* user=(char *)"aga"; //DEFAULT_USER 41 struct passwd* pwd; 42 uid_t uid=32767; 43 gid_t gid=32767; 44 45 //如果是root权限,获取将要托管的user的uid和pid 46 if( getuid()==0) 47 { 48 pwd=getpwnam(user); 49 if(pwd==(struct passwd*)0) 50 { 51 //error 52 printf("error pwd\n"); 53 exit(1); 54 } 55 uid=pwd->pw_uid; 56 gid=pwd->pw_gid; 57 } 58 59 #ifdef USE_USER_DIR 60 if( getuid()==0) 61 { 62 if( chdir(pwd->pw_dir)关注打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?
立即登录/注册


微信扫码登录