系统信息概况

光猫的嵌入式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 页面:

  1. 特殊处理:xxx包含 &telnet.gch 直接跳转到 telnet.gch 页面;访问 start.ghtml 直接跳转到 frame.gch 页面,等等。
  2. 直接跳转:大部分情况下会直接跳转,只要xxx在case所列的选项中就会直接跳转到xxx页面,例如 top.gch、template.gch等。(跳转到xxx页面时,可能在验权等处理之后会再次跳回到登录页)
  3. 其他:返回 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.gchhidden_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" >&nbsp;&?ErrRep;</font>
        </div>
    </div>
    <div id="layer_err_content">
        <font id="errmsg" class="notecontent" >&nbsp;</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>&nbsp;&?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">&nbsp;</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; ' />&nbsp;&nbsp;&nbsp;"<%
        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; '>&nbsp;&nbsp;&nbsp;"+"<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'/>&nbsp;&nbsp;&nbsp;"<%
        break;
    case 2:
        // 状态2:显示提交/取消按钮
%>+"<input name='Submit' type='button' id='Btn_Submit' onclick=pageSubmit()  class='button' value=' &?btn005; ' />&nbsp;"+"<input name='Submit' type='button' id='Btn_Cancel' onclick=pageCancel()  class='button' value=' &?btn004; '>&nbsp;&nbsp;&nbsp;"<%
        break;
    case 7:
        // 状态7:显示返回按钮
%>+"<input name='Back' type='button' id='Btn_Back' onclick=pageBack()  class='button' value=' &?btn013; ' />&nbsp;"<%
        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; '>&nbsp;&nbsp;&nbsp;"<%
        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.gchauth/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 的完整字符串解析文件

https://download.csdn.net/download/zhiyuan411/91633043

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