thttpd源码分析

最近多了个看源码的嗜好

  main函数已经分析好了,找时间分离好代码,待续...

thttpd Web Server模块

thttpd Web Server#include<stdio.h>
#include<sys/socket.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<string.h>
#include<unistd.h>
#include<time.h>
#include<sys/time.h>
#include<errno.h>
#include<netdb.h>
#include<sys/socket.h>
#include<unistd.h>
#include<sys/resource.h>typedef union
{struct sockaddr sa;struct sockaddr_in sa_in;
#ifdef USE_IPV6struct 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)<(b)?(a):(b))
#endifstatic int max_connects;
static httpd_server* hs = (httpd_server*) 0;
static int num_connects;
static char* hostname;
static int port;
int terminate=0;#ifndef FDWATCHER
#define FDWATCHER
/** fdwatcher **/
//fdwatcher用于和多种IO复用方式协作完成IO复用,抽象出IO复用的使用接口
//IO复用使用宏定义加预编译define形式实现多种IO复用形式的选择
class FdWatcher
{
private:static FdWatcher *fd;
public://可以监听的最大fd数int nfiles;//计数WATCH的调用次数,辅助统计信息long nwatches;//使用定长数组,slot map,存放对应fd的rw标志和data//read的fd和write的fd在IO复用中有不同的执行方式,所以需要标志int* fd_rw;//client的数据,可以是网络数据或者辅助数据void** fd_data;//nreturned表示WATCH得到的被激活fd数目//next_ridx表示WATCH得到的被激活fd数组的下一个fd(static int* select_rfdidx;)int nreturned, next_ridx;
private:FdWatcher(){nfiles=0;nwatches=0;fd_rw=0;fd_data=0;nreturned=next_ridx=0;}
public:static FdWatcher *getFdWatcherInstance();int fdwatch_get_nfiles();void fdwatch_add_fd(int fd, void* client_data,int rw);void fdwatch_del_fd(int fd){}int fdwatch_check_fd(int fd){}int fdwatch(long timeout_msecs){}
};
FdWatcher* FdWatcher::fd=NULL;
FdWatcher* FdWatcher::getFdWatcherInstance()
{if(fd==NULL)fd=new FdWatcher();return fd;
}
#define HAVE_SELECT
#ifdef HAVE_SELECTstatic fd_set master_rfdset;static fd_set master_wfdset;static fd_set working_rfdset;static fd_set working_wfdset;static int* select_fds;static int* select_fdidx;static int* select_rfdidx;static int nselect_fds;static int maxfd;static int maxfd_changed;#define ADD_FD( fd, rw )       select_add_fd( fd, rw )static void select_add_fd( int fd, int rw ){FdWatcher *fdw=FdWatcher::getFdWatcherInstance();if ( nselect_fds >= 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
#endifint FdWatcher::fdwatch_get_nfiles()
{int i;//FdWatcher* fd=fdw;//返回一个进程能打开的最大文件数fd->nfiles=getdtablesize();//如果可以使用rlimit,则使用rlimit获取更精确的limitstruct 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_SETSIZEfd->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;i<fd->nfiles;++i)fd->fd_rw[i]=-1;}void FdWatcher::fdwatch_add_fd(int fd, void* client_data,int rw)
{if(fd<0 || fd>=nfiles || fd_rw[fd]!=-1 ){//errorreturn;}//根据预定义的处理方式选择不同的ADD_FD方法ADD_FD(fd,rw);//使用fd_rw和fd_data两个bitmap数组,对应slot上存放对应fd的rw标志和cdfd_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 ){//errorexit(1);}if( he->h_addrtype!=AF_INET){//errorexit(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和serverhostnamehs->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))<0)ghnbuf[0]='\0';#ifdef SERVER_NAME_LISTif ( ghnbuf[0]!='\0')hs->server_hostname=hostname_map(ghnbuf);#endifif ( hs->server_hostname==(char*)0){#ifdef SERVER_NAMEhs->server_hostname=SERVER_NAME;#elseif(ghnbuf[0]!='\0')hs->server_hostname=ghnbuf;#endif}}hs->port=port;//initialize_listen_socket,就是socket bind listenif( sa6p == (httpd_sockaddr*)0)hs->listen6_fd=-1;elsehs->listen6_fd=initialize_listen_socket(sa6p);if( sa4p == (httpd_sockaddr*)0)hs->listen4_fd=-1;elsehs->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_inhttpd_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,listenhs=httpd_initialize(hostname,gotv4?&sa4:(httpd_sockaddr*)0,gotv6?&sa6:(httpd_sockaddr*)0,port);      if(hs != (httpd_server*)0){if (hs->listen4_fd!=-1)//注册监听fdfdw->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_ready<0){if( errno==EINTR || errno==EAGAIN)continue;exit(1);}(void) gettimeofday( &tv, (struct timezone*) 0 );if(num_ready==0){//     tmr_run(&tv);continue;}//num_ready>0//accept并加入listen setif ( 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安全模块

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<pwd.h>
 15 #include<sys/types.h>
 16 #include<unistd.h>
 17 #include<stdlib.h>
 18 #include<sys/file.h>
 19 #include<string.h>
 20 #include<sys/types.h>
 21 #include<grp.h>
 22 #include<stdio.h>
 23 #include<errno.h>
 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)<0 )
 63         {
 64         //error
 65         printf("error chdir\n");
 66         exit(1);
 67         }
 68     }
 69     #endif
 70
 71     //当前工作目录
 72     char cwd[MAXPATHLEN+1];
 73     printCwd(cwd);
 74
 75     //系统的目录结构将以指定的位置作为'/'的位置
 76 //作用:
 77 //增加了系统安全性,新根下访问不到旧根目录和文件
 78 //建立一个与原系统隔离的系统目录结构,方便用户开发
 79 //引导linux系统启动以及急救系统等
 80 //详细见IBM文章
 81     if(chroot(cwd)<0)
 82     //将当前程序的工作目录作为新的文件夹root,成功后getcwd变为"/"
 83     {
 84         printf("error chroot\n");
 85         exit(1);
 86     }
 87
 88     //当前的工作目录变为"/"
 89     printCwd(cwd);
 90
 91     //在新的"/"目录下跳转到a文件夹
 92     if ( chdir( "a" ) < 0 )
 93     {
 94         //error
 95         printf("error chdir2\n,%s",strerror(errno));
 96         exit( 1 );
 97     }
 98
 99     //路径 "/a/"
100     printCwd(cwd);
101
102     //root完成chroot调用后,转换身份
103     if ( getuid() == 0 )
104     {
105 /*
106     // Set aux groups to null.
107     if ( setgroups( 0, (const gid_t*) 0 ) < 0 )
108         {
109         printf("error setgroups\n");
110         exit( 1 );
111         }
112 */
113
114     if ( setgid( gid ) < 0 )
115         {
116         printf("error setgid\n");
117         exit( 1 );
118         }
119
120 #ifdef HAVE_SETLOGIN
121         (void) setlogin( user );
122 #endif
123
124     if ( setuid( uid ) < 0 )
125         {
126         printf("error syslog\n");
127         exit( 1 );
128         }
129     }
130 }

