光猫的嵌入式Linux系统(中兴zxic):Web服务框架深度解析
摘要:本文介绍了中兴光猫嵌入式Linux系统的Web服务实现细节。该光猫使用定制版的BusyBox httpd作为Web服务器,网站根目录位于/home/httpd。通过分析二进制文件发现,该httpd深度集成了对.gch动态脚本的处理逻辑,并具备多项系统管理功能。启动时通过setcap命令为httpd分配了多种内核权限,使其能够执行网络控制、系统管理等操作。此外,文章还推测了部分内部函数的功能,
文章目录
系统信息概况
光猫的嵌入式Linux系统(中兴zxic)的概况参见: 光猫的嵌入式Linux系统(中兴zxic)操作手记,不再赘述。
使用系统的前提是需要通过Telnet登录光猫,具体方法参考:光猫超级管理员账号密码和Telnet登陆。
Web服务概况
光猫内部的web服务使用BusyBox版本的httpd服务实现,其网站根目录位于:/home/httpd。
BusyBox官网:https://busybox.net
BusyBox命令帮助:https://busybox.net/downloads/BusyBox.html
深度定制的 httpd
光猫内部的 httpd 是厂商定制过的版本,具体信息如下:
file ./sbin/httpd
./sbin/httpd: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.3, for GNU/Linux 4.19.0, BuildID[sha1]=cd56b7d01703ac97d2e2b7f6b8a114de636a6095, stripped
其对 *.gch 动态脚本的处理逻辑已深度集成到服务内部。
strings ./sbin/httpd | grep -i gch
si_webd_f_log_gch
CmE8WANCDCfgChkSetTr069 return fail!iRet=%d
frRequestTimeout.gch
hidden_factory_switch.gch
hidden_telnet_switch.gch
itms.gch
/register.gch
/getpage.gch?pid=1000
template.gch
biaoqian.gch
loginSingle.gch
return2factory.gch
url_write_sn.gch
Get_WANCStatus.gch
getpage.gch
telnet.gch
top.gch
setlang.gch
template_user.gch
hidden_version_switch.gch
public/index.gch
frGo2SystemBusy.gch
pon_manager_service_ctl_t.gch
serial_config_t.gch
pon_net_ponpwd_t.gch
pon_net_ponloid_t.gch
net_ethwan_conf_t.gch
pon_monitor_config_t.gch
anhui_monitor_config_t.gch
app_dmz_conf_t.gch
net_dhcp_specialdevice_t.gch
net_v6_ra_server_t.gch
net_v6_dhcp_dynamic_t.gch
ipv6_net_prefix_t.gch
pon_net_wanuser_conf_t.gch
net_user_wan_conf_t.gch
e8_net_qos_basic_new_t.gch
net_qos_congestion_statisticsYN_t.gch
e8_net_qos_basic_t.gch
net_qos_qostemplate_t.gch
e8_net_qos_localapp_t.gch
net_qos_congestion_t.gch
net_qos_congestion_statistics_t.gch
usbrestore_t.gch
net_dhcp_dynamic_t.gch
app_virtual_conf_t.gch
pon_net_cltlmt_t.gch
app_voip_sipdigitmap_t.gch
manager_buss_mgr_t.gch
pon_net_backloop_conf_t.gch
pon_silence_t.gch
epon_status_link_info_t.gch
gpon_status_link_info_t.gch
epon_status_link_info_t_user.gch
gpon_status_link_info_t_user.gch
pon_status_stat_info_t.gch
pon_status_gemport_info_t.gch
epon_status_lan_info_t.gch
gpon_status_lan_info_t.gch
status_ethlan_info_t.gch
status_lan_info_t_user.gch
gpon_status_link_t.gch
status_usb_info_t.gch
app_samba_cfg_t.gch
usbbackup_t.gch
status_voip_4less_t.gch
status_voip_4less1_t.gch
status_voip_phoneNumber_t.gch
app_voip_sippro_t.gch
app_sipline_t.gch
app_voip_BasicControl_t.gch
app_voip_service_t.gch
app_voip_sipqos_t.gch
pon_voip_siprenew_t.gch
app_voip_h248basic_t.gch
app_voip_h248auth_t.gch
app_voip_h248endpoint_t.gch
voip_h248qos_t.gch
pon_voip_renew_t.gch
app_voip_h248_servs_t.gch
app_voip_sipmed_t.gch
app_voip_sipadv_t.gch
app_voip_cid_t.gch
app_voip_sipslc_t.gch
manager_voip_switch_t.gch
diagnose_voice_t.gch
status_wlaninfo_t.gch
e8_status_wlan_info_t.gch
net_11n_conf_t.gch
net_wlan_conf_t_user.gch
net_wlan_essid_t.gch
net_wlan_secrity_t.gch
easymesh_info_t.gch
tele_sec_tserver_t.gch
manager_SN_register_t.gch
commontest_t.gch
help_t.gch
net_v6_static_prefix_t.gch
net_v6_prefix_delegation_t.gch
net_v6_prefix_ban_port_t.gch
net_route6_default_t.gch
net_route6_static_t.gch
app_route6_table_t.gch
app_route6_policy_t.gch
app_mld_conf_t.gch
app_mld_snoop_t.gch
status_dslite_if_t.gch
status_dsliteeth_if_t.gch
net_dslite_conf_t.gch
net_dsliteeth_conf_t.gch
net_6in4_conf_t.gch
net_6in4_eth_t.gch
status_6in4_info_t.gch
status_6in4_eth_t.gch
app_voip_homeline_t.gch
app_voip_dialplan_t.gch
tr069_registering.gch
tr069_servering.gch
tr069_updating.gch
http://192.168.1.1/upgrade.gch
(szFakePage or szGCHPage)
si_webd_f_log_gch
meta_menu['%s']['URL'] = 'getpage.gch?pid=1002&nextpage=%s';
menu_items['%s']['%s']['URL'] = 'getpage.gch?pid=1002&nextpage=%s';
menu_items['%s']['%s']['URL'] = 'getpage.gch?pid=1002&nextpage=%s';
menu_subitems['%s']['%s']['%s']['URL'] = 'getpage.gch?pid=1002&nextpage=%s';
[webd gch:%s Line: %d] %s
[si_webd_f_get_cmret:gch:%s Line: %d]
log_gch
启动命令(位于 init.d/rcS 中):
# 为光猫的 Web 服务程序 httpd 分配必要的内核特权,使其能在非 root 权限下完成网络控制、系统管理、设备交互等功能
/sbin/setcap 'cap_net_bind_service,cap_ipc_owner,cap_syslog,cap_sys_tty_config,cap_fsetid,cap_ipc_lock,cap_kill,cap_net_admin,cap_sys_admin,cap_sys_boot,cap_net_broadcast,cap_net_raw,cap_setfcap,cap_setpcap,cap_sys_rawio,cap_sys_nice,cap_mknod,cap_sys_chroot,cap_sys_resource,cap_sys_time,cap_wake_alarm=eip' /sbin/httpd
(疑似)内部函数和功能推测
strings ./sbin/httpd | grep si_webd_f_
si_webd_f_file_write # 写入文件(如配置文件)
si_webd_f_log_gch # 记录日志
si_webd_f_file_read # 读取文件内容(如配置)
si_webd_f_show_plugin_log # 显示插件运行日志
si_webd_f_read_file # 读取文件
si_webd_f_query_paravalue # 查询参数值(如配置项具体数值)
si_webd_f_query_paraname # 查询参数名称(如配置项标识)
si_webd_f_query_menu_id # 查询菜单ID(用于菜单层级管理)
si_webd_f_query_wanctype # 查询WAN接口类型(如PPPoE、DHCP)
si_webd_f_query_name # 查询名称(设备)
si_webd_f_query_identity # 查询ID(设备)
si_webd_f_decrypt_nokey # 无密钥解密
si_webd_f_decrypt # 解密操作
si_webd_f_get_directory # 获取目录信息
si_webd_f_get_ip_mode # 获取IP模式(IPv4/IPv6/双栈)
si_webd_f_identity_to_name # ID转名称
si_webd_f_name_to_identity # 名称转ID
si_webd_f_show_log # 显示系统通用日志
si_webd_f_query_paralist # 查询参数列表
si_webd_f_dev_action # 执行设备动作(重启、恢复出厂等)
si_webd_f_output_menu_js_array # 生成菜单JS数组
si_webd_f_query_page_info # 查询页面信息(权限、关联菜单等)
si_webd_f_qeury_list_bycond # 按条件查询列表数据
si_webd_f_query_list # 查询列表数据
si_webd_f_set_para # 设置参数值
si_webd_f_get_para # 获取单个参数值
si_webd_f_del_inst # 删除配置实例(如WAN连接、用户账号)
si_webd_f_set_inst # 创建/修改配置实例
si_webd_f_get_inst # 获取配置实例详情
si_webd_f_puts # 向前端输出内容(HTML/数据)
si_webd_f_get_server_output # 获取其他服务的输出结果
si_webd_f_login # 处理用户登录逻辑(验证凭据、生成会话)
si_webd_f_hdl_mspage_out # 处理页面输出(过滤/格式化内容)
si_webd_f_set_menu_info # 设置菜单信息
si_webd_f_query_menu # 查询菜单信息
si_webd_f_get_cmret # 获取命令执行结果(底层操作反馈)
框架分析
index.gch(根目录路由文件)
index.gch 是核心路由文件,源码参见:光猫的嵌入式Linux系统(中兴zxic)操作手记,不再赘述。
其作用就是对访问网站根目录(http://192.168.1.1/xxx)的页面进行路由,其中xxx一般是 *.gch 页面:
- 特殊处理:xxx包含
&telnet.gch直接跳转到telnet.gch页面;访问start.ghtml直接跳转到frame.gch页面,等等。 - 直接跳转:大部分情况下会直接跳转,只要xxx在case所列的选项中就会直接跳转到xxx页面,例如 top.gch、template.gch等。(跳转到xxx页面时,可能在验权等处理之后会再次跳回到登录页)
- 其他:返回 404 页面。
start.ghtml(首页文件)
根据 index.gch 的路由分析可知,首页其实会引入使用 frame.gch。该页面即后台管理页面的框架,并通过 top.gch 展示框架,通过 template.gch 展示核心内容。
template.gch 是功能页面的核心(预)处理逻辑,它通过登录状态验权后,进一步通过 dmenu_func.gch(类似js,提供了验权等功能函数) 的 chkPageRight 函数去调用httpd服务封装的底层函数 query_page_info(page, "right"),获取具体页面的权限,并通过和当前页面比较来进行非常具体的页面权限校验。
根据实际测试结果,例如 hidden_factory_switch.gch,hidden_telnet_switch.gch 等关键页面,很可能被它封锁了所有页面权限(管理员权限需要0x…1),也就是只能通过所谓的特权账户(right = 0或3)才能被访问。而在目前的用户体系中尚未发现如何进入这种特权账户的状态。
// frame.gch:访问首页的实际处理文件
<%
var head = "<!DOCTYPE html PUBLIC \"-\/\/W3C\/\/DTD XHTML 1.0 Transitional\/\/EN\"";
var head1 = "\"http:\/\/www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">";
var head_html = "<html xmlns=\"http:\/\/www.w3.org/1999/xhtml\">";
=head + head1;
=head_html;
%>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=&?LANG;" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title><%=getenv("WEBTitle");%></title>
<style>
* {
margin: 0;
padding: 0;
}
html {
min-height:101%;
}
body {
background-color:#fff;
}
</style>
</head>
<script type="text/javascript">
// 防止页面被嵌套在iframe中(点击劫持防护)
if(self == top) {
document.documentElement.style.display = 'block';
} else {
top.location = self.location;
}
<%
IMPORT FILE "js/common.js";
%>
/**
* 重新计算iframe高度以适应内容
* 解决iframe内容高度动态变化导致的滚动问题
*/
function reinitIframe()
{
var iframe = document.getElementById("mainFrame");
try
{
var bHeight = iframe.contentWindow.document.body.scrollHeight;
var dHeight = iframe.contentWindow.document.documentElement.scrollHeight;
var height = Math.max(bHeight, dHeight);
iframe.height = height;
}catch (ex){}
}
window.setInterval("reinitIframe()", 200);
</script>
<body align="center">
<div align="center" style="margin:0 auto;" >
<table width="808px" border="0">
<tr><td>
<%
// 导入公共页面组件
IMPORT FILE "common_gch.gch";
// 处理数据库保存超时的情况:创建包含当前查询参数的表单
if (getenv("g_systemBusyStatus") == "DBSaveTimeout")
{
create_form_start("fSubmit", "");
var n = getQryStrNum();
for (var i=0; i<n; i++)
{
create_hidden_sep(getQryStrNameByIndex(i), getQryStrValueByIndex(i));
}
create_form_end();
}
// 处理页面路径:根据环境变量确定主框架加载的页面
var pagePath = "";
if(getenv("gw_saveurl") != "N/A")
{
// 仅对特殊页面 app_application_conf_t.gch 路径替换为 app_application_list_conf_t.gch,其他则是直接作为参数使用(pid=xxx&nextpage=xxx形式)。
if("pid=1002&nextpage=app_application_conf_t.gch" != getenv("gw_saveurl"))
{
pagePath = "?" + getenv("gw_saveurl");
}
else
{
pagePath = "?" + "pid=1002&nextpage=app_application_list_conf_t.gch";
}
// 清除已使用的环境变量
unsetenv("gw_saveurl");
unsetenv("gw_refreshtime");
}
else
{
// 未获取到保存的URL时使用默认路径
}
%>
<!-- 顶部框架:加载导航/标题栏内容 -->
<iframe width="808px" height="0px" src="top.gch" name="topFrame" scrolling="no" frameborder="0" id="topFrame"></iframe>
<!-- 主内容框架:根据计算的路径加载对应页面 -->
<iframe width="808px" src="template.gch<%=pagePath;%>" name="mainFrame" id="mainFrame" scrolling="no" frameborder="0" onload="this.height=350"></iframe>
</td></tr>
</table>
</div>
</body>
</html>
// top.gch:在首页的HTML中引用的顶部框架(非常简单的顶部导航的HTML,无需额外注释)
<%
IMPORT FILE "global.gch";
var head = "<!DOCTYPE html PUBLIC \"-\/\/W3C\/\/DTD XHTML 1.0 Transitional\/\/EN\"";
var head1 = "\"http:\/\/www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">";
var head_html = "<html xmlns=\"http:\/\/www.w3.org/1999/xhtml\">";
=head + head1;
=head_html;
%>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=&?LANG;" />
<title><%=getenv("WEBTitle");%></title>
<style>
<%
IMPORT FILE "&?css2path;";
IMPORT FILE "&?csslang;";
%>
</style>
</head>
<body>
<div id="container">
<div id="head">
<div id="banner" style="background:white;"></div>
</div>
</div>
</body>
</html>
// template.gch:首页的主内容框架代码,接受一个get参数串,一般形式为:pid=xxx
&nextpage=xxx。注意:本文件为业务的核心逻辑处理!!
<%
IMPORT FILE "global.gch";
IMPORT FILE "auth/api.gch";
IMPORT FILE "pageinfo_func.gch";
// 生成并设置CSRF会话令牌(安全防护)
var session_token = get_csrf_session_token();
session_set("_SESSION_TOKEN_USER", session_token);
setenv("_SESSION_TOKEN_USER", session_token);
// 获取设备实例编号及身份信息,查询对应IP地址
var FP_INSTNUM = query_list("OBJ_BRGRP_ID", "IGD");
var FP_IDENTITY = "";
var IPAddr = "";
if(FP_INSTNUM != 888 && FP_INSTNUM >= 1)
{
FP_IDENTITY = query_identity(0);
}
var GET_HANDLE = create_paralist();
if("" != FP_IDENTITY)
{
get_inst(GET_HANDLE, "OBJ_BRGRP_ID", FP_IDENTITY);
IPAddr = get_para(GET_HANDLE, "IPAddr");
}
destroy_paralist(GET_HANDLE);
setenv("Br0IPAddr", IPAddr);
// 会话认证流程
var sess_id = auth_sessid();
session_start(sess_id, 0);
var is_login = 0;
var is_banned = 0;
// 检查会话有效性,判断登录状态/封禁状态
if (auth_check(sess_id) == 0)
{
var login_status = auth_get_status("login");
if (login_status == "logined") {
is_login = 1;
} else if (login_status == "banned") {
admin_auth_logout();
is_banned = 1;
}
}
// 未登录/被封禁:跳转至认证超时处理
if (is_login == 0)
{
setenv("request/is_banned", is_banned);
IMPORT FILE "auth/timeout_ctl.gch";
return;
}
else
{
session_start(sess_id, -1); // 延长会话有效期
}
auth_env_sync(); // 同步认证环境变量
var head = "<!DOCTYPE html PUBLIC \"-\/\/W3C\/\/DTD XHTML 1.0 Transitional\/\/EN\"";
var head1 = "\"http:\/\/www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">";
var head_html = "<html xmlns=\"http:\/\/www.w3.org/1999/xhtml\">";
=head + head1;
=head_html;
%>
<head>
<META HTTP-EQUIV="pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Cache-Control" CONTENT="no-cache, must-revalidate">
<meta http-equiv="Content-Type" content="text/html; charset=&?LANG;" />
<script type="text/javascript" src="js/jquery-3.5.1.min.js"></script>
<title><%=getenv("WEBTitle");%></title>
<style>
<%
IMPORT FILE "&?css2path;";
IMPORT FILE "&?csslang;";
%>
</style>
<%
// 处理目标页面(nextpage参数)
var nextpage = raw_request("nextpage");
if(nextpage == "" || nextpage IS NULL)
{
nextpage = "status_dev_info_t.gch"; // 设备信息页面
}
else
{
// 确保页面以.gch结尾,否则使用默认页
if(strstr(nextpage, 0, ".gch") != -1)
{
var nexpage_temp = substr(nextpage, 0, strstr(nextpage, 0, ".gch") + 4); // 提取到首个.gch文件,舍弃其后的内容
nextpage = nexpage_temp;
}else{
nextpage = "status_dev_info_t.gch";
}
}
IMPORT FILE "dmenu_func.gch"; // 导入动态菜单工具
// 处理管理员权限(1:管理员;2:普通用户)
var adminright = getenv("Adminright");
log_gch("adminright:"+adminright);
setenv("Right", adminright == 1 ? 1 : 2);
// 检查页面访问权限,无权限则强制登出
if (0 == chkPageDisp(nextpage))
{
is_login = 0;
}
else
{
session_set("nextpage", nextpage);
setenv("nextpage", nextpage);
}
// 获取页面关联的上级ID,用于菜单定位
var selectSupId = query_page_info(nextpage, "supId");
setenv("selectSupId", selectSupId);
// 加载菜单文本
var menu011 = load_string("menu011");
var menu012 = load_string("menu012");
%>
<SCRIPT LANGUAGE="javascript">
var g_errorInfo = "&?ErrRep;";
var timeid;
<%
IMPORT FILE "js/common.js";
IMPORT FILE "js/common_check.js";
IMPORT FILE "js/pro_commom.js";
IMPORT FILE "js/menu.js";
IMPORT FILE "js/events.js";
// 无权限时强制登出并重定向
if(is_login == 0)
{
admin_auth_logout();
%>
top.location.href = "/";
<%
}
// 获取会话超时配置(管理员/普通用户区分)
var FP_HANDLE;
FP_HANDLE = create_paralist();
if(1 == getenv("Right")) {
get_inst(FP_HANDLE, "OBJ_LOGOUT_ID", "");
var TimerLogout = get_para(FP_HANDLE, "TimerLogout");
} else {
get_inst(FP_HANDLE, "OBJ_LOGOUT_USER_ID", "");
var TimerLogoutUser = get_para(FP_HANDLE, "TimerLogoutUser");
}
destroy_paralist(FP_HANDLE);
// 导入超时控制JS逻辑
setenv("request/callback", "relogin()");
IMPORT FILE "auth/timeout_ctl_js.gch";
%>
/**
* 超时重登录处理:提交登出请求并重定向
*/
function relogin()
{
var ipaddr = "<%=encodeJS(getenv("Br0IPAddr"));%>";
var jqxhr=$.post("setlang.gch",
{
"logout" : 1,
},
function(data, status) {
status && (top.location.href = "/");
});
}
/**
* 手动登出处理:带CSRF令牌提交登出请求
*/
function onClickLogout()
{
var ipaddr = "<%=encodeJS(getenv("Br0IPAddr"));%>";
var session_token = "<%=encodeJS(session_token);%>";
var jqxhr=$.post("setlang.gch",
{
"logout" : 1,
"_SESSION_TOKEN_USER":session_token
},
function(data, status) {
status && (top.location.href = "/");
});
}
/**
* 切换语言:提交语言切换表单
*/
function onClickSetLang()
{
var parm="pid=1002?";
var lang = "<%=getenv("gw_lang");%>";
getObj("_lang").value = ("English" == lang) ? "Chinese" : "English";
getObj("action").value = "Change_Lang";
document.fLangSet.submit();
}
// 刷新顶部框架,失败则强制登出
try
{
top.topFrame.location.reload(true);
}catch(error)
{
var jqxhr=$.post("setlang.gch", {"logout" : 1}, function(data, status) {
status && (top.location.href = "/");
});
}
/**
* 检测IE浏览器版本(兼容处理)
*/
function IEVersion() {
var userAgent = navigator.userAgent;
var isIE = userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1;
var isEdge = userAgent.indexOf("Edge") > -1 && !isIE;
var isIE11 = userAgent.indexOf('Trident') > -1 && userAgent.indexOf("rv:11.0") > -1;
if(isIE) {
var reIE = new RegExp("MSIE (\\d+\\.\\d+);");
reIE.test(userAgent);
var fIEVersion = parseFloat(RegExp["$1"]);
return [7,8,9,10].includes(fIEVersion) ? fIEVersion : 6;
} else if(isEdge) {
return 'edge';
} else if(isIE11) {
return 11;
}else{
return -1;
}
}
// 低版本IE处理(防止嵌套)
var ieVersion = IEVersion();
if(ieVersion < 7 && ieVersion != -1)
{
self == top ? (document.documentElement.style.display = 'block') : (top.location = self.location);
}
/**
* 回车键处理:文本框回车转为Tab
*/
function setenter(e)
{
e = window.event?window.event:e;
if( e == "[object]" && event.keyCode == 13 && event.srcElement.type == "text")
{
event.keyCode = 9;
}
}
</SCRIPT>
<body id="htmlBody">
<!-- 错误提示层 -->
<div id="myLayer" onmousedown="setinner('hidden')">
<iframe id="myiframe" style="position: absolute; width: 179px; height: 88px; border: 1px none #000000; z-index: -1; filter:alpha(opacity=0); -moz-opacity:0">
</iframe>
<div id="layer_err_title">
<div style="position:relative; left:35px; top:8px;" >
<font class="note" id="errnote" > &?ErrRep;</font>
</div>
</div>
<div id="layer_err_content">
<font id="errmsg" class="notecontent" > </font>
</div>
</div>
<!-- 帮助弹窗 -->
<div id="alertFram" align="center" style="position:absolute; width:314px; height:height:auto; z-index:999; left: 380px; top: 110px; font-size: 9pt; display:none;">
<div style='width:220px; position: absolute;' align=left id='dragAble' class='dragAble'>
<div id='helpTextWrapper' style='position: relative'>
<iframe id="helpiframe" style="position: absolute; width: 100%; display: block; border: 0px 0px; z-index: -1;"></iframe>
<div class="round">
<table style='width:220px;background-color:#fff; border:1px solid #427594;' cellpadding='0' cellspacing='0'>
<tr align='center'>
<td style='height:23px;width:90%;background-color:#427594;'>
<font align='left' color='#FFFFFF'><b> &?menu006;</b></font>
</td>
<td onclick='hideHelp();' class="help_close">
<img src='img/close.gif' border='0'>
</td>
</tr>
<tr>
<td colspan='2' height='200px' width='220px' valign='top' align='left' class='word-break-all' style='padding:5px 0 0 10px;'>
<%=load_string(query_page_info(nextpage, "helpTag"));%>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<!-- 主内容区域 -->
<div id="container">
<!-- Logo区域 -->
<div class="logoarea round">
<div class="logo_text">&?CorporationName;</div>
<div class="type">
<%
if(get_config("useWebModelName")=="Y")
{
if(getenv("ponmode") == "EPON")
{
%>&?ModelName;<font>E</font><%
}
else if(getenv("ponmode") == "GPON")
{
%>&?ModelName;<font>G</font><%
}
}
else
{
=getenv("WEBTitle");
}
%>
</div>
</div>
<div class="divide_line"></div>
<!-- 主内容区 -->
<div class="content" id="content">
<!-- 顶部菜单 -->
<div class="class1 round" id="class1"></div>
<!-- 右侧操作区(帮助/登出) -->
<div class="help">
<%
var helpTag = query_page_info(nextpage, "helpTag");
var helpInfo = load_string(helpTag);
%>
<ul>
<table>
<tr height="43px"><td></td></tr>
<tr height="43px"><td>
<input name="help_info" type="button" onclick="showHelp('<%=helpInfo;%>', '&?menu006;')" class="help_button" id="help_button" value="&?menu006;"/>
</td></tr>
<tr height="43px"><td>
<input name="help_info" type="button" onclick="onClickLogout()" class="help_button" id="exit_button" value="<%=menu011;%>"/>
</td></tr>
</table>
</ul>
</div>
<!-- 主内容区 -->
<div class="main_content text_content">
<!-- 左侧子菜单 -->
<div class="class2 round" id="class2">
<div class="class2_h2" id="menu0"></div>
</div>
<!-- 页面内容(动态导入nextpage参数页面) -->
<div id="div1">
<%
if(is_login != 0)
{
IMPORT FILE nextpage; // 导入目标页面内容
}
%>
<div class="div4"></div>
</div>
</div>
<!-- 底部操作按钮区 -->
<div class="bottom_div round" id="bottom"> </div>
</div>
<!-- 版权信息 -->
<div class="copyright">
<%if(get_config("hideCopyRight")!="Y"){%>&?CopyRight;<%}%>
</div>
</div>
<!-- 隐藏域:上传状态/临时URL -->
<input type="hidden" id="IF_UPLOADING" name="IF_UPLOADING" value="<%=getenv("Uploading");%>">
<input type="hidden" id="temClickURL" name="temClickURL" value="">
<script language="javascript">
<%
IMPORT FILE "pagefunc_js.gch"; // 导入页面功能JS
%>
// 动态生成底部操作按钮(根据页面状态staFlag)
text = "<table class='bottom_table' border='0' cellpadding='0' cellspacing='0'>"
+"<tbody><tr>"
+"<td class='bottom_td1'></td>"
+"<td class='bottom_td2'>"
<%
var staFlag = query_page_info(nextpage, "staFlag");
switch(staFlag)
{
case 1:
case 6:
// 状态1/6:显示刷新按钮
%>+"<input name='Submit' type='button'id='Btn_ReFresh' onclick=pageReFresh() class='button' value=' &?btn001; ' /> "<%
break;
case 3:
// 状态3:显示添加/取消/编辑/删除按钮
%>+"<input name='Submit' type='button' id='Btn_Add' onclick=pageAdd() class='button' value=' &?btn002; ' />"+"<input name='Submit' type='button' id='Btn_Cancel' onclick=pageCancel() class='button' value=' &?btn004; '> "+"<input name='Submit' type='button' id='Btn_DoEdit' onclick=pageEdit() class='button' value=' &?btn003; ' style='display:none'/>"+"<input name='Submit' type='button' id='Btn_Delete' onclick=pageDel() class='button' value=' &?btn006; ' style='display:none'/> "<%
break;
case 2:
// 状态2:显示提交/取消按钮
%>+"<input name='Submit' type='button' id='Btn_Submit' onclick=pageSubmit() class='button' value=' &?btn005; ' /> "+"<input name='Submit' type='button' id='Btn_Cancel' onclick=pageCancel() class='button' value=' &?btn004; '> "<%
break;
case 7:
// 状态7:显示返回按钮
%>+"<input name='Back' type='button' id='Btn_Back' onclick=pageBack() class='button' value=' &?btn013; ' /> "<%
break;
case 8:
// 状态8:显示提交/取消/隐藏返回按钮
%>+"<input name='Submit' type='button' id='Btn_Back' onclick=pageBack() class='button' value=' &?btn013; ' style='display:none'/>"+"<input name='Submit' type='button' id='Btn_Submit' onclick=pageSubmit() class='button' value=' &?btn005; ' />"+"<input name='Submit' type='button' id='Btn_Cancel' onclick=pageCancel() class='button' value=' &?btn004; '> "<%
break;
default:
break;
}
%>
+"</td></tr></tbody>"+"</table>";
getObj("bottom").innerHTML=text;
<%
output_menuJSArray(); // 输出菜单数据数组
%>
var selectPage = "<%=nextpage;%>"; // 当前选中页面
var selectSupId = "<%=selectSupId;%>"; // 当前选中上级菜单ID
/**
* 更新顶部菜单:根据选中状态添加样式
*/
function TOPmenuUpdate()
{
var TOPmenuText = "<ul>";
for(var supId in meta_menu)
{
if(supId == "del") continue;
var styleClass = (supId == selectSupId) ? "selected" : "";
var lang = "<%=getenv("gw_lang");%>";
var TOPmenuTemp = ("Chinese" == lang) ?
"<li><a class='" + styleClass + "' ><div id='"+ supId + "' onclick=\"javascript:openLink('" + meta_menu[supId]['URL'] + "')\">" + meta_menu[supId]['langName'] + "</div></a></li>" :
"<li><b class='" + styleClass + "' ><div id='"+ supId + "' onclick=\"javascript:openLink('" + meta_menu[supId]['URL'] + "')\">" + meta_menu[supId]['langName'] + "</div></b></li>";
TOPmenuText += TOPmenuTemp;
}
TOPmenuText += "</ul>";
getObj("class1").innerHTML= TOPmenuText;
}
/**
* 更新左侧子菜单:根据选中状态生成层级菜单
*/
function LEFTmenuUpdate()
{
var supId = selectSupId;
text = "<ul class='class2ul'>";
for ( var midId in menu_items[supId] )
{
var stat = getMidMenuStat(supId, midId);
if ( stat == "single" )
{
text += (menu_items[supId][midId]['page'] == selectPage) ?
"<li class='class2li'><span class='text_h2_s'>" + menu_items[supId][midId]['langName'] + "</span>" :
"<li class='class2li'><a class='h2_link' id='"+ midId + "' onclick='javascript:openLink(\"" + menu_items[supId][midId]['URL'] + "\")'>" + menu_items[supId][midId]['langName'] + "</a>";
}
else if ( stat == "closed" )
{
text += "<li class='class2li'><a class='h2_link' id='"+ midId + "' onclick='javascript:openLink(\"" + menu_items[supId][midId]['URL'] + "\")'>" + menu_items[supId][midId]['langName'] + "</a>";
}
else if ( stat == "open" )
{
text += "<li class='class2li'><div class='class2textspan'><span class='text_h2_s class2textspan'>" + menu_items[supId][midId]['langName'] + "</span></div>";
var textc3 = "<ul class='class3ul'>";
for ( var subId in menu_subitems[supId][midId] )
{
if(subId == "del") continue;
textc3 += (menu_subitems[supId][midId][subId]['page'] == selectPage) ?
"<li class='class3li_selected'><div id='"+subId+"'>" + menu_subitems[supId][midId][subId]['langName'] + "</div></li>" :
"<li class='class3li'><a class='class3_space' id='"+subId+"' onclick=\"javascript:openLink('" + menu_subitems[supId][midId][subId]['URL'] + "')\"><div class=\"class3lispan\">" + menu_subitems[supId][midId][subId]['langName'] + "</div></a></li>";
}
textc3 += "</ul>";
text += textc3 + "</li>";
}
}
text += "</ul>";
getObj("menu0").innerHTML=text;
}
// 统一更新菜单
function menuUpdate()
{
TOPmenuUpdate();
LEFTmenuUpdate();
}
// 菜单hover样式切换
function altMenuClass(ele,flag)
{
if (flag) {
ele.className += " bghover";
} else {
ele.className = ele.className.replace(/(^|\s)bghover\b/, "");
}
}
/**
* 页面加载初始化
*/
function myOnLoad()
{
menuDisp(); // 显示菜单
// 绑定全局事件(键盘/鼠标)
addEvent(getObj("htmlBody"), "keydown", setenter);
addEvent(getObj("htmlBody"), "keyup", setinnerHidden);
addEvent(getObj("htmlBody"), "mousedown", setinnerHidden);
fnBreakWordAll({word:15,re:'[\\w]'});
framedispalay('myiframe'); // 初始化iframe显示
<%
if(staFlag != 1 && staFlag != 0)
{
%>
// 定位错误提示层
getObj("myLayer").style.left = "580px";
getObj("myLayer").style.top = "59px";
<%
if(is_login != 0)
{
// 显示错误信息(若配置开启)
if (get_config("showerror")=="Y")
{
%>
ShowError();
<%
}
// 处理数据库保存超时:同步表单数据
if ("DBSaveTimeout" == getenv("g_systemBusyStatus"))
{
unsetenv("g_systemBusyStatus");
%>
try
{
var clds = parent.document.getElementById("fSubmit").childNodes;
for (var i=0; i<clds.length; i++)
{
var nodeName = clds[i].nodeName;
if (nodeName.match(/Input/i) != null)
{
try { setValue(clds[i].id, clds[i].value); }
catch(e) { appendHiddenInput("fSubmit", clds[i].id, clds[i].value); }
}
}
}
catch(e) {}
<%
}
%>
pageLoad(getURL("<%=nextpage;%>")); // 加载页面数据
<%
}
}
%>
}
// 生成并设置CSRF令牌(用于表单提交)
<%
var session_token = get_csrf_session_token();
session_set("_SESSION_TOKEN", session_token);
setenv("_SESSION_TOKEN", session_token);
%>
/**
* 为所有表单添加CSRF令牌(排除文件上传表单)
*/
function addToken2AllForms()
{
function doAddLogic()
{
var session_token = "<%=encodeJS(getenv("_SESSION_TOKEN"));%>";
var forms = document.getElementsByTagName("form");
for ( var i=0; i<forms.length; i++ )
{
var tempform = forms[i];
var sEnctype = tempform.getAttribute("enctype");
// 跳过multipart/form-data类型(文件上传)
if ( sEnctype && "multipart/form-data" == sEnctype.toLowerCase() ) continue;
// 添加隐藏的令牌字段
var tokenInput = document.createElement("input");
tokenInput.setAttribute("id", "_SESSION_TOKEN");
tokenInput.setAttribute("name", "_SESSION_TOKEN");
tokenInput.setAttribute("type", "hidden");
tokenInput.setAttribute("value", session_token);
tempform.appendChild(tokenInput);
}
}
// 整合页面加载事件
if( window.onload == null )
{
window.onload=function(){ doAddLogic(); myOnLoad(); }
}
else
{
var tempfunction = window.onload;
window.onload=function(){ tempfunction(); doAddLogic(); myOnLoad(); }
}
}
addToken2AllForms(); // 执行令牌添加逻辑
</script>
</body>
</html>
// dmenu_func.gch:相当于是首页的主框架的js文件(功能函数文件)
<%
// 菜单层级标志
var g_midFlag = 0;
var g_subFlag = 0;
/**
* 检查页面访问权限
*/
function chkPageRight(page)
{
// query_page_info 为 httpd 服务内置底层函数,具体逻辑未知
// 重要:但可以通过拼接pid和nextpage访问一些页面的具体表现来看,很多关键的hidden页面(包括Telnet相关的)都被这个函数给拦截了!
var pageright = query_page_info(page, "right"); // 页面所需权限
var userright = getenv("Right"); // 用户实际权限
if(userright == "N/A") return 0; // 无效权限
if (userright == 0 || userright == 4) return 1; // 特殊权限直接通过(实际上用户权限为2,管理员权限为1,均不满足)
// 权限位运算校验(用户权限需包含页面所需权限)
var tmpright = 1 << (userright - 1);
return (tmpright & pageright) == tmpright ? 1 : 0;
}
/**
* 检查页面是否符合当前升级模式
*/
function chkPageUpMode(page)
{
var currUpMode = getenv("TypeValue");
var pageUpMode = query_page_info(page, "upMode");
return (currUpMode & pageUpMode) == pageUpMode ? 1 : 0;
}
/**
* 综合检查页面是否应显示(权限+模式+配置+地区等)
*/
function chkPageDisp(page)
{
if (0 == chkPageRight(page) || 0 == chkPageUpMode(page)) return 0;
if (page == "commontest_t.gch" && get_config("commtestpage") == "N") return 0;
if (page == "help_t.gch" && get_config("help") == "N") return 0;
if (page == "manager_buss_mgr_t.gch" && getenv("OperationManageEnable") == "0") return 0;
if(("0" == getenv("SNEnable")) && (page == "manager_SN_register_t.gch")) return 0;
if(("0" != getenv("SNWebMode")) && ("2" != getenv("SNWebMode")) && (page == "manager_SN_register_t.gch")) return 0;
if (page == "status_country_code_t.gch" && getenv("hidden_menu") != "countrycode") return 0;
if (page == "pon_manager_service_ctl_t.gch" && getenv("hidden_menu") != "service") return 0;
if (page == "wlan_service_hidden_ctl_t.gch" && getenv("hidden_menu") != "configure") return 0;
if(page == "pon_monitor_config_t.gch" && "210" != getenv("CountryCode")) return 0;
if(page == "anhui_monitor_config_t.gch" && "204" != getenv("CountryCode")) return 0;
if (page == "status_dev_info_t.gch" && ("212" == getenv("CountryCode") || "208" == getenv("CountryCode"))) return 0;
if (page == "status_dev_info_t_fj.gch" && "212" != getenv("CountryCode") && "208" != getenv("CountryCode")) return 0;
if("212" != getenv("CountryCode") && 1 != getenv("Right")){
switch(page){
case "app_dmz_conf_t.gch":
case "net_dhcp_specialdevice_t.gch":
case "net_v6_ra_server_t.gch":
case "net_v6_dhcp_dynamic_t.gch":
case "ipv6_net_prefix_t.gch":
return 0;
}
}
if((page == "net_dhcp_dynamic_t.gch" || page == "app_virtual_conf_t.gch") && 1 != getenv("Right") && "212" != getenv("CountryCode") && "205" != getenv("CountryCode")) return 0;
var VoIPProtocal = getenv("VoIPProtocal");
if ("H248" == VoIPProtocal) {
switch (page) {
case "app_voip_mgcpbasic_t.gch": case "app_voip_mgcpauth_t.gch":
case "app_voip_sippro_t.gch": case "app_sipline_t.gch":
case "app_voip_BasicControl_t.gch": case "app_voip_service_t.gch":
case "app_voip_sipdigitmap_t.gch": case "app_voip_sipqos_t.gch":
case "pon_voip_siprenew_t.gch": case "status_voip_phoneNumber_t.gch":
return 0;
}
} else if ("MGCP" == VoIPProtocal) {
switch (page) {
case "app_voip_h248basic_t.gch": case "app_voip_h248auth_t.gch":
case "app_voip_h248endpoint_t.gch": case "app_voip_h248_servs_t.gch":
case "voip_h248qos_t.gch": case "app_voip_sippro_t.gch":
case "app_sipline_t.gch": case "app_voip_BasicControl_t.gch":
case "app_voip_service_t.gch": case "app_voip_sipdigitmap_t.gch":
case "app_voip_sipqos_t.gch": case "status_voip_phoneNumber_t.gch":
return 0;
}
} else if ("SIP" == VoIPProtocal) {
switch (page) {
case "app_voip_h248basic_t.gch": case "app_voip_h248auth_t.gch":
case "app_voip_h248endpoint_t.gch": case "app_voip_h248_servs_t.gch":
case "voip_h248qos_t.gch": case "pon_voip_renew_t.gch":
case "app_voip_mgcpbasic_t.gch": case "app_voip_mgcpauth_t.gch":
return 0;
}
}
if((page == "pon_net_wanuser_conf_t.gch") && ("223" != getenv("CountryCode"))) return 0;
if((page == "net_user_wan_conf_t.gch") && ("0" != getenv("CountryCode") && "205" != getenv("CountryCode"))) return 0;
if("217" != getenv("CountryCode")){
if(page == "e8_net_qos_basic_new_t.gch" || page == "net_qos_congestion_statisticsYN_t.gch") return 0;
} else {
switch (page) {
case "e8_net_qos_basic_t.gch": case "net_qos_qostemplate_t.gch":
case "e8_net_qos_localapp_t.gch": case "net_qos_congestion_t.gch":
case "net_qos_congestion_statistics_t.gch":
return 0;
}
}
if( page == "usbrestore_t.gch" && 1 != getenv("Right") && "205" != getenv("CountryCode")) return 0;
if(getenv("ponmode") == "EPON"){
if (page == "pon_net_cltlmt_t.gch" && "208" != getenv("CountryCode") && 1 == getenv("Right")) return 0;
if (page == "pon_silence_t.gch" && getenv("hidden_menu") != "silence") return 0;
}
return 1; // 所有检查通过,允许显示
}
/**
* 判断页面是否属于VoIP相关模块
*/
function chkPageBelongVoip(page)
{
switch(page)
{
case "app_voip_mgcpbasic_t.gch": case "app_voip_mgcpauth_t.gch":
case "app_voip_sippro_t.gch": case "app_sipline_t.gch":
case "app_voip_homeline_t.gch": case "app_voip_sipmed_t.gch":
case "app_voip_dialplan_t.gch": case "app_voip_BasicControl_t.gch":
case "app_voip_service_t.gch": case "app_voip_sipdigitmap_t.gch":
case "app_voip_sipqos_t.gch": case "status_voip_phoneNumber_t.gch":
case "app_voip_sipadv_t.gch": case "app_voip_cid_t.gch":
case "app_voip_sipslc_t.gch": case "pon_voip_siprenew_t.gch":
case "app_voip_h248basic_t.gch": case "app_voip_h248auth_t.gch":
case "app_voip_h248endpoint_t.gch": case "app_voip_h248_servs_t.gch":
case "voip_h248qos_t.gch": case "pon_voip_renew_t.gch":
case "status_voip_4less_t.gch":
break;
default:
return 0;
}
return 1;
}
/**
* 填充顶级菜单元数据(生成JS数组)
*/
function fillMetaMenu(supId, page, flag)
{
var nameTag = query_menu_info(supId, "nameTag");
var langName = load_string(nameTag);
= "meta_menu['" + supId + "'] = new Array();\n";
= "meta_menu['" + supId + "']['langName'] = '" + langName + "';\n";
= "meta_menu['" + supId + "']['page'] = '" + page + "';\n\n";
if ("1" == flag)
{
= "menu_items['" + supId + "'] = new Array();\n";
= "menu_subitems['" + supId + "'] = new Array();\n";
}
}
/**
* 填充二级菜单数据(生成JS数组)
*/
function fillMenuItems(supId, midId, page, flag)
{
var nameTag = query_menu_info(midId, "nameTag");
var langName = load_string(nameTag);
= "menu_items['" + supId + "']['" + midId + "'] = new Array();\n";
= "menu_items['" + supId + "']['" + midId + "']['langName'] = '" + langName + "';\n";
= "menu_items['" + supId + "']['" + midId + "']['page'] = '" + page +"';\n\n";
if ("1" == flag)
{
= "menu_subitems['" + supId + "']['" + midId + "'] = new Array();\n";
}
}
/**
* 填充三级子菜单数据(生成JS数组)
*/
function fillMenuSubitems(supId, midId, subId, page)
{
var nameTag = query_menu_info(subId, "nameTag");
var langName = load_string(nameTag);
= "menu_subitems['" + supId + "']['" + midId + "']['" + subId + "'] = new Array();\n";
= "menu_subitems['" + supId + "']['" + midId + "']['" + subId + "']['langName'] = '" + langName + "';\n";
= "menu_subitems['" + supId + "']['" + midId + "']['" + subId + "']['page'] = '" + page + "';\n\n";
}
/**
* 处理一级菜单离开事件(生成符合条件的菜单)
*/
function hdlMC1Leave(supId, mode, nextpage, npsi)
{
var page = query_menu_info(supId, "page");
if (1 == chkPageDisp(page) && 1 == hdlMSPageOut(page, nextpage))
{
if ("initM" == substr(mode, 0, 5))
{
fillMetaMenu(supId, page, "0");
}
}
}
/**
* 处理二级菜单离开事件(生成符合条件的菜单)
*/
function hdlMC2Leave(supId, midId, mode, nextpage, npsi)
{
var page = query_menu_info(midId, "page");
if (1 == chkPageDisp(page) && 1 == hdlMSPageOut(page, nextpage))
{
if ("initM" == substr(mode, 0, 5))
{
if (0 == g_midFlag)
{
g_midFlag = 1;
if ("initMC1" == mode || ("initM" == mode && supId != npsi))
{
fillMetaMenu(supId, page, "0");
return;
}
fillMetaMenu(supId, page, "1");
}
fillMenuItems(supId, midId, page, "0");
}
}
}
/**
* 处理三级菜单离开事件(生成符合条件的菜单)
*/
function hdlMC3Leave(supId, midId, subId, mode, nextpage, npsi, npmi)
{
var page = query_menu_info(subId, "page");
if (1 == chkPageDisp(page) && 1 == hdlMSPageOut(page, nextpage))
{
if ("initM" == substr(mode, 0, 5))
{
if (0 == g_subFlag)
{
g_subFlag = 1;
if (0 == g_midFlag)
{
g_midFlag = 1;
if ("initMC1" == mode || ("initM" == mode && supId != npsi))
{
fillMetaMenu(supId, page, "0");
return;
}
fillMetaMenu(supId, page, "1");
}
fillMenuItems(supId, midId, page, "1");
}
fillMenuSubitems(supId, midId, subId, page);
}
}
}
/**
* 遍历菜单树,生成符合条件的菜单数据
*/
function visitMenuTree(mode, nextpage)
{
var supNum = 0;
var supId[20] = {""};
var midNum = 0;
var midId[50] = {""};
var subNum = 0;
var subId[50] = {""};
var npsi;
var npmi;
if (mode == "initM")
{
npsi = query_page_info(nextpage, "supId");
npmi = query_page_info(nextpage, "midId");
}
supNum = query_menu("NULL");
for (var i=0; i<supNum; i++) supId[i] = query_menu_id(i);
for (var i=0; i<supNum; i++ )
{
midNum = query_menu(supId[i]);
if (0 == midNum)
{
hdlMC1Leave(supId[i], mode, nextpage, npsi);
}
for (var j=0; j<midNum; j++) midId[j] = query_menu_id(j);
g_midFlag = 0;
for (var j=0; j<midNum; j++)
{
subNum = query_menu(midId[j]);
if (0 == subNum)
{
hdlMC2Leave(supId[i], midId[j], mode, nextpage, npsi);
}
for (var k=0; k<subNum; k++) subId[k] = query_menu_id(k);
g_subFlag = 0;
for (var k=0; k<subNum; k++)
{
hdlMC3Leave(supId[i], midId[j], subId[k], mode, nextpage, npsi, npmi);
if (("initMC1" == mode && 1 == g_midFlag) || ("initM" == mode && supId[i] != npsi && 1 == g_subFlag))
{
break;
}
}
if (("initMC1" == mode && 1 == g_midFlag) || ("initM" == mode && supId[i] != npsi && 1 == g_midFlag))
{
break;
}
}
}
}
%>
common_gch.gch(通用函数)
common_gch.gch 是会被所有脚本直接或间接引用的,提供通用且基本功能函数的文件。该文件已经比较底层,其函数使用的是httpd服务的底层函数进行实现。
function setpara(handle, para_name) # 将请求参数赋值给指定句柄的参数
function set_multiinst_para(handle, para_name, index) # 将带索引的请求参数赋值给指定句柄的参数
function init_request_para(para_name) # 初始化请求参数
function create_form_start(ID, URL) # 创建表单开始标签
function create_form_start_ex(ID, URL, TARGET) # 创建带扩展参数TARGET的表单开始标签
function create_form_end() # 创建表单结束标签
function create_hidden_sep(ID, value) # 创建隐藏输入框
function set_hidden(ID, value) # 设置隐藏输入框的值
function create_hidden_newpara(PARA[], num) # 创建多个新参数的隐藏输入框
function cover_hidden(handle, PARA[], num) # 覆盖多个隐藏输入框的值
function filter_cover_hidden(handle, PARA[], MASK_PARA[], num) # 根据掩码参数过滤并覆盖隐藏输入框的值
function create_hidden_para(handle, PARA[], num) # 根据句柄创建多个参数的隐藏输入框
function filter_create_hidden_para(handle, PARA[], MASK_PARA[], num) # 根据掩码参数过滤并创建多个参数的隐藏输入框
function create_newwan_hidden_para(handle, PARA[], num) # 创建新WAN参数的隐藏输入框
function create_hidden_multiinstnewpara(handle, PARA[], instindex, num) # 创建多实例新参数的隐藏输入框
function create_hidden_multiinstpara(handle, PARA[], instindex, num) # 创建多实例参数的隐藏输入框
function filter_create_hidden_multiinstpara(handle, PARA[], MASK_PARA[], instindex, num) # 根据掩码参数过滤并创建多实例参数的隐藏输入框
function create_hidden_multiinstpara_user(handle, PARA[], instindex, num,user) # 创建带用户标识的多实例参数隐藏输入框
function create_hidden_multiinstpara1(handle, PARA[], instindex, num) # 创建多实例参数的隐藏输入框(带索引参数名)
function set_hidden_multiinstpara(handle, PARA[], instindex, num) # 设置多实例参数的隐藏输入框值
function create_special_hidden_multiinstpara(handle, inputName, handleName, instindex) # 创建特殊多实例参数的隐藏输入框
function ifIndexNULL(flag, FP_INDEX) # 处理索引为空的情况
function getInstIndex(flag) # 获取实例索引
function getInstNum(FP_OBJNAME) # 获取实例数量
function getInstIdentity(FP_OBJNAME, FP_INDEX) # 获取实例标识
function getMultiInstIdentity(FP_OBJNAME, FP_MULTIDISPLAY, FP_INDEX) # 获取多实例标识
function getInstNumByAction(FP_OBJNAME, FP_MULTIDISPLAY, FP_ACTION) # 根据操作获取实例数量
function getInstIdentityByAction(FP_OBJNAME, FP_MULTIDISPLAY, FP_ACTION, FP_INDEX) # 根据操作获取实例标识
function createBasicHidden() # 创建基本的隐藏输入框
function createSingleInstHidden(PARA, FP_PARANUM) # 创建单实例的隐藏输入框
function createHigherHidden(FP_INDEX) # 创建高级的隐藏输入框(带索引)
function createMultiInstHidden(PARA, FP_PARANUM, FP_INDEX) # 创建多实例的隐藏输入框
function createSubmitHidden(FP_MULTIDISPLAY, PARA, FP_PARANUM, FP_INDEX) # 根据多实例显示状态创建提交用的隐藏输入框
function getNewInstError(FP_ERRORSTR) # 获取新实例的错误信息
function setInst(FP_OBJNAME, FP_IDENTITY, PARA, FP_PARANUM) # 设置实例参数
function applyOrNewInst(FP_OBJNAME, FP_ACTION, FP_IDENTITY, PARA, FP_PARANUM, FP_ERRORSTR) # 应用或创建新实例
function setInstindex(FP_OBJNAME, FP_IDENTITY, PARA, FP_PARANUM,FP_INDEX) # 设置带索引的实例参数
function applyOrNewInstindex(FP_OBJNAME, FP_ACTION, FP_IDENTITY, PARA, FP_PARANUM, FP_INDEX,FP_ERRORSTR) # 应用或创建带索引的新实例
function applyOrNewOrDelInst(FP_OBJNAME, FP_ACTION, FP_IDENTITY, PARA, FP_PARANUM, FP_ERRORSTR) # 应用、创建或删除实例
function applyOrNewOrDelInstindex(FP_OBJNAME, FP_ACTION, FP_IDENTITY, PARA, FP_PARANUM, FP_INDEX,FP_ERRORSTR) # 应用、创建或删除带索引的实例
function displayOldSingleInst(FP_OBJNAME, FP_IDENTITY, PARA, FP_PARANUM, FP_ERRORSTR) # 显示旧的单实例
function displaySingleInst(FP_OBJNAME, PARA, FP_PARANUM) # 显示单实例
function displayInstByNum(FP_OBJNAME, FP_INSTNUM, PARA, FP_PARANUM) # 按数量显示实例
function displayInstByNumUser(FP_OBJNAME, FP_INSTNUM, PARA, FP_PARANUM,USER) # 按数量和用户显示实例
function displayMultiInst(FP_OBJNAME, PARA, FP_PARANUM) # 显示多实例
function displayFunctionInst(FP_OBJNAME, FP_MULTIDISPLAY, PARA, FP_PARANUM, FP_ERRORSTR) # 根据功能显示实例
function displayInst(FP_OBJNAME, FP_MULTIDISPLAY, PARA, FP_PARANUM, FP_ERRORSTR) # 显示实例并返回数量
function displayInstByAction(FP_OBJNAME, FP_MULTIDISPLAY, FP_INSTNUM, FP_ACTION, PARA, FP_PARANUM) # 根据操作显示实例
function getDisplayInstError(FP_ERRORSTR) # 获取显示实例的错误信息
function setInstNumHidden(FP_OBJNAME, FP_MULTIDISPLAY) # 设置实例数量的隐藏输入框
function getBasicGCH(FP_OBJNAME, FP_MULTIDISPLAY, PARA, FP_PARANUM, FP_ACTION, FP_INDEX, FP_ERRORSTR) # 处理基本的GCH(通用配置处理)逻辑
function getBasicGCHindex(FP_OBJNAME, FP_MULTIDISPLAY, PARA, FP_PARANUM, FP_ACTION, FP_INDEX, FP_ERRORSTR) # 处理带索引的基本GCH逻辑
function getSubmitGCH(FP_OBJNAME, FP_MULTIDISPLAY, PARA, FP_PARANUM) # 处理提交的GCH逻辑
function getSubmitGCHindex(FP_OBJNAME, FP_MULTIDISPLAY, PARA, FP_PARANUM) # 处理带索引的提交GCH逻辑
function getFunctionGCH(FP_OBJNAME, FP_MULTIDISPLAY, PARA, FP_PARANUM, FP_ACTION, FP_INDEX, FP_ERRORSTR) # 处理功能GCH逻辑
function getPageGCH(FP_OBJNAME, FP_MULTIDISPLAY, PARA, FP_PARANUM) # 处理页面GCH逻辑
function getOldPageGCH(FP_OBJNAME, FP_MULTIDISPLAY, PARA, FP_PARANUM) # 处理旧页面的GCH逻辑
function getOldFunctionGCH(FP_OBJNAME, FP_MULTIDISPLAY, PARA, FP_PARANUM, FP_ACTION, FP_INDEX, FP_ERRORSTR) # 处理旧功能的GCH逻辑
function getPageGCHindex(FP_OBJNAME, FP_MULTIDISPLAY, PARA, FP_PARANUM) # 处理带索引的页面GCH逻辑
function Escapquotes(para) # 转义引号
function getXMLLabelStart(nodeName) # 获取XML标签开始部分
function getXMLLabelEnd(nodeName) # 获取XML标签结束部分
function getXMLNodeEntity(nodeName, nodeValue) # 获取XML节点实体
function getInstParaXML(OBJNAME, ID, PARA, PARANUM) # 获取实例参数的XML
function getSIXML(OBJNAME, PARA, PARANUM) # 获取单实例XML
function getMIAllInstXML(OBJNAME, PARA, PARANUM) # 获取所有多实例的XML
function getMIOneInstXML(OBJNAME, ID, PARA, PARANUM) # 获取单个多实例的XML
function getMIIDListXML(OBJNAME) # 获取多实例ID列表的XML
function outputXML(xmlStr) # 输出XML响应
function isExistParaName(FP_HANDLE, name) # 检查参数名是否存在
function gchAlertStr(strval) # 显示提示信息
function ModuleAction(MPrefixs, MNum, ERRORSTR) # 处理模块操作
function ModuleLogic(MHandle) # 模块逻辑处理
pageinfo_func.gch(页面基本信息相关函数)
pageinfo_func.gch 通过 global.gch 被大部分文件所引用,提供了一些页面基本信息相关的函数,例如:设备型号、语言等。
function getDevModeName() # 获取设备型号名称
function initEnvWanType(defaultTypeValue, defaultObjWancId) # 初始化环境中的WAN类型相关参数
function getDevLang() # 获取设备当前语言设置
function setDevLang() # 根据设备语言设置加载对应语言配置文件
function setWebSkinInfo() # 设置网页皮肤样式相关路径环境变量
function initOnceEnv() # 初始化环境变量(仅执行一次),包括系统状态、登录参数、设备信息等
function initEnv() # 初始化环境,调用initOnceEnv并补充设置语言和VoIP协议等信息
auth/api.gch 和 auth/impl.gch(验权API封装接口和实现函数)
auth/api.gch 和 auth/impl.gch 分别是验权相关API的封装接口和对应的函数实现文件。
包含了登录验证的实现函数 auth_login_impl,该函数严格限制了登录冲突情况(禁止同时登录等),并且仅使用用户名来判定登录权限,具体而言:
- 管理员是从配置文件读取ID为
IGD.AU1的用户名(该ID的用户名默认值是CMCCAdmin),如果登录用户名和该用户名相同则为管理员权限。 - 普通用户是代码写死的
user,如果登录用户名为user,则判定为普通用户权限。 - 注意:在判定登录权限时,完全不会考虑
Level字段(权限字段,管理员为:1,普通用户为:2),修改该字段来提权自然也没有效果。
function auth_sessid_impl () # 实现获取会话 ID,调用通用会话 ID 函数
function auth_get_status_impl (type) # 实现获取认证状态,调用通用状态获取函数
function auth_router_action_impl () # 处理认证相关路由动作,根据请求参数确定具体动作
function auth_chgpwd_impl (sess_id) # 实现密码修改功能,验证新密码一致性并更新
function auth_preempt_impl (sess_id) # 实现会话抢占功能,根据索引和会话 ID 进行抢占处理
function auth_check_ticket_impl (ticket) # 验证票据有效性,对比参考票据
function auth_login_impl (sess_id) # 实现登录功能,处理登录验证、会话管理、权限设置等
function admin_auth_logout_impl () # 实现管理员登出功能,清理管理员相关环境变量
function user_auth_logout_impl () # 实现用户登出功能,清理用户相关环境变量
function auth_check_impl (sess_id) # 实现会话验证功能,检查会话有效性及并发限制
function auth_login_impl(sess_id)
{
// 声明变量,用于存储句柄和超时时间相关参数
var FP_HANDLE, TimerLogout, adminTimerLogout, TimerLogoutUser, userTimerLogout;
// 获取管理员超时设置并处理
FP_HANDLE = create_paralist();
get_inst(FP_HANDLE, "OBJ_TIMERLOGOUT_ID", "");
TimerLogout = get_para(FP_HANDLE, "TimerLogout");
destroy_paralist(FP_HANDLE);
adminTimerLogout = integer(TimerLogout);
log_gch("==========adminTimerLogout:"+adminTimerLogout);
if(adminTimerLogout == 0)
{
unsetenv("IsAdminLogin"); // 超时为0时清除管理员登录标识
}
// 获取用户超时设置并处理
FP_HANDLE = create_paralist();
get_inst(FP_HANDLE, "OBJ_TIMERLOGOUT_USER_ID", "");
TimerLogoutUser = get_para(FP_HANDLE, "TimerLogoutUser");
destroy_paralist(FP_HANDLE);
userTimerLogout = integer(TimerLogoutUser);
log_gch("==========userTimerLogout:"+userTimerLogout);
if(userTimerLogout == 0)
{
unsetenv("IsUserLogin"); // 超时为0时清除用户登录标识
}
// 获取登录请求参数
var user = request("Username");
var pass = decrypt_nokey(request("logincode")); // 解密登录密码
// 获取网络地址和MAC地址信息
var currentipaddr = sess_id;
var adminlastipaddr = getenv("adminClientaddr");
var userlastipaddr = getenv("userClientaddr");
var currentmac = get_remote_mac();
var adminlastmac = getenv("adminClientmac");
var userlastmac = getenv("userClientmac");
// 获取登录状态标识
var Adminflag = getenv("IsAdminLogin");
var Userflag = getenv("IsUserLogin");
var flag = 0;
var sameip = 0;
// 获取登录错误相关参数
var err_login_num = auth_getenv("errLoginNum", 0);
var err_login_num_g = auth_getenv("errLoginNum_g", 0);
var err_login_max = auth_getenv("errLoginNumMax", 3);
var err_login_time = auth_getenv("errLoginTime", 0);
var err_login_num_g = auth_getenv("errLoginNum_g", 0);
var err_login_num_e = auth_getenv("errLoginNum_e", 0);
var err_login_time_g = auth_getenv("errLoginTime_g", 0);
var err_login_time_e = auth_getenv("errLoginTime_e", 0);
// 验证用户存在性并获取权限信息
var paras[5];
var isUserExist = getRightByUsername(user, pass, paras);
log_gch("*********************yinyx paras[1]**********:"+paras[1]);
// 根据用户类型检查登录错误次数限制
if(paras[1] == 1){ // 管理员类型
if (err_login_num_g >= err_login_max)
{
if ( err_login_time_g > 0 && (timestamp() - err_login_time_g) > 60 )
{
setenv("errLoginNum_g", 0); // 超过1分钟重置错误计数
}
else
{
return 202; // 错误次数超限
}
}
}
else if(paras[1] == 2){ // 普通用户类型
if (err_login_num >= err_login_max)
{
if ( err_login_time > 0 && (timestamp() - err_login_time) > 60 )
{
setenv("errLoginNum", 0); // 超过1分钟重置错误计数
}
else
{
return 202; // 错误次数超限
}
}
}
else { // 其他用户类型
if (err_login_num_e >= err_login_max)
{
if ( err_login_time_e > 0 && (timestamp() - err_login_time_e) > 60 )
{
setenv("errLoginNum_e", 0); // 超过1分钟重置错误计数
}
else
{
return 202; // 错误次数超限
}
}
log_gch("*********************yinyx else**********");
}
log_gch("*********************yinyx come in**********");
// 处理票据验证
var ticket = sess_id;
var ticket2 = ticket + ":" + request("Frm_Logintoken");
var login_allowed = 0;
var is_logined_before = 0;
// 检查是否为已登录用户
if (session_get("login_name") == user)
{
login_allowed = 1;
is_logined_before = 1;
}
else if (1) // 允许新登录
{
login_allowed = 1;
}
// 初始化错误代码
var err_code = 0;
log_gch("currentipaddr="+currentipaddr+", mac: "+currentmac);
log_gch("adminlastipaddr="+adminlastipaddr+", mac: "+adminlastmac);
log_gch("userlastipaddr="+userlastipaddr+", mac: "+userlastmac);
log_gch("user="+user);
var err_code = 0;
// 检查管理员登录冲突(MAC地址不一致)
if(Adminflag == 1 && user == "CMCCAdmin" )
{
if((currentmac != adminlastmac) && (adminlastmac != "N/A"))
{
log_gch("Admin chongfu********");
flag = 1;
err_code = 203; // 登录冲突错误
}
}
// 检查用户登录冲突(MAC地址不一致)
if(Userflag == 1 && user == "user" )
{
if((currentmac != userlastmac) && (userlastmac != "N/A"))
{
log_gch("User chongfu********");
flag = 1;
err_code = 203; // 登录冲突错误
}
}
// 检查管理员与用户同时登录冲突
if(Adminflag == 1 && user == "user" )
{
log_gch("Admin chongfu 1********");
flag = 1;
err_code = 203; // 登录冲突错误
}
// 检查用户与管理员同时登录冲突
if(Userflag == 1 && user == "CMCCAdmin" )
{
log_gch("User chongfu 1********");
flag = 1;
err_code = 203; // 登录冲突错误
}
// 检查登录权限
if (!login_allowed && flag == 0)
{
log_gch("login_allowed==0 && 201");
err_code = 201; // 无登录权限
}
// 处理登录冲突
if (flag == 1)
{
log_gch("203");
err_code = 203; // 登录冲突
}
else
{
// 检查并发登录限制
var logined_sess_count = auth_sess_count("logined");
var user_max = get_integer_config("user_max");
// 调用通用登录函数
err_code = auth_login_common(sess_id, user, pass);
// 验证票据并清除
if (ticket == getenv("auth/login_ticket"))
{
unsetenv("auth/login_ticket");
}
// 登录成功处理
if (err_code == 0)
{
// 更新登录单调计数器(首次登录)
if (is_logined_before == 0)
{
var token = integer(auth_getenv("auth/login_monotonic_id", 0));
setenv("auth/login_monotonic_id", token + 1);
}
// 处理会话抢占逻辑
if (auth_if_defined("login.MUS.Preempt"))
{
var logined_me_count = auth_sess_count("logined_me");
var logined_sess_count2 = auth_sess_count("logined");
if ( 1 < logined_me_count
|| (logined_sess_count2 > logined_sess_count && logined_sess_count2 > user_max) )
{
if (0 < auth_sess_count("preemptable"))
{
auth_set_status_common("login", "preempt"); // 标记需要抢占
}
else
{
session_destroy(sess_id); // 无法抢占时销毁会话
return 203; // 返回冲突错误
}
}
}
// 设置登录相关环境变量
setenv("login_name", session_get("login_name"));
setenv("login_pwd", session_get("login_pwd"));
setenv("Lastvisit", session_get("Lastvisit"));
// 获取管理员用户名并验证
FP_HANDLE = create_paralist();
get_inst(FP_HANDLE, "OBJ_USERINFO_ID", "IGD.AU1");
var AdminPass = get_para(FP_HANDLE, "Username");
log_gch("AdminPass============"+AdminPass);
destroy_paralist(FP_HANDLE);
// 管理员登录后环境变量设置
if(getenv("login_name")==AdminPass)
{
setenv("IsAdminLogin", 1);
setenv("Adminsession", 1);
setenv("Adminright", 1);
setenv("adminClientaddr", session_get("Clientaddr"));
setenv("adminClientmac", session_get("Clientmac"));
setenv("_SESSION_TOKEN", session_get("_SESSION_TOKEN"));
// 获取管理员超时恢复设置
var FP_HANDLE;
FP_HANDLE = create_paralist();
get_inst(FP_HANDLE, "OBJ_TIMER_RECOVER_ID", "");
var TimerLogout = get_para(FP_HANDLE, "TimerLogout");
destroy_paralist(FP_HANDLE);
}
// 普通用户登录后环境变量设置
if(getenv("login_name")=="user")
{
setenv("IsUserLogin", 1);
setenv("Usersession", 1);
setenv("Userright", 2);
setenv("userClientaddr", session_get("Clientaddr"));
setenv("userClientmac", session_get("Clientmac"));
setenv("_SESSION_TOKEN_USER", session_get("_SESSION_TOKEN_USER"));
// 获取用户超时恢复设置
var FP_HANDLE;
FP_HANDLE = create_paralist();
get_inst(FP_HANDLE, "OBJ_TIMER_RECOVER_USER_ID", "");
var TimerLogoutUser = get_para(FP_HANDLE, "TimerLogoutUser");
destroy_paralist(FP_HANDLE);
}
// 设置通用登录状态变量
setenv("Right", session_get("Right"));
setenv("IsLogin", session_get("IsLogin"));
setenv("weblogin", session_get("IsLogin"));
}
// 处理登录错误(非特定错误码)
if (err_code != 0 && err_code != 2004)
{
err_login_num = auth_login_err(); // 更新错误计数
if (err_login_num >= err_login_max)
{
return 202; // 错误次数超限
}
}
}
return err_code; // 返回最终错误代码
}
auth/common.gch(验权API的通用函数)
auth/common.gch 提供了验权的通用函数,提供给 auth/impl.gch 使用。
function auth_getenv(name, default_value) # 获取环境变量值,不存在时返回默认值
function get_integer_config(name) # 获取配置项并转换为整数
function auth_get_status_common(type) # 获取指定类型的认证状态
function auth_set_status_common(type, new_status) # 设置指定类型的认证状态
function auth_sess_count(type) # 根据类型统计会话数量(已登录、当前用户登录、可抢占等)
function auth_if_defined(config_name) # 检查配置项是否已定义
function auth_dochgpwd(username, password, chgpwd_flag) # 执行密码修改操作
function auth_preempt_common(from_sessid, to_sessid) # 处理会话抢占逻辑
function isUserNamePwdCorrect(username, userpwd) # 验证用户名和密码是否正确
function getRightByUsername(username, password, paras[]) # 根据用户名获取用户权限信息
function getParasByUsername(username, password, paras[]) # 根据用户名和密码获取用户详细参数
function getIdleSecs() # 获取设备空闲超时时间(秒)
function is_sess_id_unique(sess_id) # 检查会话ID是否唯一
function auth_sessid_common() # 获取通用会话ID(远程地址)
function auth_login_common(sess_id, user, pass) # 通用登录处理函数,验证并创建会话
function auth_logout_common() # 通用登出处理函数,销毁会话
function auth_check_common(sess_id) # 检查会话是否有效
function auth_login_err() # 处理登录错误,更新错误计数
function auth_sess_id(sessid) # 检查会话ID与密码是否匹配
附录
所参考光猫的信息
| 设备型号 | 硬件版本号 | 软件版本号 |
|---|---|---|
| H5-9 | HWVer-A590 | V32.AA1.04 |
| SK-D840N | V2.0 | V2.0.0 |
中兴zxic定制版 httpd 的完整字符串解析文件
更多推荐



所有评论(0)