int websDefaultHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg,
char_t *url, char_t *path, char_t *query)
{
websStatType sbuf;
char_t *lpath, *tmp, *date;
int bytes, flags, nchars;
flags = websGetRequestFlags(wp);
// 此处处理传入路径 websValidateUrl,对其中的../和其他一些情况作了处理
if (websValidateUrl(wp, path) < 0)
{
/*
* preventing a cross-site scripting exploit -- you may restore the
* following line of code to revert to the original behavior...
*/
/*websError(wp, 500, T("Invalid URL %s"), url);*/
websError(wp, 500, T("Invalid URL"));
return 1;
}
lpath = websGetRequestLpath(wp);
nchars = gstrlen(lpath) - 1;
if (lpath[nchars] == '/' || lpath[nchars] == '\\') {
lpath[nchars] = '\0';
}
//。。。。
}
int websValidateUrl(webs_t wp, char_t *path)
{
/*
Thanks to Dhanwa T (dhanwa@polyserve.com) for this fix -- previously,
if an URL was requested having more than (the hardcoded) 64 parts,
the webServer would experience a hard crash as it attempted to
write past the end of the array 'parts'.
*/
#define kMaxUrlParts 64 // 此处是防止溢出,当./././././层级太多时,这个用来矫正URL的函数反而会出现溢出,为什么和层级有关请看下面。https://www.exploit-db.com/exploits/21707
char_t *parts[kMaxUrlParts]; /* Array of ptr's to URL parts *///用来存储路径的每一层的字符串
char_t *token, *dir, *lpath;
int i, len, npart;
a_assert(websValid(wp));
a_assert(path);
dir = websGetRequestDir(wp);
if (dir == NULL || *dir == '\0') {
return -1;
}
/*
* Copy the string so we don't destroy the original
*/
path = bstrdup(B_L, path);
websDecodeUrl(path, path, gstrlen(path));
//初始化 层数以及第一层的指针
len = npart = 0;
parts[0] = NULL;
/*
* 22 Jul 02 -- there were reports that a directory traversal exploit was
* possible in the WebServer running under Windows if directory paths
* outside the server's specified root web were given by URL-encoding the
* backslash character, like:
*
* GoAhead is vulnerable to a directory traversal bug. A request such as
*
* GoAhead-server/../../../../../../../ results in an error message
* 'Cannot open URL'.
* However, by encoding the '/' character, it is possible to break out of
* the
* web root and read arbitrary files from the server.
* Hence a request like:
*
* GoAhead-server/..%5C..%5C..%5C..%5C..%5C..%5C/winnt/win.ini returns the
* contents of the win.ini file.
* (Note that the description uses forward slashes (0x2F), but the example
* uses backslashes (0x5C). In my tests, forward slashes are correctly
* trapped, but backslashes are not. The code below substitutes forward
* slashes for backslashes before attempting to validate that there are no
* unauthorized paths being accessed.
*/
token = gstrchr(path, '\\');
while (token != NULL)
{
*token = '/';
token = gstrchr(token, '\\');//把\替换成/,在windows平台会出现这个问题,参考链接https://www.exploit-db.com/exploits/20607
}
token = gstrtok(path, T("/"));
/*
* Look at each directory segment and process "." and ".." segments
* Don't allow the browser to pop outside the root web.
*/
// 下面这个循环是处理的核心,函数把url按/解析成一层一层的,存储每一层名称的是char_t *parts[kMaxUrlParts];
//如果是../就把当前层数index往上移动,路径中永远不会包含../,也就解决了路径穿越的问题
while (token != NULL)
{
if (npart >= kMaxUrlParts)// 判断是否达到了最大层,防止溢出
{
/*
* malformed URL -- too many parts for us to process.
*/
bfree(B_L, path);
return -1;
}
if (gstrcmp(token, T("..")) == 0) // 判断该层是不是..,如果是就把定位层数的npart--
{
if (npart > 0)
{
npart--;
}
}
else if (gstrcmp(token, T(".")) != 0) //如果不是.,就层数+1,将该层存储
{
parts[npart] = token;
len += gstrlen(token) + 1;
npart++;//
}
token = gstrtok(NULL, T("/"));
}
#ifdef WIN32
if (isBadWindowsPath(parts, npart))
{
bfree(B_L, path);
return -1;
}
#endif
/*
* Create local path for document. Need extra space all "/" and null.
*/
if (npart || (gstrcmp(path, T("/")) == 0) || (path[0] == '\0'))
{
lpath = balloc(B_L, (gstrlen(dir) + 1 + len + 1) * sizeof(char_t));
gstrcpy(lpath, dir);
for (i = 0; i < npart; i++)
{
gstrcat(lpath, T("/"));
gstrcat(lpath, parts[i]);
}
websSetRequestLpath(wp, lpath);
bfree(B_L, path);
bfree(B_L, lpath);
}
else
{
bfree(B_L, path);
return -1;
}
return 0;
}
int main(int argc, char** argv)
{
/*
* Initialize the memory allocator. Allow use of malloc and start
* with a 60K heap. For each page request approx 8KB is allocated.
* 60KB allows for several concurrent page requests. If more space
* is required, malloc will be used for the overflow.
*/
bopen(NULL, (60 * 1024), B_USE_MALLOC);
signal(SIGPIPE, SIG_IGN);
/*
* Initialize the web server
*/
if (initWebs() < 0) {// 这个是我们要关注的
return -1;
}
#ifdef WEBS_SSL_SUPPORT
websSSLOpen();
#endif
/*
* Basic event loop. SocketReady returns true when a socket is ready for
* service. SocketSelect will block until an event occurs. SocketProcess
* will actually do the servicing.
*/
while (!finished) {
if (socketReady(-1) || socketSelect(-1, 1000)) {
socketProcess(-1);
}
websCgiCleanup();
emfSchedProcess();
}
#ifdef WEBS_SSL_SUPPORT
websSSLClose();
#endif
#ifdef USER_MANAGEMENT_SUPPORT
umClose();
#endif
/*
* Close the socket module, report memory leaks and close the memory allocator
*/
websCloseServer();
socketClose();
#ifdef B_STATS
memLeaks();
#endif
bclose();
return 0;
}
static int initWebs()
{
struct hostent *hp;
struct in_addr intaddr;
char host[128], dir[128], webdir[128];
char *cp;
char_t wbuf[128];
/*
* Initialize the socket subsystem
*/
socketOpen();
#ifdef USER_MANAGEMENT_SUPPORT
/*
* Initialize the User Management database
*/
umOpen();
umRestore(T("umconfig.txt"));
#endif
/*
* Define the local Ip address, host name, default home page and the
* root web directory.
*/
if (gethostname(host, sizeof(host)) < 0) {
error(E_L, E_LOG, T("Can't get hostname"));
return -1;
}
if ((hp = gethostbyname(host)) == NULL) {
error(E_L, E_LOG, T("Can't get host address"));
return -1;
}
memcpy((char *) &intaddr, (char *) hp->h_addr_list[0],
(size_t) hp->h_length);
/*
* Set ../web as the root web. Modify this to suit your needs
*/
getcwd(dir, sizeof(dir));
if ((cp = strrchr(dir, '/'))) {
*cp = '\0';
}
sprintf(webdir, "%s/%s", dir, rootWeb);
/*
* Configure the web server options before opening the web server
*/
websSetDefaultDir(webdir);
cp = inet_ntoa(intaddr);
ascToUni(wbuf, cp, min(strlen(cp) + 1, sizeof(wbuf)));
websSetIpaddr(wbuf);
ascToUni(wbuf, host, min(strlen(host) + 1, sizeof(wbuf)));
websSetHost(wbuf);
/*
* Configure the web server options before opening the web server
*/
websSetDefaultPage(T("default.asp"));
websSetPassword(password);
/*
* Open the web server on the given port. If that port is taken, try
* the next sequential port for up to "retries" attempts.
*/
websOpenServer(port, retries);
/*
* First create the URL handlers. Note: handlers are called in sorted order
* with the longest path handler examined first. Here we define the security
* handler, forms handler and the default web page handler.
*/
websUrlHandlerDefine(T(""), NULL, 0, websSecurityHandler,
WEBS_HANDLER_FIRST);
websUrlHandlerDefine(T("/goform"), NULL, 0, websFormHandler, 0);//自定义的路径,可执行特定功能
websUrlHandlerDefine(T("/cgi-bin"), NULL, 0, websCgiHandler, 0);//执行cgi程序或脚本
websUrlHandlerDefine(T(""), NULL, 0, websDefaultHandler,
WEBS_HANDLER_LAST); //普通地请求web文件
/*
* Now define two test procedures. Replace these with your application
* relevant ASP script procedures and form functions.
*/
websAspDefine(T("aspTest"), aspTest);
websFormDefine(T("formTest"), formTest);
/*
* Create the Form handlers for the User Management pages
*/
#ifdef USER_MANAGEMENT_SUPPORT
formDefineUserMgmt();
#endif
/*
* Create a handler for the default home page
*/
websUrlHandlerDefine(T("/"), NULL, 0, websHomePageHandler, 0);
return 0;
}
/* 函数websCgiHandler中最后运行的
* Now launch the process. If not successful, do the cleanup of resources.
* If successful, the cleanup will be done after the process completes.
*/
if ((pHandle = websLaunchCgiProc(cgiPath, argp, envp, stdIn, stdOut))
== -1) {
websError(wp, 200, T("failed to spawn CGI task"));
for (ep = envp; *ep != NULL; ep++) {
bfreeSafe(B_L, *ep);
}
bfreeSafe(B_L, cgiPath);
bfreeSafe(B_L, argp);
bfreeSafe(B_L, envp);
bfreeSafe(B_L, stdOut);
} else {
/*
int websLaunchCgiProc(char_t *cgiPath, char_t **argp, char_t **envp,
char_t *stdIn, char_t *stdOut)
{
PROCESS_INFORMATION procinfo; /* Information about created proc */
DWORD dwCreateFlags;
char *fulldir;
BOOL bReturn;
int i, nLen;
/*
* Replace directory delimiters with Windows-friendly delimiters
*/
nLen = gstrlen(cgiPath);
for (i = 0; i < nLen; i++) {
if (cgiPath[i] == '/') {
cgiPath[i] = '\\';
}
}
fulldir = NULL;
dwCreateFlags = CREATE_NEW_CONSOLE;
/*
* CreateProcess returns errors sometimes, even when the process was
* started correctly. The cause is not evident. For now: we detect
* an error by checking the value of procinfo.hProcess after the call.
*/
procinfo.hThread = NULL;
bReturn = CreateProcess(//看到这里就明白了
cgiPath, /* Name of executable module */
NULL, /* Command line string */
NULL, /* Process security attributes */
NULL, /* Thread security attributes */
0, /* Handle inheritance flag */
dwCreateFlags, /* Creation flags */
NULL, /* New environment block */
NULL, /* Current directory name */
NULL, /* STARTUPINFO */
&procinfo); /* PROCESS_INFORMATION */
if (bReturn == 0) {
DWORD dw;
dw = GetLastError();
return -1;
} else {
CloseHandle(procinfo.hThread);
}
return (int) procinfo.dwProcessId;
}