其他模块

thttpd源码分析相关推荐

  1. thttpd源码解析 定时器模块

    thttpd源码解析 定时器模块 thttpd是非常轻量级的http服务器,可执行文件仅50kB.名称中的第一个t表示tiny, turbo, 或throttling 与lighttpd.memcac ...

  2. 【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析

    目录[阅读时间:约10分钟] 一.概述 二.对比: gorilla/mux与net/http DefaultServeMux 三.简单使用 四.源码简析 1.NewRouter函数 2.HandleF ...

  3. SpringBoot-web开发(四): SpringMVC的拓展、接管(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) SpringBoot-web开发(二): 页面和图标定制(源码分析) SpringBo ...

  4. SpringBoot-web开发(二): 页面和图标定制(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) 目录 一.首页 1. 源码分析 2. 访问首页测试 二.动态页面 1. 动态资源目录t ...

  5. SpringBoot-web开发(一): 静态资源的导入(源码分析)

    目录 方式一:通过WebJars 1. 什么是webjars? 2. webjars的使用 3. webjars结构 4. 解析源码 5. 测试访问 方式二:放入静态资源目录 1. 源码分析 2. 测 ...

  6. Yolov3Yolov4网络结构与源码分析

    Yolov3&Yolov4网络结构与源码分析 从2018年Yolov3年提出的两年后,在原作者声名放弃更新Yolo算法后,俄罗斯的Alexey大神扛起了Yolov4的大旗. 文章目录 论文汇总 ...

  7. ViewGroup的Touch事件分发(源码分析)

    Android中Touch事件的分发又分为View和ViewGroup的事件分发,View的touch事件分发相对比较简单,可参考 View的Touch事件分发(一.初步了解) View的Touch事 ...

  8. View的Touch事件分发(二.源码分析)

    Android中Touch事件的分发又分为View和ViewGroup的事件分发,先来看简单的View的touch事件分发. 主要分析View的dispatchTouchEvent()方法和onTou ...

  9. MyBatis原理分析之四:一次SQL查询的源码分析

    上回我们讲到Mybatis加载相关的配置文件进行初始化,这回我们讲一下一次SQL查询怎么进行的. 准备工作 Mybatis完成一次SQL查询需要使用的代码如下: Java代码   String res ...

最新文章

  1. MapReduce编程实战之“高级特性”
  2. MySQL重要概念图解(重要)
  3. 如何使用Redis做MySQL的缓存
  4. 第七节:Trigger(SimpleTrigger、CronTrigger)哑火(MisFire)策略 :
  5. xml分析错误:注释未终止_错误:C中的未终止注释(无效的注释块) 常见的C程序错误...
  6. 最短路+状压DP【洛谷P3489】 [POI2009]WIE-Hexer
  7. 文本处理 - 测试一个对象是否是类字符串
  8. iOS-QQ自动聊天机器人
  9. python控制窗口_python小笔记-控制窗口的现实和隐藏
  10. 【Java8新特性】Stream流
  11. 关于半岛体试验的一些知识汇总
  12. 前向纠错FEC纠错编码原理
  13. solidworks显示无法连接到服务器,SOLIDWORKS Electrical—无法连接协同服务器
  14. PCL学习:基于形态学滤波的地面分割
  15. MYSQL高可用架构MMM实现
  16. 【过关斩将7】面试谈薪资时,HR压价怎么办?
  17. 按图索骥|到底网络空间安全、网络安全、信息安全之间有啥区别?
  18. Maven的安装步骤(保姆级安装教程)
  19. 如何选择股市短线黑马
  20. Halcon学习笔记(九)——OCR实战练习 倾斜日期检测、倒着的字符检测

热门文章

  1. java private 接口_java接口中 定义 private 私有方法
  2. K - 老鼠走迷宫(DFS)
  3. 【自动驾驶】12.百度Apollo对ROS的优化【详细干货】
  4. 【Linux】15.mdc启动网卡并设置其ip和子网掩码
  5. error C2872: “IDocument”: 不明确的符号
  6. 让你彻底理解Synchronized
  7. wireshark抓包图解 TCP三次握手/四次挥手详解
  8. Spring学习总结——Spring实现AOP的多种方式
  9. JSP/SERVLET入门教程--Servlet 使用入门
  10. 【LSH源码分析】p稳定分布LSH算法