uhttpd作為一個簡單的web服務器,其代碼量并不多,而且組織結構比較清楚。和其它網絡服務器差不多,其main函數進行一些初始化(首先parse config-file,然后parse argv),然后進入一個循環,不斷地監聽,每當有一個客戶請求到達時,則對它進行處理。
1、uhttpd uci配置參數說明
/etc/init.d/uhttpd腳本會使用procd的方式啟動uhttpd進程,該工具后面可以帶很多參數,如下:
root@zihome:~# uhttpd -h
uhttpd: option requires an argument -- h
Usage: uhttpd -p [addr:]port -h docroot
-f Do not fork to background
-c file Configuration file, default is '/etc/httpd.conf'
-p [addr:]port Bind to specified address and port, multiple allowed
-s [addr:]port Like -p but provide HTTPS on this port
-C file ASN.1 server certificate file
-K file ASN.1 server private key file
-h directory Specify the document root, default is '.'
-E string Use given virtual URL as 404 error handler
-I string Use given filename as index for directories, multiple allowed
-S Do not follow symbolic links outside of the docroot
-D Do not allow directory listings, send 403 instead
-R Enable RFC1918 filter
-n count Maximum allowed number of concurrent script requests
-N count Maximum allowed number of concurrent connections
-l string URL prefix for Lua handler, default is '/lua'
-L file Lua handler script, omit to disable Lua
-u string URL prefix for UBUS via JSON-RPC handler
-U file Override ubus socket path
-a Do not authenticate JSON-RPC requests against UBUS session api
-X Enable CORS HTTP headers on JSON-RPC api
-x string URL prefix for CGI handler, default is '/cgi-bin'
-i .ext=path Use interpreter at path for files with the given extension
-t seconds CGI, Lua and UBUS script timeout in seconds, default is 60
-T seconds Network timeout in seconds, default is 30
-k seconds HTTP keepalive timeout
-d string URL decode given string
-r string Specify basic auth realm
-m string MD5 crypt given string
openwrt上面還是通過uci來配置uhttpd,配置文件如下:
config uhttpd 'main'
list listen_http '0.0.0.0:80'
list listen_http '[::]:80'
list listen_https '0.0.0.0:443'
list listen_https '[::]:443'
option home '/www'
option rfc1918_filter '1'
option max_requests '3'
option max_connections '10'
option cert '/etc/uhttpd.crt'
option key '/etc/uhttpd.key'
option cgi_prefix '/cgi-bin'
option script_timeout '120'
option network_timeout '60'
option http_keepalive '60'
option tcp_keepalive '5'
option no_ubusauth '0'
option ubus_prefix '/ubus'
uHTTPd 配置項含義:
名 稱 | 類 型 | 含 義 |
---|---|---|
listen_http | 字符串 | 定義服務器的 IP 和端口。指所監聽的非加密的地址和端口。如果僅給出端口號,將同時服務于IPv4和IPv6請求。使用0.0.0.0:80僅綁定在 IPv4 接口,使用[::]:80 僅綁定 IPv6 |
home | 目錄路徑 | 定義服務器的文檔根目錄 |
max_requests | 整型數字 | 最大的并行請求數,如果大于這個值,后續的請求將進入排隊隊列中 |
cert | 文件路徑 | 用于 HTTPS 連接的 ASN.1/DER 證書。在提供 HTTS 連接時必須提供 |
key | 文件路徑 | 用于 HTTPS 連接的 ASN.1/DER 私鑰。在提供 HTTPS 連接時必須提供 |
cgi_prefix | 字符串 | 定義 CGI 腳本的相對于根目錄的前綴。如果沒有該選項,CGI功能將不支持 |
script_timeout | 整型數字 | Lua 或CGI請求的最大等待時間秒值。如果沒有輸出產生,則超時后執行就結束了 |
network_timeout | 整型數字 | 網絡活動的最大等待時間,如果指定的秒數內沒有網絡活動發生,則程序終止,連接關閉 |
tcp_keepalive | 整型數字 | tcp 心跳檢測時間間隔,發現對端已不存在時則關閉連接。設置為 0 則關閉 tcp 心跳檢測 |
realm | 字符串 | 基本認證的域值,默認為主機名,是當客戶端進行基本認證的提示內容 |
config | 文件路徑 | 用于基本認證的配置文件 |
no_dirlists | 是否顯示文件列表 | 正常情況可以直接通過瀏覽器查看文件,不安全可以使用該字段屏蔽瀏覽器查看 |
max_requests | 最大請求數 | 同時處理的請求的最大數量。當這個數量被超過時,額外的請求將被放入隊列中等待 |
max_connections | 最大連接數 | 同時處理的最大TCP連接數量。當這個數量被超過時,額外的TCP連接嘗試將被放入隊列中等待 |
ubus_prefix | ubus前綴 | 使用JSON-RPC訪問,如http://192.168.18.1/ubus. |
ubus_noauth | session認證 | 關閉后可以不用認證,直接訪問http接口 |
/etc/init.d/uhttpd腳本在啟動的時候會去獲取/etc/config/uhttpd配置里面的信息然后進行啟動,我們現在用到的參數如下:
root@zihome:/# ps | grep uhttp
1395 root 1556 S /usr/sbin/uhttpd -f -h /www -r openwrt -x /cgi-bin -t 120 -T 60 -k 60 -A 5 -n 3 -N 10 -R -p 0.0.0.0:80 -p [::]:80
31572 root 1520 S grep uhttp
https://openwrt.org/docs/guide-user/services/webserver/uhttpd
2、uhttpd的登錄認證超時機制
登錄做為web登錄最基礎的功能,其背后是由rpcd提供的session模式實現的。
session模式實現了創建、更新、退出、添加acl權限等功能。
具體使用可以查看rpcd的介紹:
'session' @3602a25d
"create":{"timeout":"Integer"}
"list":{"ubus_rpc_session":"String"}
"grant":{"ubus_rpc_session":"String","scope":"String","objects":"Array"}
"revoke":{"ubus_rpc_session":"String","scope":"String","objects":"Array"}
"access":{"ubus_rpc_session":"String","scope":"String","object":"String","function":"String"}
"set":{"ubus_rpc_session":"String","values":"Table"}
"get":{"ubus_rpc_session":"String","keys":"Array"}
"unset":{"ubus_rpc_session":"String","keys":"Array"}
"destroy":{"ubus_rpc_session":"String"}
"login":{"username":"String","password":"String","timeout":"Integer"
3、ubus over http功能
在openwrt默認的web應用中,很主要的一共功能點,就是展示和設置信息。因為ubus -v list里面覆蓋了大部分我們需要的信息,所以如果可以通過一種簡單的rpc方案,通過http直接訪問ubus的內容那就方便了。
這就是ubus over http功能,web發送一個http請求,這個請求的結構根據ubus命令封裝之后,直接從ubus接口里面獲取內容返回給web頁面。
因為ubus接口里面包含了太多的信息,我們不想把所有信息都暴露給web,所以有了rpcd里面的acls權限管理。權限管理里面說明了只能執行那些命令。
使用該功能需要在uci里面把配置打開
option ubus_prefix /ubus
之后可以用postman發送post請求
登錄:
{
"jsonrpc":"2.0",
"id":1,
"method":"call",
"params":[
"00000000000000000000000000000000",
"session",
"login",
{
"username":"root",
"password":"admin"
}
]
}
此時會返回該賬號的session,還有權限之類的
{
"jsonrpc": "2.0",
"id": 1,
"result": [
0,
{
"ubus_rpc_session": "e5333f3fe2ed4601be3b0a8da0a6998e",
"timeout": 300,
"expires": 300,
"acls": {
"access-group": {
"luci-access": [
"read",
"write"
],
"luci-app-firewall": [
"read",
"write"
],
...
"uci-access": [
"read",
"write"
],
"unauthenticated": [
"read"
]
},
"cgi-io": {
"backup": [
"read"
],
"download": [
"read"
],
"exec": [
"read"
],
"upload": [
"write"
]
},
"file": {
"/": [
"list"
],
"/*": [
"list"
],
"/bin/dmesg -r": [
"exec"
],
...
"/usr/sbin/logread -e ^": [
"exec"
]
},
"ubus": {
"file": [
"list",
"read",
"stat",
"write",
"remove",
"exec"
],
"hostapd.*": [
"del_client"
],
"iwinfo": [
"assoclist",
"freqlist",
"txpowerlist",
"countrylist",
"scan"
],
"luci": [
"getFeatures",
"getConntrackList",
"getInitList",
"getLocaltime",
"getProcessList",
"getRealtimeStats",
"getTimezones",
"getLEDs",
"getUSBDevices",
"getSwconfigFeatures",
"getSwconfigPortState",
"getBlockDevices",
"getMountPoints",
"setInitAction",
"setLocaltime",
"setPassword",
"setBlockDetect",
"getConntrackHelpers"
],
"luci-rpc": [
"getBoardJSON",
"getDHCPLeases",
"getDSLStatus",
"getDUIDHints",
"getHostHints",
"getNetworkDevices",
"getWirelessDevices"
],
"network": [
"get_proto_handlers"
],
"network.interface": [
"dump"
],
"network.rrdns": [
"lookup"
],
"session": [
"access",
"login"
],
"system": [
"board",
"info",
"validate_firmware_image"
],
"uci": [
"changes",
"get",
"add",
"apply",
"confirm",
"delete",
"order",
"set",
"rename"
]
},
"uci": {
"*": [
"read",
"write"
],
...
"uhttpd": [
"read",
"write"
]
}
},
"data": {
"username": "root"
}
}
]
}
之后我們就可以使用這個session進行發送ubus請求,比如系統信息
{
"jsonrpc":"2.0",
"id":1,
"method":"call",
"params":[
"2cd182b0120310db8869b43d4340a307",
"system",
"board",
{
}
]
}
返回內容:
{
"jsonrpc": "2.0",
"id": 1,
"result": [
0,
{
"kernel": "4.14.275",
"hostname": "OpenWrt",
"system": "Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz",
"model": "VMware, Inc. VMware Virtual Platform",
"board_name": "vmware-inc-vmware-virtual-platform",
"release": {
"distribution": "OpenWrt",
"version": "19.07-SNAPSHOT",
"revision": "r11430-ecbbb37",
"target": "x86/64",
"description": "OpenWrt 19.07-SNAPSHOT r11430-ecbbb37"
}
}
]
}
可以看到返回的內容跟直接輸入ubus call一致
root@OpenWrt:~# ubus call system board
{
"kernel": "4.14.275",
"hostname": "OpenWrt",
"system": "Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz",
"model": "VMware, Inc. VMware Virtual Platform",
"board_name": "vmware-inc-vmware-virtual-platform",
"release": {
"distribution": "OpenWrt",
"version": "19.07-SNAPSHOT",
"revision": "r11430-ecbbb37",
"target": "x86/64",
"description": "OpenWrt 19.07-SNAPSHOT r11430-ecbbb37"
}
}
當然如果不屬于上面權限的ubus消息是無法發送的,比如
{
"jsonrpc":"2.0",
"id":1,
"method":"call",
"params":[
"2cd182b0120310db8869b43d4340a307",
"network.interface.lan",
"status",
{
}
]
}
返回沒有權限
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32002,
"message": "Access denied"
}
}
這些權限的配置都位于/usr/share/rpcd/acl.d/
目錄下面配置,我們可以自己按照規則增刪。
上面json請求的結構體描述如下:
{ "jsonrpc": "2.0",
"id": <unique-id-to-identify-request>,
"method": "call",
"params": [
<ubus_rpc_session>, <ubus_object>, <ubus_method>,
{ <ubus_arguments> }
]
}
{ "jsonrpc": "2.0", "id": 1, "method": "call", "params": [ "c1ed6c7b025d0caca723a816fa61b668", "file", "read", { "path": "/etc/board.json" } ] }
how to use ubus over http in openwrt 12.09
:https://www.cnblogs.com/nicephil/p/6768381.html#e4bdbfe794a8e696b9e6b395_2
openwrt-rpcd服務ACL配置錯誤風險分析:https://www.cnblogs.com/hac425/p/9416854.html
https://openwrt.org/docs/techref/ubus#access_to_ubus_over_http
4、cgi框架
要運行cgi程序,首先意味著需fork出一個子進程,并通過execl函數替換進程空間為cgi程序;其次,數據傳遞,子進程替換了進程空間后,怎么獲得原信息,有怎么把回饋數據傳輸給父進程(即uhttpd),父進程又怎么接收這些數據。
https://www.cnblogs.com/zmkeil/archive/2013/05/14/3078766.html