main函数已经分析好了,找时间分离好代码,待续...
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_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)<(b)?(a):(b))
#endif
static 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_SELECT
static 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
#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;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 )
{
//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))<0)
ghnbuf[0]='\0';
#ifdef SERVER_NAME_LIST
if ( ghnbuf[0]!='\0')
hs->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_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 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<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 }