董老师前几天给我们布置了3道作业,第三道作业是cgi程序设计。
题目:
web服务器cgi接口功能的实现
要求:
能调用cgi程序并得到返回结果;
cgi程序能接受参数并得到返回结果;
使用两种或以上语言实现。
我的想法是:
1、既然要求能够接受参数,那么必然需要使用到表单来提交用户数据,方法可以为get或者post。
2、使用linux系统下最通用的语言——c语言,最通用的脚本语言——shell脚本。
3、该cgi程序为一个linux命令,post的数据即为该命令的参数。我选择了man命令,因为使用该命令,基本上不会产生安全隐患。不仅输入的参数具有丰富的可选项,而且不同的参数返回的结果具有很强的差异性。
一、前台html的设计
代码示例
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<title>man for web</title>
<link rel="stylesheet" type="text/css" href="man.css"/> <body>
<div id="box">
<h1>man for web</h1>
<form method="get" action="/cgi-bin/man.sh" name="myform">
<input type="text" placeholder="使用shell脚本" name="cmd"></input>
<button class="but" type="submit">确定</button>
</form>
<br>
<form method="post" action="/cgi-bin/man.exe" name="myform">
<input type="text" placeholder="使用c语言" name="cmd"></input>
<button class="but" type="submit">确定</button>
</form>
</div>
</body>
</html>
man.css代码
html{
width: 100%;
height: 100%;
overflow: hidden;
}
body{
width: 100%;
height: 100%;
margin: 0;
background-color: #4A374A;
}
#box{
position: absolute;
top: 50%;
left:50%;
margin: -150px 0 0 -150px;
width: 300px;
height: 300px;
}
#box h1{
color: #fff;
letter-spacing: 1px;
text-align: center;
}
h1{
font-size: 2em;
margin: 0.67em 0;
}
input{
width: 278px;
height: 18px;
margin-bottom: 10px;
outline: none;
padding: 10px;
font-size: 13px;
color: #fff;
border-top: 1px solid #312E3D;
border-left: 1px solid #312E3D;
border-right: 1px solid #312E3D;
border-bottom: 1px solid #56536A;
border-radius: 4px;
background-color: #2D2D3F;
}
.but{
width: 300px;
min-height: 20px;
display: block;
background-color: #4a77d4;
border: 1px solid #3762bc;
color: #fff;
padding: 9px 14px;
font-size: 15px;
line-height: normal;
border-radius: 5px;
margin: 0;
}
二、cgi程序设计
1、c语言
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LINE 2000
main()
{
char poststr[14];
char cmd[10];
printf ("Content-type:text/plain \n\n");
int n=0;
int i;
if(getenv("CONTENT_LENGTH"))
{
n=atoi(getenv("CONTENT_LENGTH"));
fgets(poststr,n+1,stdin);
sscanf(poststr,"cmd=%s",cmd); char cmds[15];
sprintf(cmds,"man %s > data.txt",cmd);
system(cmds); FILE* fp;
int len;
if((fp=fopen("data.txt","r"))==NULL)
{
perror("fail to read");
}
while(fgets(buf,MAX_LINE,fp)!=NULL)
{
len=strlen(buf);
buf[len-1] = '\0';
printf("%s\n",buf);
}
} else{
printf("error");}
}
程序思路:
调用system函数,将处理过得post数据作为参数进行处理。
具体实现方法:
"Content-type:html/plain \n\n"这条语句用于输出头信息,浏览器读取到后,可获知该网页格式为plain(标准ascii字符),紧接着的两个\n\n缺一不可,否则该cgi程序不会被执行,并且访问的时候会显示500错误,查看apache2错误日志,记录为“malformed header from script. Bad header=xxx”或者“Premature end of script headers”。
数组poststr[15]用于存储环境变量CONTENT_LENGTH,cmd[10]用于存储用户真正输入的数据,长度15够用了。cmds[15]用于存储system函数调用的程序path及参数。比如欲查询find命令相关用法,按下确定后,浏览器将使用post方法发送一串字符串到服务器,该字符串经过url编码,编码后的字符串为“cmd=find”。在这个案例中,数组poststr的作用就是存储这个字符串,数组cmd的作用是存储"find",数组cmds的作用是存储“man find > in.txt”,至于怎么通过poststr来生成cmd、cmds,则需要用到相应的函数。
if(getenv("CONTENT_LENGTH")用于判断传递的字符串是否为空。
语句 fgets(poststr,n+1,stdin) 作用是从标准输入中读取n个字符串并存储到数组poststr[]。参考百度百科:“从文件结构体指针stream中读取数据,每次读取一行。读取的数据保存在buf指向的字符数组中,每次最多读取bufsize-1个字符(第bufsize个字符赋'\0')”,函数原型:char *fgets(char *buf, int bufsize, FILE *stream)。
语句 sscanf(poststr,"cmd=%s",cmd) 作用是生成cmd字符串。参考百度百科:“sscanf() - 从一个字符串中读进与指定格式相符的数据”。通俗的理解就是裁剪字符串,提取有效信息。
语句 sprintf(cmds,"man %s > data.txt",cmd) 作用是将字符串cmd和“man ”拼接,生成字符串cmds。
c语言中,system()函数用于调用系统命令。比如system("ls")调用ls命令,然而system调用成功返回值是0,因此需要将结果重定向到一个文本文件中再读取此文本文档以显示命令执行结果。system(cmds)相当于system("man xxx")。
之后的语句用于打开文本文档,不再详细解释。
2、使用shell脚本
#!/bin/bash
echo "content-type:text/html"
echo
echo "<html>"
echo "<head>"
echo "<meta charset="UTF-8">"
echo "<title>shell-cgi实现man命令</title>"
echo "<style>"
echo "pre {margin:0 auto; width:"50%"; height:"100%"; font-size:12pt;}"
echo "</style>"
echo "</head>"
echo "<body>"
echo "<pre>"
data=$QUERY_STRING
cmd=${data#*cmd=}
if man $cmd >/dev/null;then
man $cmd
else
echo "没有查询到相关信息"
echo "</pre>"
echo "</body>"
echo "</html>"
data=$QUERY_STRING用于从环境变量中获取数据并赋给data。
cmd=${data#*cmd=} 作用是从data字符串中截取“cmd=”之后的字符串。
之所以使用<pre>标签是因为位如果不使用的话生成的html页面不自动换行。
ps.不需要担心不怀好意的人输入一下诸如 "ls && rm -rf / "之类的数据到cgi,因为数据的传输都是经过了url编码的,空格会变成加号,特殊字符也会改变。shell.cgi没有进行相应的解码,输入多个命令传递给cgi程序的只是一堆无意义的乱码,如果设计成对url完全解码的cgi程序,应该格外注意安全问题。