Merge Mainline

This commit is contained in:
AmadeusGhost
2020-07-05 21:36:02 +08:00
621 changed files with 111161 additions and 109540 deletions

View File

@@ -8,12 +8,12 @@ endif
LINUX_VERSION-4.9 = .229
LINUX_VERSION-4.14 = .187
LINUX_VERSION-4.19 = .130
LINUX_VERSION-4.19 = .131
LINUX_VERSION-5.4 = .50
LINUX_KERNEL_HASH-4.9.229 = 3256c2835fd95a1a739603e78b02d363eac2ce73a39fa19b13b32da4fc370fdc
LINUX_KERNEL_HASH-4.14.187 = 5b223475eaeea196aa7e127d3f253bca5c35d8afdc72ca75230ce1ecdd1454bd
LINUX_KERNEL_HASH-4.19.130 = a692c0e61dc885b4d6e66ae7bf202dadf7d5538fbf92766ce7cf8e227fd4f00f
LINUX_KERNEL_HASH-4.19.131 = 19dfb9f6cc4ba30104b65dcce7d78240a4ae188cb366747d5f8eae35e98964ba
LINUX_KERNEL_HASH-5.4.50 = ad10f4c1e900f4e3eb4903b65dbcb4ca74250de63aa9fa7105b9b3c3f9a8a6e2
remove_uri_prefix=$(subst git://,,$(subst http://,,$(subst https://,,$(1))))

View File

@@ -998,13 +998,18 @@ do_run_core()
core_type="Game"
fi
if [ "$proxy_mode" = "Script" ] || [ -n "$(grep "^ \{0,\}rule-providers:" "$CONFIG_FILE" 2>/dev/null)" ] || [ "$rule_source" = "ConnersHua_provider" ]; then
if [ "$proxy_mode" = "Script" ] || [ "$rule_source" = "ConnersHua_provider" ]; then
ln -s /etc/openclash/core/clash_tun /etc/openclash/clash 2>/dev/null
core_type="Tun"
fi
if [ -n "$(grep "^ \{0,\}rule-providers:" "$RULE_PROVIDER_FILE" 2>/dev/null)" ] && [ -n "$(grep "^ \{0,\}behavior" "$RULE_PROVIDER_FILE" 2>/dev/null |grep -v "^ \{0,\}#")" ]; then
ln -s /etc/openclash/core/clash_tun /etc/openclash/clash 2>/dev/null
core_type="Tun"
fi
if [ ! -f "/etc/openclash/clash" ] && [ -f "/etc/openclash/core/clash" ] && [ -z "$core_type" ]; then
ln -s /etc/openclash/core/clash /etc/openclash/clash 2>/dev/null
ln -s /etc/openclash/core/clash /etc/openclash/clash 2>/dev/null
fi
#权限检查

View File

@@ -346,7 +346,7 @@ o:depends("rule_source", "lhie1")
file:seek("set")
o = s:taboption("rules", ListValue, "AdBlock", translate("AdBlock"))
o:depends("rule_source", "lhie1")
o:depends("rule_source", "ConnersHua_provider")
--o:depends("rule_source", "ConnersHua_provider")
for l in file:lines() do
o:value(l)
end

View File

@@ -1 +1 @@
<!doctype html><html lang="en" dir="ltr"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><link rel="icon" type="image/x-icon" href="https://cdn.jsdelivr.net/gh/Dreamacro/clash/docs/logo.png"/><title>Clash</title><link href="main.592d9cc6ea35f4c8ca4d.css" rel="stylesheet"></head><body><div id="root"></div><script src="js/1.bundle.592d9cc6ea35f4c8ca4d.min.js"></script><script src="js/bundle.592d9cc6ea35f4c8ca4d.min.js"></script></body></html>
<!doctype html><html lang="en" dir="ltr"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><link rel="icon" type="image/x-icon" href="https://cdn.jsdelivr.net/gh/Dreamacro/clash/docs/logo.png"/><title>Clash</title><link href="main.0f871e89a0b8ff683256.css" rel="stylesheet"></head><body><div id="root"></div><script src="js/1.bundle.0f871e89a0b8ff683256.min.js"></script><script src="js/bundle.0f871e89a0b8ff683256.min.js"></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,28 +1,28 @@
/*! modern-normalize v0.6.0 | MIT License | https://github.com/sindresorhus/modern-normalize */*,:after,:before{box-sizing:border-box}:root{-moz-tab-size:4;tab-size:4}html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji}hr{height:0}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{padding:0}progress{vertical-align:initial}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}
._2MMSFnbhST{stroke-dasharray:890;stroke-dashoffset:890;animation:apL4DUAKgd 3s ease-in-out infinite normal forwards}@keyframes apL4DUAKgd{0%{stroke-dashoffset:890}to{stroke-dashoffset:0}}
._3oi0NFbeOm{opacity:.5;width:100%;height:100%;display:flex;justify-content:center;align-items:center}
._1rJPiLWN4s{position:fixed;top:0;bottom:0;left:0;right:0;overflow:hidden;padding:20px;background:var(--color-background);color:var(--color-text);text-align:center}._3h_IywJG1l{color:#2a477a;opacity:.6;display:flex;justify-content:center;padding:40px}.aXXDDfyTjE,._3h_IywJG1l{align-items:center}.aXXDDfyTjE{display:inline-flex;color:var(--color-text-secondary)}.aXXDDfyTjE:active,.aXXDDfyTjE:hover{color:#387cec}.aXXDDfyTjE svg{margin-right:5px}
._30oJwXNik9{background:var(--color-bg-sidebar);position:relative}._1SsCcpJvxN{display:block}._2r8EkOI78X{display:flex;align-items:center;justify-content:center;padding:25px 0 15px;color:#2a477a;transition:color .3s ease-in-out}@media (max-width:768px){._2r8EkOI78X{display:none}}._2r8EkOI78X:hover{animation:_2KRqAfqV8c .3s ease-in-out 0s infinite alternate}._2r8EkOI78X img{width:80px;height:80px}@keyframes _2KRqAfqV8c{0%{color:#2a477a}to{color:#1f52ac}}@media (max-width:768px){._2vUQ0Hs_C5{display:flex;justify-content:space-between;overflow:scroll}}._8mEn9Wlw1n{color:var(--color-text);text-decoration:none;display:flex;align-items:center;padding:6px 16px}@media screen and (min-width:30em){._8mEn9Wlw1n{padding:8px 20px}}@media (max-width:768px){._8mEn9Wlw1n{flex-direction:column}}._8mEn9Wlw1n svg{color:var(--color-icon);width:22px;height:22px}@media screen and (min-width:30em){._8mEn9Wlw1n svg{width:24px;height:24px}}._1WyHmd6t6y{background:var(--color-sb-active-row-bg)}@media (max-width:768px){._1WyHmd6t6y{background:none;border-bottom:2px solid #387cec}}._2eMIYGbP9O{padding-left:14px;font-size:.75em}@media (max-width:768px){._2eMIYGbP9O{padding-left:0;padding-top:5px}}@media screen and (min-width:30em){._2eMIYGbP9O{font-size:1em}}.nURY8qkFLS{--sz:40px;position:absolute;bottom:10px;left:50%;transform:translateX(-50%);width:var(--sz);height:var(--sz);display:flex;justify-content:center;align-items:center;color:var(--color-text);padding:5px;-webkit-appearance:none;-moz-appearance:none;appearance:none;outline:none;-webkit-user-select:none;-ms-user-select:none;user-select:none;background:none;cursor:pointer;border:1px solid transparent;border-radius:100%}@media (max-width:768px){.nURY8qkFLS{display:none}}.nURY8qkFLS:focus{border-color:var(--color-focus-blue)}
.SNYKRrv_2I{height:76px;display:flex;align-items:center}.VG1cD2OYvg{padding:0 15px;font-size:1.7em;text-align:left;margin:0}@media screen and (min-width:30em){.VG1cD2OYvg{padding:0 40px;font-size:2em}}
._37kQcxVR4T{color:var(--color-text);display:flex;align-items:center;flex-wrap:wrap}._37kQcxVR4T .sec{padding:10px;width:180px}._37kQcxVR4T .sec div:first-child{color:var(--color-text-secondary);font-size:.7em}._37kQcxVR4T .sec div:nth-child(2){padding:10px 0 0;font-size:1.8em}
._1EnK5MMInH{width:100%;height:100%;display:flex;justify-content:center;align-items:center}._39z9L5I2ao{--color1:#ddd;--size:40px;width:var(--size);height:var(--size);margin:10px;background-color:var(--color1);border-radius:100%;animation:_1DSWK2a-pe 1s ease-in-out infinite}@keyframes _1DSWK2a-pe{0%{transform:scale(0)}to{transform:scale(1);opacity:0}}
._2rN7aLQPCl{padding:6px 15px}@media screen and (min-width:30em){._2rN7aLQPCl{padding:10px 40px}}
._1u5AP7XMF9{padding:0 40px 5px}._2zeyKJDdFH{position:relative;height:40px}._3DQ7SXxKRA{position:absolute;top:50%;transform:translateY(-50%);left:0;width:100%}._1f-XUgRxH1{-webkit-appearance:none;background-color:var(--color-input-bg);background-image:none;border-radius:20px;border:1px solid var(--color-input-border);box-sizing:border-box;color:#c1c1c1;display:inline-block;font-size:inherit;height:40px;outline:none;padding:0 15px 0 35px;transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}._3PLtWxZwrd{position:absolute;top:50%;transform:translateY(-50%);left:10px}
._2tpN_G7FeO{display:flex;align-items:center;flex-wrap:wrap;font-size:.9em;padding:10px}._3wuPHKqO5W{color:#eee;flex-shrink:0;text-align:center;width:66px;background:green;border-radius:5px;padding:3px 5px;margin:0 8px}.IwiVCclCSC{flex-shrink:0;color:#999;font-size:14px}._3I1beKAMFt{flex-shrink:0;display:flex;font-family:Roboto Mono,Menlo,monospace;align-items:center;padding:8px 0;width:100%;white-space:pre;overflow:auto}._2MDNI6JESq{margin:0;padding:0;color:var(--color-text)}._2MDNI6JESq li,._2MDNI6JESq li.even{background:var(--color-background)}._3KX1sKJ1QD{padding:10px 40px}._19_8g6kTIV{display:flex;flex-direction:column;align-items:center;justify-content:center;color:#2d2d30}._19_8g6kTIV div:nth-child(2){color:var(--color-text-secondary);font-size:1.4em;opacity:.6}._3ljFcrWmBC{opacity:.3}
._3evbv-Ui87{-webkit-user-select:none;-ms-user-select:none;user-select:none;border:1px solid #525252;color:var(--color-text);background:var(--color-toggle-bg);display:flex;position:relative;outline:none}._3evbv-Ui87:focus{border-color:var(--color-focus-blue)}._3evbv-Ui87 input{position:absolute;left:0;opacity:0}._3evbv-Ui87 label{z-index:2;display:flex;align-items:center;justify-content:center;padding:10px 0;cursor:pointer}._1ok8KIb1RH{z-index:1;position:absolute;display:block;left:0;height:100%;transition:left .2s ease-out;background:var(--color-toggle-selected)}
._2S85tjFa1n{-webkit-appearance:none;background-color:var(--color-input-bg);background-image:none;border-radius:4px;border:1px solid var(--color-input-border);box-sizing:border-box;color:#c1c1c1;display:inline-block;font-size:inherit;height:40px;outline:none;padding:0 15px;transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}._2S85tjFa1n:focus{border-color:var(--color-focus-blue)}input::-webkit-inner-spin-button,input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}
h2._1p7G03ShKD{margin:0;font-size:1.3em}@media screen and (min-width:30em){h2._1p7G03ShKD{font-size:1.5em}}h2._1p7G03ShKD span:nth-child(2){font-size:12px;color:#777;font-weight:400;margin:0 .3em}body.light{--loading-dot-1-1:rgba(0,0,0,0.1);--loading-dot-1-2:rgba(0,0,0,0.5);--loading-dot-1-3:rgba(0,0,0,0.3);--loading-dot-2-1:rgba(0,0,0,0.3);--loading-dot-2-2:rgba(0,0,0,0.1);--loading-dot-2-3:rgba(0,0,0,0.5);--loading-dot-3-1:rgba(0,0,0,0.5);--loading-dot-3-2:rgba(0,0,0,0.3);--loading-dot-3-3:rgba(0,0,0,0.1)}body.dark{--loading-dot-1-1:hsla(0,0%,100%,0.5);--loading-dot-1-2:hsla(0,0%,100%,0.1);--loading-dot-1-3:hsla(0,0%,100%,0.3);--loading-dot-2-1:hsla(0,0%,100%,0.3);--loading-dot-2-2:hsla(0,0%,100%,0.5);--loading-dot-2-3:hsla(0,0%,100%,0.1);--loading-dot-3-1:hsla(0,0%,100%,0.1);--loading-dot-3-2:hsla(0,0%,100%,0.3);--loading-dot-3-3:hsla(0,0%,100%,0.5)}._1l_b31nvKC,._1l_b31nvKC:after,._1l_b31nvKC:before{display:inline-block;vertical-align:middle;width:6px;height:6px;border-radius:50%;font-size:0}._1l_b31nvKC{position:relative;background-color:var(--loading-dot-2-1);animation:AmeWPxQSDb 1s step-start infinite}._1l_b31nvKC:before{content:"";position:absolute;left:-12px;background-color:var(--loading-dot-1-1);animation:_1C49ms67Ai 1s step-start infinite}._1l_b31nvKC:after{content:"";position:absolute;right:-12px;background-color:var(--loading-dot-3-1);animation:p_etI2Ova8 1s step-start infinite}@keyframes _1C49ms67Ai{0%,to{background-color:var(--loading-dot-1-1)}33%{background-color:var(--loading-dot-1-2)}66%{background-color:var(--loading-dot-1-3)}}@keyframes AmeWPxQSDb{0%,to{background-color:var(--loading-dot-2-1)}33%{background-color:var(--loading-dot-2-2)}66%{background-color:var(--loading-dot-2-3)}}@keyframes p_etI2Ova8{0%,to{background-color:var(--loading-dot-3-1)}33%{background-color:var(--loading-dot-3-2)}66%{background-color:var(--loading-dot-3-3)}}
@font-face{font-family:Roboto Mono;font-style:normal;font-display:swap;font-weight:400;src:local("Roboto Mono Regular "),local("Roboto Mono-Regular"),url(roboto-mono-latin-400.woff2) format("woff2")}@font-face{font-family:Open Sans;font-style:normal;font-display:swap;font-weight:400;src:local("Open Sans Regular "),local("Open Sans-Regular"),url(open-sans-latin-400.woff2) format("woff2")}@font-face{font-family:Open Sans;font-style:normal;font-display:swap;font-weight:700;src:local("Open Sans Bold "),local("Open Sans-Bold"),url(open-sans-latin-700.woff2) format("woff2")}.border-bottom,.border-left,.border-top,.relative{position:relative}.border-top:before{top:0}.border-bottom:after,.border-top:before{position:absolute;content:"";height:1px;width:100%;transform:scaleY(.5) translateZ(0);left:0;right:0;background:#555}.border-bottom:after{bottom:0}.border-left:before{position:absolute;content:"";height:100%;width:1px;transform:scaleX(.5) translateZ(0);top:0;bottom:0;background:#555;left:0}*,:after,:before{box-sizing:border-box}:root{--font-mono:"Roboto Mono",Menlo,monospace;--font-normal:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,"PingFang SC","Microsoft YaHei","微软雅黑",Arial,sans-serif;--color-focus-blue:#1a73e8}body{font-family:Open Sans,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,PingFang SC,Microsoft YaHei,;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-text-size-adjust:100%;-webkit-font-smoothing:antialiased;margin:0;padding:0}body,body.dark{--color-background:#202020;--color-text:#ddd;--color-text-secondary:#ccc;--color-text-highlight:#fff;--color-bg-sidebar:#2d2d30;--color-sb-active-row-bg:#494b4e;--color-input-bg:#2d2d30;--color-input-border:#3f3f3f;--color-toggle-bg:#353535;--color-toggle-selected:#181818;--color-icon:#c7c7c7;--color-separator:#333;--color-btn-bg:#232323;--color-btn-fg:#bebebe;--color-bg-proxy:#303030;--color-row-odd:#282828;--bg-modal:#1f1f20;--bg-near-transparent:hsla(0,0%,100%,0.1);--select-border-color:#040404;--select-bg-hover:url("data:image/svg+xml;charset=utf-8,%3Csvg width='8' height='24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M4 7l3 4H1l3-4zM4 17l-3-4h6l-3 4z' fill='%23fff'/%3E%3C/svg%3E")}body.light{--color-background:#fbfbfb;--color-text:#222;--color-text-secondary:#646464;--color-text-highlight:#040404;--color-bg-sidebar:#e7e7e7;--color-sb-active-row-bg:#d0d0d0;--color-input-bg:#fff;--color-input-border:silver;--color-toggle-bg:#fff;--color-toggle-selected:#d7d7d7;--color-icon:#5b5b5b;--color-separator:#ccc;--color-btn-bg:#f4f4f4;--color-btn-fg:#101010;--color-bg-proxy:#e7e7e7;--color-row-odd:#f5f5f5;--bg-modal:#fbfbfb;--bg-near-transparent:rgba(0,0,0,0.1);--select-border-color:#999;--select-bg-hover:url("data:image/svg+xml;charset=utf-8,%3Csvg width='8' height='24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M4 7l3 4H1l3-4zM4 17l-3-4h6l-3 4z' fill='%23222'/%3E%3C/svg%3E")}.flexCenter{display:flex;align-items:center;justify-content:center}.fabgrp{position:fixed;z-index:3;right:20px;bottom:20px}
._3hz7LVhvUv:focus{outline:none}._3HF-KB9mgO{display:flex;justify-content:center;align-items:center}._3HF-KB9mgO ._3HzgPICn91{color:#2d2d30;opacity:.4;transition:opacity .4s}._3HF-KB9mgO ._3HzgPICn91:hover{opacity:.7}._1wpZuvoD5I{padding:30px 0 10px}.L7jTy-EFJ2{display:flex}.L7jTy-EFJ2 div{flex:1 1 auto}.L7jTy-EFJ2 div:nth-child(2){flex-grow:0;flex-basis:120px;margin-left:10px}._2fehqRU9GV{padding:30px 0 10px;display:flex;justify-content:flex-end;align-items:center}
._2A0HoxnDqc{-webkit-appearance:none;outline:none;-webkit-user-select:none;-ms-user-select:none;user-select:none;position:relative;display:inline-flex;align-items:center;justify-content:center;color:var(--color-btn-fg);background:var(--color-btn-bg);border:1px solid #555;border-radius:100px;font-size:.85em;padding:4px 7px}._2A0HoxnDqc:focus{border-color:var(--color-focus-blue)}._2A0HoxnDqc:hover{background:#387cec;border:1px solid #387cec;color:#fff}._2A0HoxnDqc:active{transform:scale(.97)}@media screen and (min-width:30em){._2A0HoxnDqc{font-size:1em;padding:6px 12px}}._2A0HoxnDqc.rBrOhcv1IU{border-color:transparent;background:none}._2A0HoxnDqc.rBrOhcv1IU:focus{border-color:var(--color-focus-blue)}._2A0HoxnDqc.rBrOhcv1IU:hover{color:#fff;background:#387cec;border:1px solid #387cec}.CtvjIaf7QB{margin-right:5px;display:inline-flex;align-items:center;justify-content:center}._2KAqQdptfT{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);display:inline-flex}
._2id19fefQX{display:flex;flex-wrap:wrap}._2QQQyNTKoG{flex-grow:0;margin-right:10px;margin-bottom:10px;cursor:pointer;border:2px solid transparent}.XJkW0wZSAx{border-color:#387cec}
._2OZZRrEL0J>div{min-width:345px}@media screen and (min-width:30em){._2OZZRrEL0J>div{width:360px}}._2OZZRrEL0J,.lF_ZoyIdZN{padding:6px 15px 15px}@media screen and (min-width:30em){._2OZZRrEL0J,.lF_ZoyIdZN{padding:10px 40px 40px}}.VduFBb2hWX{padding:0 15px}@media screen and (min-width:30em){.VduFBb2hWX{padding:0 40px}}.VduFBb2hWX>div{border-top:1px dashed #373737}._2NQoBOQcGA{padding:16px 0}
._73r9mFp69q{display:grid;grid-template-columns:repeat(11,minmax(-webkit-max-content,auto));grid-template-columns:repeat(11,minmax(max-content,auto))}.hEAcjybq1r{padding:8px 10px;height:50px;background:var(--color-background);position:-webkit-sticky;position:sticky;top:0;font-size:.8em;text-align:center;-webkit-user-select:none;-ms-user-select:none;user-select:none;display:flex;align-items:center;justify-content:space-between}.hEAcjybq1r:hover{color:var(--color-text-highlight)}._1x7JSEXzR8{padding:8px 13px;font-size:.9em;font-family:var(--font-normal)}._1x7JSEXzR8._3n5sGVMC-F{background:var(--color-row-odd)}._26SQDJZWya{text-align:right}._3LKH-WXUjR{display:inline-flex;margin-left:10px;width:16px;height:16px}._1CoVX1S_So{transform:rotate(180deg)}
h2._1p7G03ShKD{margin:0;font-size:1.3em}@media screen and (min-width:30em){h2._1p7G03ShKD{font-size:1.5em}}h2._1p7G03ShKD span:nth-child(2){font-size:12px;color:#777;font-weight:400;margin:0 .3em}body.light{--loading-dot-1-1:rgba(0,0,0,0.1);--loading-dot-1-2:rgba(0,0,0,0.5);--loading-dot-1-3:rgba(0,0,0,0.3);--loading-dot-2-1:rgba(0,0,0,0.3);--loading-dot-2-2:rgba(0,0,0,0.1);--loading-dot-2-3:rgba(0,0,0,0.5);--loading-dot-3-1:rgba(0,0,0,0.5);--loading-dot-3-2:rgba(0,0,0,0.3);--loading-dot-3-3:rgba(0,0,0,0.1)}body.dark{--loading-dot-1-1:hsla(0,0%,100%,0.5);--loading-dot-1-2:hsla(0,0%,100%,0.1);--loading-dot-1-3:hsla(0,0%,100%,0.3);--loading-dot-2-1:hsla(0,0%,100%,0.3);--loading-dot-2-2:hsla(0,0%,100%,0.5);--loading-dot-2-3:hsla(0,0%,100%,0.1);--loading-dot-3-1:hsla(0,0%,100%,0.1);--loading-dot-3-2:hsla(0,0%,100%,0.3);--loading-dot-3-3:hsla(0,0%,100%,0.5)}._1l_b31nvKC,._1l_b31nvKC:after,._1l_b31nvKC:before{display:inline-block;vertical-align:middle;width:6px;height:6px;border-radius:50%;font-size:0}._1l_b31nvKC{position:relative;background-color:var(--loading-dot-2-1);animation:AmeWPxQSDb 1s step-start infinite}._1l_b31nvKC:before{content:"";position:absolute;left:-12px;background-color:var(--loading-dot-1-1);animation:_1C49ms67Ai 1s step-start infinite}._1l_b31nvKC:after{content:"";position:absolute;right:-12px;background-color:var(--loading-dot-3-1);animation:p_etI2Ova8 1s step-start infinite}@keyframes _1C49ms67Ai{0%,to{background-color:var(--loading-dot-1-1)}33%{background-color:var(--loading-dot-1-2)}66%{background-color:var(--loading-dot-1-3)}}@keyframes AmeWPxQSDb{0%,to{background-color:var(--loading-dot-2-1)}33%{background-color:var(--loading-dot-2-2)}66%{background-color:var(--loading-dot-2-3)}}@keyframes p_etI2Ova8{0%,to{background-color:var(--loading-dot-3-1)}33%{background-color:var(--loading-dot-3-2)}66%{background-color:var(--loading-dot-3-3)}}
._1r-KsYFNaj{position:relative;padding:10px 0}._1r-KsYFNaj input{-webkit-appearance:none;background-color:initial;background-image:none;border:none;border-radius:0;border-bottom:1px solid var(--color-input-border);box-sizing:border-box;color:#c1c1c1;display:inline-block;font-size:inherit;height:40px;outline:none;padding:0 8px;transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}._1r-KsYFNaj input:focus{border-color:var(--color-focus-blue)}._1r-KsYFNaj label{position:absolute;left:8px;bottom:22px;transition:transform .15s ease-in-out;transform-origin:0 0}._1r-KsYFNaj input:focus+label,._1r-KsYFNaj label.Hn6h5kxOg7{transform:scale(.75) translateY(-25px)}._1r-KsYFNaj input:focus+label{color:var(--color-focus-blue)}
._2MMSFnbhST{stroke-dasharray:890;stroke-dashoffset:890;animation:apL4DUAKgd 3s ease-in-out infinite normal forwards}@keyframes apL4DUAKgd{0%{stroke-dashoffset:890}to{stroke-dashoffset:0}}
._3D3ZNp4oBz{background:none;position:fixed;top:0;bottom:0;left:0;right:0;transform:none;padding:0;border-radius:0;display:flex;justify-content:center}.tgH3yv-xGR{position:relative;top:10%;margin-left:20px;margin-right:20px}._3MMuzHtwZL{background:#222}
.ctrHyq7uir{position:fixed;top:0;right:0;left:0;bottom:0;background:#444;z-index:1024}._17mHpKiOUD{outline:none;position:relative;color:#ddd;top:50%;left:50%;transform:translate(-50%,-50%);background:#444;padding:20px;border-radius:10px}
._3R-iKwDVj-{background-color:rgba(0,0,0,.6)}._1vh9rFTHqn{background-color:var(--bg-modal);color:var(--color-text);max-width:300px;line-height:1.4;transform:translate(-50%,-50%) scale(1.2);opacity:.6;transition:all .3s ease}._3bTCBReMiZ{opacity:1;transform:translate(-50%,-50%) scale(1)}._1lwetyauPD{display:flex;align-items:center;justify-content:center;margin-top:30px}
.rtf{box-sizing:border-box;margin:25px;position:fixed;white-space:nowrap;z-index:9998;padding-left:0;list-style:none}.rtf.open .rtf--mb>*{transition:transform .2s ease-in-out}.rtf.open .rtf--mb{background:rgba(56,124,236,.92);box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12)}.rtf.open .rtf--mb>ul{list-style:none;margin:0;padding:0}.rtf.open .rtf--ab__c:hover>span,.rtf.open .rtf--ab__c>span.always-show{transition:opacity .2s ease-in-out;opacity:.9}.rtf.open .rtf--ab__c:first-child{transform:translateY(-60px) scale(1);transition-delay:.03s}.rtf.open .rtf--ab__c:first-child.top{transform:translateY(60px) scale(1)}.rtf.open .rtf--ab__c:nth-child(2){transform:translateY(-120px) scale(1);transition-delay:.09s}.rtf.open .rtf--ab__c:nth-child(2).top{transform:translateY(120px) scale(1)}.rtf.open .rtf--ab__c:nth-child(3){transform:translateY(-180px) scale(1);transition-delay:.12s}.rtf.open .rtf--ab__c:nth-child(3).top{transform:translateY(180px) scale(1)}.rtf.open .rtf--ab__c:nth-child(4){transform:translateY(-240px) scale(1);transition-delay:.15s}.rtf.open .rtf--ab__c:nth-child(4).top{transform:translateY(240px) scale(1)}.rtf.open .rtf--ab__c:nth-child(5){transform:translateY(-300px) scale(1);transition-delay:.18s}.rtf.open .rtf--ab__c:nth-child(5).top{transform:translateY(300px) scale(1)}.rtf.open .rtf--ab__c:nth-child(6){transform:translateY(-360px) scale(1);transition-delay:.21s}.rtf.open .rtf--ab__c:nth-child(6).top{transform:translateY(360px) scale(1)}.rtf--mb__c{padding:25px;margin:-25px}.rtf--mb__c :last-child{margin-bottom:0}.rtf--mb__c:hover>span,.rtf--mb__c>span.always-show{transition:opacity .2s ease-in-out;opacity:.9}.rtf--mb__c>span{opacity:0;transition:opacity .2s ease-in-out;position:absolute;top:50%;transform:translateY(-50%);margin-right:6px;margin-left:4px;background:rgba(0,0,0,.75);padding:2px 4px;border-radius:2px;color:#fff;font-size:13px;box-shadow:0 0 4px rgba(0,0,0,.14),0 4px 8px rgba(0,0,0,.28)}.rtf--mb__c>span.right{right:100%}.rtf--mb{height:48px;width:48px;z-index:9999;background:#387cec;display:inline-flex;justify-content:center;align-items:center;position:relative;border:none;border-radius:50%;box-shadow:0 0 4px rgba(0,0,0,.14),0 4px 8px rgba(0,0,0,.28);cursor:pointer;outline:none;padding:0;-webkit-user-drag:none;font-weight:700;color:#f1f1f1;font-size:18px}.rtf--ab__c,.rtf--mb>*{transition:transform .2s ease-in-out}.rtf--ab__c{display:block;position:absolute;top:0;right:1px;padding:10px 0;margin:-10px 0}.rtf--ab__c>span{opacity:0;transition:opacity .2s ease-in-out;position:absolute;top:50%;transform:translateY(-50%);margin-right:6px;background:rgba(0,0,0,.75);padding:2px 4px;border-radius:2px;color:#fff;font-size:13px;box-shadow:0 0 4px rgba(0,0,0,.14),0 4px 8px rgba(0,0,0,.28)}.rtf--ab__c>span.right{right:100%}.rtf--ab__c:first-child{transform:translateY(-60px) scale(0);transition-delay:.21s}.rtf--ab__c:first-child.top{transform:translateY(60px) scale(0)}.rtf--ab__c:nth-child(2){transform:translateY(-120px) scale(0);transition-delay:.18s}.rtf--ab__c:nth-child(2).top{transform:translateY(120px) scale(0)}.rtf--ab__c:nth-child(3){transform:translateY(-180px) scale(0);transition-delay:.15s}.rtf--ab__c:nth-child(3).top{transform:translateY(180px) scale(0)}.rtf--ab__c:nth-child(4){transform:translateY(-240px) scale(0);transition-delay:.12s}.rtf--ab__c:nth-child(4).top{transform:translateY(240px) scale(0)}.rtf--ab__c:nth-child(5){transform:translateY(-300px) scale(0);transition-delay:.09s}.rtf--ab__c:nth-child(5).top{transform:translateY(300px) scale(0)}.rtf--ab__c:nth-child(6){transform:translateY(-360px) scale(0);transition-delay:.03s}.rtf--ab__c:nth-child(6).top{transform:translateY(360px) scale(0)}.rtf--ab{height:40px;width:40px;margin-right:4px;background-color:#aaa;display:inline-flex;justify-content:center;align-items:center;position:relative;border:none;border-radius:50%;box-shadow:0 0 4px rgba(0,0,0,.14),0 4px 8px rgba(0,0,0,.28);cursor:pointer;outline:none;padding:0;-webkit-user-drag:none;font-weight:700;color:#f1f1f1;font-size:16px;z-index:10000}.rtf--ab:hover{background:#387cec;border:1px solid #387cec;color:#fff}.rtf--ab:focus{border-color:var(--color-focus-blue)}
._2OZZRrEL0J>div{min-width:345px}@media screen and (min-width:30em){._2OZZRrEL0J>div{width:360px}}._2OZZRrEL0J,.lF_ZoyIdZN{padding:6px 15px 15px}@media screen and (min-width:30em){._2OZZRrEL0J,.lF_ZoyIdZN{padding:10px 40px 40px}}.VduFBb2hWX{padding:0 15px}@media screen and (min-width:30em){.VduFBb2hWX{padding:0 40px}}.VduFBb2hWX>div{border-top:1px dashed #373737}._2NQoBOQcGA{padding:16px 0}
.SNYKRrv_2I{height:76px;display:flex;align-items:center}.VG1cD2OYvg{padding:0 15px;font-size:1.7em;text-align:left;margin:0}@media screen and (min-width:30em){.VG1cD2OYvg{padding:0 40px;font-size:2em}}
._2S85tjFa1n{-webkit-appearance:none;background-color:var(--color-input-bg);background-image:none;border-radius:4px;border:1px solid var(--color-input-border);box-sizing:border-box;color:#c1c1c1;display:inline-block;font-size:inherit;height:40px;outline:none;padding:0 15px;transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}._2S85tjFa1n:focus{border-color:var(--color-focus-blue)}input::-webkit-inner-spin-button,input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}
._2id19fefQX{display:flex;flex-wrap:wrap}._2QQQyNTKoG{flex-grow:0;margin-right:10px;margin-bottom:10px;cursor:pointer;border:2px solid transparent}.XJkW0wZSAx{border-color:#387cec}
._3evbv-Ui87{-webkit-user-select:none;-ms-user-select:none;user-select:none;border:1px solid #525252;color:var(--color-text);background:var(--color-toggle-bg);display:flex;position:relative;outline:none}._3evbv-Ui87:focus{border-color:var(--color-focus-blue)}._3evbv-Ui87 input{position:absolute;left:0;opacity:0}._3evbv-Ui87 label{z-index:2;display:flex;align-items:center;justify-content:center;padding:10px 0;cursor:pointer}._1ok8KIb1RH{z-index:1;position:absolute;display:block;left:0;height:100%;transition:left .2s ease-out;background:var(--color-toggle-selected)}
.react-tabs{-webkit-tap-highlight-color:transparent}.react-tabs__tab-list{margin:0 0 10px;padding:0 30px}.react-tabs__tab{display:inline-flex;align-items:center;border:1px solid transparent;border-radius:5px;bottom:-1px;position:relative;list-style:none;padding:6px 10px;cursor:pointer;font-size:1.2em;opacity:.5}.react-tabs__tab--selected{opacity:1}.react-tabs__tab--disabled{color:GrayText;cursor:default}.react-tabs__tab:focus{border-color:#0188fe;outline:none}.react-tabs__tab:focus:after{content:"";position:absolute}.react-tabs__tab-panel{display:none}.react-tabs__tab-panel--selected{display:block}
.duOnUwq-nI{height:100%;display:flex;color:var(--color-background);opacity:.1}.CCpULSE9Uh,.duOnUwq-nI{align-items:center;justify-content:center}.CCpULSE9Uh{font-family:var(--font-normal);font-size:.75em;margin-left:3px;padding:2px 7px;display:inline-flex;background-color:var(--bg-near-transparent);border-radius:30px}.peSK87gUob{margin:0 30px;width:100%;max-width:350px;justify-self:flex-end}._1nDSx5DASl{-webkit-appearance:none;background-color:var(--color-input-bg);background-image:none;border-radius:18px;border:1px solid var(--color-input-border);box-sizing:border-box;color:#c1c1c1;display:inline-block;font-size:inherit;height:36px;outline:none;padding:0 15px;transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}
._1r-KsYFNaj{position:relative;padding:10px 0}._1r-KsYFNaj input{-webkit-appearance:none;background-color:initial;background-image:none;border:none;border-radius:0;border-bottom:1px solid var(--color-input-border);box-sizing:border-box;color:#c1c1c1;display:inline-block;font-size:inherit;height:40px;outline:none;padding:0 8px;transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}._1r-KsYFNaj input:focus{border-color:var(--color-focus-blue)}._1r-KsYFNaj label{position:absolute;left:8px;bottom:22px;transition:transform .15s ease-in-out;transform-origin:0 0}._1r-KsYFNaj input:focus+label,._1r-KsYFNaj label.Hn6h5kxOg7{transform:scale(.75) translateY(-25px)}._1r-KsYFNaj input:focus+label{color:var(--color-focus-blue)}
._3hz7LVhvUv:focus{outline:none}._3HF-KB9mgO{display:flex;justify-content:center;align-items:center}._3HF-KB9mgO ._3HzgPICn91{color:#2d2d30;opacity:.4;transition:opacity .4s}._3HF-KB9mgO ._3HzgPICn91:hover{opacity:.7}._1wpZuvoD5I{padding:30px 0 10px}.L7jTy-EFJ2{display:flex}.L7jTy-EFJ2 div{flex:1 1 auto}.L7jTy-EFJ2 div:nth-child(2){flex-grow:0;flex-basis:120px;margin-left:10px}._2fehqRU9GV{padding:30px 0 10px;display:flex;justify-content:flex-end;align-items:center}
._3D3ZNp4oBz{background:none;position:fixed;top:0;bottom:0;left:0;right:0;transform:none;padding:0;border-radius:0;display:flex;justify-content:center}.tgH3yv-xGR{position:relative;top:10%;margin-left:20px;margin-right:20px}._3MMuzHtwZL{background:#222}
@font-face{font-family:Roboto Mono;font-style:normal;font-display:swap;font-weight:400;src:local("Roboto Mono Regular "),local("Roboto Mono-Regular"),url(roboto-mono-latin-400.woff2) format("woff2")}@font-face{font-family:Open Sans;font-style:normal;font-display:swap;font-weight:400;src:local("Open Sans Regular "),local("Open Sans-Regular"),url(open-sans-latin-400.woff2) format("woff2")}@font-face{font-family:Open Sans;font-style:normal;font-display:swap;font-weight:700;src:local("Open Sans Bold "),local("Open Sans-Bold"),url(open-sans-latin-700.woff2) format("woff2")}.border-bottom,.border-left,.border-top,.relative{position:relative}.border-top:before{top:0}.border-bottom:after,.border-top:before{position:absolute;content:"";height:1px;width:100%;transform:scaleY(.5) translateZ(0);left:0;right:0;background:#555}.border-bottom:after{bottom:0}.border-left:before{position:absolute;content:"";height:100%;width:1px;transform:scaleX(.5) translateZ(0);top:0;bottom:0;background:#555;left:0}*,:after,:before{box-sizing:border-box}:root{--font-mono:"Roboto Mono",Menlo,monospace;--font-normal:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,"PingFang SC","Microsoft YaHei","微软雅黑",Arial,sans-serif;--color-focus-blue:#1a73e8}body{font-family:Open Sans,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,PingFang SC,Microsoft YaHei,;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-text-size-adjust:100%;-webkit-font-smoothing:antialiased;margin:0;padding:0}body,body.dark{--color-background:#202020;--color-text:#ddd;--color-text-secondary:#ccc;--color-text-highlight:#fff;--color-bg-sidebar:#2d2d30;--color-sb-active-row-bg:#494b4e;--color-input-bg:#2d2d30;--color-input-border:#3f3f3f;--color-toggle-bg:#353535;--color-toggle-selected:#181818;--color-icon:#c7c7c7;--color-separator:#333;--color-btn-bg:#232323;--color-btn-fg:#bebebe;--color-bg-proxy:#303030;--color-row-odd:#282828;--bg-modal:#1f1f20;--bg-near-transparent:hsla(0,0%,100%,0.1);--select-border-color:#040404;--select-bg-hover:url("data:image/svg+xml;charset=utf-8,%3Csvg width='8' height='24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M4 7l3 4H1l3-4zM4 17l-3-4h6l-3 4z' fill='%23fff'/%3E%3C/svg%3E")}body.light{--color-background:#fbfbfb;--color-text:#222;--color-text-secondary:#646464;--color-text-highlight:#040404;--color-bg-sidebar:#e7e7e7;--color-sb-active-row-bg:#d0d0d0;--color-input-bg:#fff;--color-input-border:silver;--color-toggle-bg:#fff;--color-toggle-selected:#d7d7d7;--color-icon:#5b5b5b;--color-separator:#ccc;--color-btn-bg:#f4f4f4;--color-btn-fg:#101010;--color-bg-proxy:#e7e7e7;--color-row-odd:#f5f5f5;--bg-modal:#fbfbfb;--bg-near-transparent:rgba(0,0,0,0.1);--select-border-color:#999;--select-bg-hover:url("data:image/svg+xml;charset=utf-8,%3Csvg width='8' height='24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M4 7l3 4H1l3-4zM4 17l-3-4h6l-3 4z' fill='%23222'/%3E%3C/svg%3E")}.flexCenter{display:flex;align-items:center;justify-content:center}.fabgrp{position:fixed;z-index:3;right:20px;bottom:20px}
._73r9mFp69q{display:grid;grid-template-columns:repeat(11,minmax(-webkit-max-content,auto));grid-template-columns:repeat(11,minmax(max-content,auto))}.hEAcjybq1r{padding:8px 10px;height:50px;background:var(--color-background);position:-webkit-sticky;position:sticky;top:0;font-size:.8em;text-align:center;-webkit-user-select:none;-ms-user-select:none;user-select:none;display:flex;align-items:center;justify-content:space-between}.hEAcjybq1r:hover{color:var(--color-text-highlight)}._1x7JSEXzR8{padding:8px 13px;font-size:.9em;font-family:var(--font-normal)}._1x7JSEXzR8._3n5sGVMC-F{background:var(--color-row-odd)}._26SQDJZWya{text-align:right}._3LKH-WXUjR{display:inline-flex;margin-left:10px;width:16px;height:16px}._1CoVX1S_So{transform:rotate(180deg)}
._3R-iKwDVj-{background-color:rgba(0,0,0,.6)}._1vh9rFTHqn{background-color:var(--bg-modal);color:var(--color-text);max-width:300px;line-height:1.4;transform:translate(-50%,-50%) scale(1.2);opacity:.6;transition:all .3s ease}._3bTCBReMiZ{opacity:1;transform:translate(-50%,-50%) scale(1)}._1lwetyauPD{display:flex;align-items:center;justify-content:center;margin-top:30px}
.rtf{box-sizing:border-box;margin:25px;position:fixed;white-space:nowrap;z-index:9998;padding-left:0;list-style:none}.rtf.open .rtf--mb>*{transition:transform .2s ease-in-out}.rtf.open .rtf--mb{background:rgba(56,124,236,.92);box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12)}.rtf.open .rtf--mb>ul{list-style:none;margin:0;padding:0}.rtf.open .rtf--ab__c:hover>span,.rtf.open .rtf--ab__c>span.always-show{transition:opacity .2s ease-in-out;opacity:.9}.rtf.open .rtf--ab__c:first-child{transform:translateY(-60px) scale(1);transition-delay:.03s}.rtf.open .rtf--ab__c:first-child.top{transform:translateY(60px) scale(1)}.rtf.open .rtf--ab__c:nth-child(2){transform:translateY(-120px) scale(1);transition-delay:.09s}.rtf.open .rtf--ab__c:nth-child(2).top{transform:translateY(120px) scale(1)}.rtf.open .rtf--ab__c:nth-child(3){transform:translateY(-180px) scale(1);transition-delay:.12s}.rtf.open .rtf--ab__c:nth-child(3).top{transform:translateY(180px) scale(1)}.rtf.open .rtf--ab__c:nth-child(4){transform:translateY(-240px) scale(1);transition-delay:.15s}.rtf.open .rtf--ab__c:nth-child(4).top{transform:translateY(240px) scale(1)}.rtf.open .rtf--ab__c:nth-child(5){transform:translateY(-300px) scale(1);transition-delay:.18s}.rtf.open .rtf--ab__c:nth-child(5).top{transform:translateY(300px) scale(1)}.rtf.open .rtf--ab__c:nth-child(6){transform:translateY(-360px) scale(1);transition-delay:.21s}.rtf.open .rtf--ab__c:nth-child(6).top{transform:translateY(360px) scale(1)}.rtf--mb__c{padding:25px;margin:-25px}.rtf--mb__c :last-child{margin-bottom:0}.rtf--mb__c:hover>span,.rtf--mb__c>span.always-show{transition:opacity .2s ease-in-out;opacity:.9}.rtf--mb__c>span{opacity:0;transition:opacity .2s ease-in-out;position:absolute;top:50%;transform:translateY(-50%);margin-right:6px;margin-left:4px;background:rgba(0,0,0,.75);padding:2px 4px;border-radius:2px;color:#fff;font-size:13px;box-shadow:0 0 4px rgba(0,0,0,.14),0 4px 8px rgba(0,0,0,.28)}.rtf--mb__c>span.right{right:100%}.rtf--mb{height:48px;width:48px;z-index:9999;background:#387cec;display:inline-flex;justify-content:center;align-items:center;position:relative;border:none;border-radius:50%;box-shadow:0 0 4px rgba(0,0,0,.14),0 4px 8px rgba(0,0,0,.28);cursor:pointer;outline:none;padding:0;-webkit-user-drag:none;font-weight:700;color:#f1f1f1;font-size:18px}.rtf--ab__c,.rtf--mb>*{transition:transform .2s ease-in-out}.rtf--ab__c{display:block;position:absolute;top:0;right:1px;padding:10px 0;margin:-10px 0}.rtf--ab__c>span{opacity:0;transition:opacity .2s ease-in-out;position:absolute;top:50%;transform:translateY(-50%);margin-right:6px;background:rgba(0,0,0,.75);padding:2px 4px;border-radius:2px;color:#fff;font-size:13px;box-shadow:0 0 4px rgba(0,0,0,.14),0 4px 8px rgba(0,0,0,.28)}.rtf--ab__c>span.right{right:100%}.rtf--ab__c:first-child{transform:translateY(-60px) scale(0);transition-delay:.21s}.rtf--ab__c:first-child.top{transform:translateY(60px) scale(0)}.rtf--ab__c:nth-child(2){transform:translateY(-120px) scale(0);transition-delay:.18s}.rtf--ab__c:nth-child(2).top{transform:translateY(120px) scale(0)}.rtf--ab__c:nth-child(3){transform:translateY(-180px) scale(0);transition-delay:.15s}.rtf--ab__c:nth-child(3).top{transform:translateY(180px) scale(0)}.rtf--ab__c:nth-child(4){transform:translateY(-240px) scale(0);transition-delay:.12s}.rtf--ab__c:nth-child(4).top{transform:translateY(240px) scale(0)}.rtf--ab__c:nth-child(5){transform:translateY(-300px) scale(0);transition-delay:.09s}.rtf--ab__c:nth-child(5).top{transform:translateY(300px) scale(0)}.rtf--ab__c:nth-child(6){transform:translateY(-360px) scale(0);transition-delay:.03s}.rtf--ab__c:nth-child(6).top{transform:translateY(360px) scale(0)}.rtf--ab{height:40px;width:40px;margin-right:4px;background-color:#aaa;display:inline-flex;justify-content:center;align-items:center;position:relative;border:none;border-radius:50%;box-shadow:0 0 4px rgba(0,0,0,.14),0 4px 8px rgba(0,0,0,.28);cursor:pointer;outline:none;padding:0;-webkit-user-drag:none;font-weight:700;color:#f1f1f1;font-size:16px;z-index:10000}.rtf--ab:hover{background:#387cec;border:1px solid #387cec;color:#fff}.rtf--ab:focus{border-color:var(--color-focus-blue)}
._1rJPiLWN4s{position:fixed;top:0;bottom:0;left:0;right:0;overflow:hidden;padding:20px;background:var(--color-background);color:var(--color-text);text-align:center}._3h_IywJG1l{color:#2a477a;opacity:.6;display:flex;justify-content:center;padding:40px}.aXXDDfyTjE,._3h_IywJG1l{align-items:center}.aXXDDfyTjE{display:inline-flex;color:var(--color-text-secondary)}.aXXDDfyTjE:active,.aXXDDfyTjE:hover{color:#387cec}.aXXDDfyTjE svg{margin-right:5px}
._2rN7aLQPCl{padding:6px 15px}@media screen and (min-width:30em){._2rN7aLQPCl{padding:10px 40px}}
._1EnK5MMInH{width:100%;height:100%;display:flex;justify-content:center;align-items:center}._39z9L5I2ao{--color1:#ddd;--size:40px;width:var(--size);height:var(--size);margin:10px;background-color:var(--color1);border-radius:100%;animation:_1DSWK2a-pe 1s ease-in-out infinite}@keyframes _1DSWK2a-pe{0%{transform:scale(0)}to{transform:scale(1);opacity:0}}
._37kQcxVR4T{color:var(--color-text);display:flex;align-items:center;flex-wrap:wrap}._37kQcxVR4T .sec{padding:10px;width:180px}._37kQcxVR4T .sec div:first-child{color:var(--color-text-secondary);font-size:.7em}._37kQcxVR4T .sec div:nth-child(2){padding:10px 0 0;font-size:1.8em}
._3oi0NFbeOm{opacity:.5;width:100%;height:100%;display:flex;justify-content:center;align-items:center}
._2tpN_G7FeO{display:flex;align-items:center;flex-wrap:wrap;font-size:.9em;padding:10px}._3wuPHKqO5W{color:#eee;flex-shrink:0;text-align:center;width:66px;background:green;border-radius:5px;padding:3px 5px;margin:0 8px}.IwiVCclCSC{flex-shrink:0;color:#999;font-size:14px}._3I1beKAMFt{flex-shrink:0;display:flex;font-family:Roboto Mono,Menlo,monospace;align-items:center;padding:8px 0;width:100%;white-space:pre;overflow:auto}._2MDNI6JESq{margin:0;padding:0;color:var(--color-text)}._2MDNI6JESq li,._2MDNI6JESq li.even{background:var(--color-background)}._3KX1sKJ1QD{padding:10px 40px}._19_8g6kTIV{display:flex;flex-direction:column;align-items:center;justify-content:center;color:#2d2d30}._19_8g6kTIV div:nth-child(2){color:var(--color-text-secondary);font-size:1.4em;opacity:.6}._3ljFcrWmBC{opacity:.3}
._1u5AP7XMF9{padding:0 40px 5px}._2zeyKJDdFH{position:relative;height:40px}._3DQ7SXxKRA{position:absolute;top:50%;transform:translateY(-50%);left:0;width:100%}._1f-XUgRxH1{-webkit-appearance:none;background-color:var(--color-input-bg);background-image:none;border-radius:20px;border:1px solid var(--color-input-border);box-sizing:border-box;color:#c1c1c1;display:inline-block;font-size:inherit;height:40px;outline:none;padding:0 15px 0 35px;transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}._3PLtWxZwrd{position:absolute;top:50%;transform:translateY(-50%);left:10px}
._35EMVy62Je{display:flex;background:var(--color-background);color:var(--color-text);min-height:300px;height:100vh}@media (max-width:768px){._35EMVy62Je{flex-direction:column}}.AwL8oIubvP{flex-grow:1;overflow:auto}
._30oJwXNik9{background:var(--color-bg-sidebar);position:relative}._1SsCcpJvxN{display:block}._2r8EkOI78X{display:flex;align-items:center;justify-content:center;padding:25px 0 15px;color:#2a477a;transition:color .3s ease-in-out}@media (max-width:768px){._2r8EkOI78X{display:none}}._2r8EkOI78X:hover{animation:_2KRqAfqV8c .3s ease-in-out 0s infinite alternate}._2r8EkOI78X img{width:80px;height:80px}@keyframes _2KRqAfqV8c{0%{color:#2a477a}to{color:#1f52ac}}@media (max-width:768px){._2vUQ0Hs_C5{display:flex;justify-content:space-between;overflow:scroll}}._8mEn9Wlw1n{color:var(--color-text);text-decoration:none;display:flex;align-items:center;padding:6px 16px}@media screen and (min-width:30em){._8mEn9Wlw1n{padding:8px 20px}}@media (max-width:768px){._8mEn9Wlw1n{flex-direction:column}}._8mEn9Wlw1n svg{color:var(--color-icon);width:22px;height:22px}@media screen and (min-width:30em){._8mEn9Wlw1n svg{width:24px;height:24px}}._1WyHmd6t6y{background:var(--color-sb-active-row-bg)}@media (max-width:768px){._1WyHmd6t6y{background:none;border-bottom:2px solid #387cec}}._2eMIYGbP9O{padding-left:14px;font-size:.75em}@media (max-width:768px){._2eMIYGbP9O{padding-left:0;padding-top:5px}}@media screen and (min-width:30em){._2eMIYGbP9O{font-size:1em}}.nURY8qkFLS{--sz:40px;position:absolute;bottom:10px;left:50%;transform:translateX(-50%);width:var(--sz);height:var(--sz);display:flex;justify-content:center;align-items:center;color:var(--color-text);padding:5px;-webkit-appearance:none;-moz-appearance:none;appearance:none;outline:none;-webkit-user-select:none;-ms-user-select:none;user-select:none;background:none;cursor:pointer;border:1px solid transparent;border-radius:100%}@media (max-width:768px){.nURY8qkFLS{display:none}}.nURY8qkFLS:focus{border-color:var(--color-focus-blue)}

View File

@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="UTF-8"><link rel="shortcut icon" href="yacd.ico"><link rel="icon" type="image/png" sizes="64x64" href="yacd-64.png"><link rel="icon" type="image/png" sizes="128x128" href="yacd-128.png"><link rel="preload" href="/open-sans-latin-400.woff2" as="font"><link rel="preload" href="/open-sans-latin-700.woff2" as="font"><link rel="preload" href="/roboto-mono-latin-400.woff2" as="font"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="application-name" content="yacd"><meta name="description" content="Yet Another Clash Dashboard"><meta name="theme-color" content="#202020"><title>yacd - Yet Another Clash Dashboard</title><meta property="og:image" content="https://user-images.githubusercontent.com/1166872/47304841-536f3d80-d65a-11e8-8908-1917127dafc5.png"><meta property="og:site_name" content="yacd"><meta property="og:type" content="object"><meta property="og:title" content="yacd"><meta property="og:url" content="http://yacd.haishan.me"><meta property="og:description" content="Yet Another Clash Dashboard"><script defer="defer" src="runtime.37e8203ec536250e1e4c.js"></script><script defer="defer" src="core-js~app.f925e7b0d7db8cb56360.js"></script><script defer="defer" src="react~app.11ff7e45a0d5e0bfa30d.js"></script><script defer="defer" src="app.2ba50fd5acdb533fac49.js"></script><link href="app.8ff5510a4335f1c96a7c.css" rel="stylesheet"></head><body><div id="app"></div></body></html>
<!doctype html><html lang="en"><head><meta charset="UTF-8"><link rel="shortcut icon" href="yacd.ico"><link rel="icon" type="image/png" sizes="64x64" href="yacd-64.png"><link rel="icon" type="image/png" sizes="128x128" href="yacd-128.png"><link rel="preload" href="/open-sans-latin-400.woff2" as="font" type="font/woff2" crossorigin><link rel="preload" href="/open-sans-latin-700.woff2" as="font" type="font/woff2" crossorigin><link rel="preload" href="/roboto-mono-latin-400.woff2" as="font" type="font/woff2" crossorigin><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="application-name" content="yacd"><meta name="description" content="Yet Another Clash Dashboard"><meta name="theme-color" content="#202020"><title>yacd - Yet Another Clash Dashboard</title><meta property="og:image" content="https://user-images.githubusercontent.com/1166872/47304841-536f3d80-d65a-11e8-8908-1917127dafc5.png"><meta property="og:site_name" content="yacd"><meta property="og:type" content="object"><meta property="og:title" content="yacd"><meta property="og:url" content="http://yacd.haishan.me"><meta property="og:description" content="Yet Another Clash Dashboard"><script defer="defer" src="runtime.2f322c031bcb67b98af2.js"></script><script defer="defer" src="core-js~app.36c6f0077ac1e602b325.js"></script><script defer="defer" src="react~app.10618449a8e9b56a1845.js"></script><script defer="defer" src="app.09d100e5dacbf4f76606.js"></script><link href="app.b814d6460bc4d015f5f9.css" rel="stylesheet"></head><body><div id="app"></div></body></html>

View File

@@ -1,11 +1,12 @@
._37wt2_a2Gx{display:flex;align-items:center}._37wt2_a2Gx .L80zPM0Rx0{display:inline-flex;transform:rotate(0deg);transition:transform .3s}._37wt2_a2Gx .L80zPM0Rx0._36RO7_wtuv{transform:rotate(180deg)}._37wt2_a2Gx .L80zPM0Rx0:focus{outline:var(--color-focus-blue) solid 1px}._2XKN3NArpV{margin-left:5px}._1s98xvUoBx{font-family:var(--font-normal);font-size:.75em;margin-left:3px;padding:2px 7px;display:inline-flex;justify-content:center;align-items:center;background-color:var(--bg-near-transparent);border-radius:30px}
._2tHS8vcjWZ{border-radius:20px;color:#eee;font-size:.6em}@media screen and (min-width:30em){._2tHS8vcjWZ{font-size:1em}}
._1OypGPFF1I{margin:3px;padding:5px;position:relative;border-radius:8px;overflow:hidden;display:flex;flex-direction:column;justify-content:space-between;max-width:280px;background-color:var(--color-bg-proxy)}@media screen and (min-width:30em){._1OypGPFF1I{min-width:200px;border-radius:10px;padding:10px}}._1OypGPFF1I._25Vy0R3DBh{background-color:var(--color-focus-blue);color:#ddd}._1OypGPFF1I._20b8CxdJZg{opacity:.5}._1OypGPFF1I._2jncDUnzU1{transition:transform .2s ease-in-out;cursor:pointer}._1OypGPFF1I._2jncDUnzU1:hover{transform:translateY(-2px)}._3ILPKOrew2{font-family:var(--font-mono);font-size:.6em}@media screen and (min-width:30em){._3ILPKOrew2{font-size:1em}}._3DtTZsIYVP{display:flex;align-items:center;justify-content:space-between}._2dGvkUlaDx{width:100%;margin-bottom:5px;font-size:.85em}@media screen and (min-width:30em){._2dGvkUlaDx{font-size:1em}}._1fX-k2HS41{width:13px;height:13px;border-radius:50%;border:1px solid var(--color-background)}._1fX-k2HS41._25Vy0R3DBh{border-color:var(--color-text-secondary)}._1fX-k2HS41._2jncDUnzU1{transition:transform .1s ease-in-out;cursor:pointer}._1fX-k2HS41._2jncDUnzU1:hover{transform:scale(1.2)}
._1Zy60rJawW{display:flex;flex-wrap:wrap;margin:8px 0 8px -3px}._1YJnElh2Sr{margin:8px 0;display:grid;grid-template-columns:repeat(auto-fill,13px);grid-gap:10px}
.WqD74l0KeZ{margin-bottom:12px}._3u5R6urbB9{width:20px;height:20px;display:flex;align-items:center;justify-content:center}
._2OKIZuCJtW{background-color:rgba(0,0,0,.6)}._1y6NeshM4O{position:absolute;background-color:var(--bg-modal);color:var(--color-text);line-height:1.4;opacity:.6;transition:all .3s ease;transform:translate(-50%,-50%) scale(1.2);box-shadow:0 4px 4px rgba(0,0,0,.12),0 16px 32px rgba(0,0,0,.24)}._25KARE4UsT{opacity:1;transform:translate(-50%,-50%) scale(1)}
.W_0q0BiEOR{display:flex;justify-content:center;align-items:center}
._1vpeUMroNJ{position:-webkit-sticky;position:sticky;top:0;justify-content:space-between;flex-wrap:wrap;z-index:1;background:var(--color-background);background:linear-gradient(var(--color-background) 70%,transparent)}._1vpeUMroNJ,.pGNJbFaunZ{display:flex;align-items:center}.pGNJbFaunZ{flex-wrap:wrap;flex:1;justify-content:flex-end;margin-right:20px}._1aJVGJw_DC{max-width:350px;min-width:150px;flex:1;margin-right:8px}._2gOPGelc7s{padding:10px 15px}@media screen and (min-width:30em){._2gOPGelc7s{padding:10px 40px}}._3BayoLOhA0{position:relative;border-radius:50%;background:linear-gradient(60deg,#e66465,#9198e5)}._3BayoLOhA0:before{content:"";position:absolute;top:0;bottom:0;left:0;right:0;border:2px solid transparent;border-top-color:initial;border-radius:50%;animation:NKvZlK5zKz 1s linear infinite}@keyframes NKvZlK5zKz{0%{transform:rotate(0)}to{transform:rotate(1turn)}}
._37wt2_a2Gx{display:flex;align-items:center}._37wt2_a2Gx .L80zPM0Rx0{display:inline-flex;transform:rotate(0deg);transition:transform .3s}._37wt2_a2Gx .L80zPM0Rx0._36RO7_wtuv{transform:rotate(180deg)}._37wt2_a2Gx .L80zPM0Rx0:focus{outline:var(--color-focus-blue) solid 1px}._2XKN3NArpV{margin-left:5px}._1s98xvUoBx{font-family:var(--font-normal);font-size:.75em;margin-left:3px;padding:2px 7px;display:inline-flex;justify-content:center;align-items:center;background-color:var(--bg-near-transparent);border-radius:30px}
.WqD74l0KeZ{margin-bottom:12px}._3u5R6urbB9{width:20px;height:20px;display:flex;align-items:center;justify-content:center}
._1OypGPFF1I{margin:3px;padding:5px;position:relative;border-radius:8px;overflow:hidden;display:flex;flex-direction:column;justify-content:space-between;max-width:280px;background-color:var(--color-bg-proxy)}@media screen and (min-width:30em){._1OypGPFF1I{min-width:200px;border-radius:10px;padding:10px}}._1OypGPFF1I._25Vy0R3DBh{background-color:var(--color-focus-blue);color:#ddd}._1OypGPFF1I._20b8CxdJZg{opacity:.5}._1OypGPFF1I._2jncDUnzU1{transition:transform .2s ease-in-out;cursor:pointer}._1OypGPFF1I._2jncDUnzU1:hover{transform:translateY(-2px)}._3ILPKOrew2{font-family:var(--font-mono);font-size:.6em}@media screen and (min-width:30em){._3ILPKOrew2{font-size:1em}}._3DtTZsIYVP{display:flex;align-items:center;justify-content:space-between}._2dGvkUlaDx{width:100%;margin-bottom:5px;font-size:.85em}@media screen and (min-width:30em){._2dGvkUlaDx{font-size:1em}}._1fX-k2HS41{width:13px;height:13px;border-radius:50%;border:1px solid var(--color-background)}._1fX-k2HS41._25Vy0R3DBh{border-color:var(--color-text-secondary)}._1fX-k2HS41._2jncDUnzU1{transition:transform .1s ease-in-out;cursor:pointer}._1fX-k2HS41._2jncDUnzU1:hover{transform:scale(1.2)}
._2tHS8vcjWZ{border-radius:20px;color:#eee;font-size:.6em}@media screen and (min-width:30em){._2tHS8vcjWZ{font-size:1em}}
._1Zy60rJawW{display:flex;flex-wrap:wrap;margin:8px 0 8px -3px}._1YJnElh2Sr{margin:8px 0;display:grid;grid-template-columns:repeat(auto-fill,13px);grid-gap:10px}
._2PJqMIeTLt{margin-bottom:12px}._2PJqMIeTLt small{color:#777}._2bdge0dkRf{padding:10px 15px}@media screen and (min-width:30em){._2bdge0dkRf{padding:10px 40px}}.Vp69gn8Y28{display:flex}.Vp69gn8Y28 button{margin:0 5px}.Vp69gn8Y28 button:first-child{margin-left:0}._32jty5m2Ss{display:flex;justify-content:center;align-items:center;cursor:pointer}
.t6lsDKNXTK{height:30px;width:100%;padding-left:8px;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--color-background);color:var(--color-text);padding-right:20px;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='8' height='24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M4 7l3 4H1l3-4zM4 17l-3-4h6l-3 4z' fill='%23999'/%3E%3C/svg%3E");border-radius:4px;border-image:initial;border:1px solid var(--select-border-color);transition:all .1s ease 0s;background-position:calc(100% - 8px) 50%;background-repeat:no-repeat}.t6lsDKNXTK:focus,.t6lsDKNXTK:hover{border-color:#343434;outline:none!important;color:var(--color-text-highlight);background-image:var(--select-bg-hover)}.t6lsDKNXTK option{background-color:var(--color-background)}
._1h4Qixiwxj{max-width:85vw;width:400px;display:flex;justify-content:space-between;align-items:center;font-size:13px;padding:13px 0}hr{height:1px;background-color:var(--color-separator);border:none;outline:none;margin:1rem 0}
.W_0q0BiEOR{display:flex;justify-content:center;align-items:center}
._2PJqMIeTLt{margin-bottom:12px}._2PJqMIeTLt small{color:#777}._2bdge0dkRf{padding:10px 15px}@media screen and (min-width:30em){._2bdge0dkRf{padding:10px 40px}}.Vp69gn8Y28{display:flex}.Vp69gn8Y28 button{margin:0 5px}.Vp69gn8Y28 button:first-child{margin-left:0}._32jty5m2Ss{display:flex;justify-content:center;align-items:center;cursor:pointer}
._1vpeUMroNJ{position:-webkit-sticky;position:sticky;top:0;display:flex;align-items:center;justify-content:space-between;z-index:1;background:var(--color-background);background:linear-gradient(var(--color-background) 70%,transparent)}._2fxAc3OESk{margin-right:20px}._2gOPGelc7s{padding:10px 15px}@media screen and (min-width:30em){._2gOPGelc7s{padding:10px 40px}}._3BayoLOhA0{position:relative;border-radius:50%;background:linear-gradient(60deg,#e66465,#9198e5)}._3BayoLOhA0:before{content:"";position:absolute;top:0;bottom:0;left:0;right:0;border:2px solid transparent;border-top-color:initial;border-radius:50%;animation:NKvZlK5zKz 1s linear infinite}@keyframes NKvZlK5zKz{0%{transform:rotate(0)}to{transform:rotate(1turn)}}
._1LsRPWba72{-webkit-appearance:none;background-color:var(--color-input-bg);background-image:none;border-radius:20px;border:1px solid var(--color-input-border);box-sizing:border-box;color:#c1c1c1;display:inline-block;font-size:inherit;outline:none;padding:8px 15px;transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}._1LsRPWba72:focus{border:1px solid var(--color-focus-blue)}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
(window.webpackJsonp=window.webpackJsonp||[]).push([[4],{BVyM:function(e,t,a){"use strict";a.r(t);var r=a("ODXe"),n=a("q1tI"),i=a.n(n),c=a("iR1w"),o=a("OAQO"),l=a("9cvt"),u=a("WfPo"),s=a("OADI"),f=a("DKqX"),p=a("17x9"),d=a.n(p),m=a("xrux"),v=a.n(m),b={_default:"#59caf9",DIRECT:"#f5bc41",REJECT:"#cb3166"};function y(e){var t=e.type,a=e.payload,r=e.proxy,n=e.id,c=function(e){var t=e.proxy,a=b._default;return b[t]&&(a=b[t]),{color:a}}({proxy:r});return i.a.createElement("div",{className:v.a.rule},i.a.createElement("div",{className:v.a.left},n),i.a.createElement("div",null,i.a.createElement("div",{className:v.a.b},a),i.a.createElement("div",{className:v.a.a},i.a.createElement("div",{className:v.a.type},t),i.a.createElement("div",{style:c},r))))}y.propTypes={id:d.a.number,type:d.a.string,payload:d.a.string,proxy:d.a.string};var O=y,E=a("II4a"),h=a("EwbC"),w=Object(u.a)((function(e){return{searchText:Object(h.d)(e),updateSearchText:h.f}}))(E.a),g=a("Kv4h"),x=i.a.memo,j=i.a.useEffect,k=i.a.useMemo,C=i.a.useCallback;function N(e,t){return t[e].id}var I=x((function(e){var t=e.index,a=e.style,r=e.data[t];return i.a.createElement("div",{style:a},i.a.createElement(O,r))}),c.b);t.default=Object(u.a)((function(e){return{apiConfig:Object(s.c)(e),rules:Object(h.c)(e)}}))((function(e){var t=e.dispatch,a=e.apiConfig,n=e.rules,u=C((function(){t(Object(h.a)(a))}),[a,t]);j((function(){t(Object(h.b)(a))}),[t,a]);var s=Object(g.a)(),p=Object(r.a)(s,2),d=p[0],m=p[1],v=k((function(){return i.a.createElement(o.a,{width:16})}),[]);return i.a.createElement("div",null,i.a.createElement(f.a,{title:"Rules"}),i.a.createElement(w,null),i.a.createElement("div",{ref:d,style:{paddingBottom:30}},i.a.createElement(c.a,{height:m-30,width:"100%",itemCount:n.length,itemSize:80,itemData:n,itemKey:N},I)),i.a.createElement(l.b,{icon:v,text:"Refresh",onClick:u,position:l.c}))}))},OAQO:function(e,t,a){"use strict";var r=a("q1tI"),n=a.n(r),i=a("17x9"),c=a.n(i);function o(){return(o=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var a=arguments[t];for(var r in a)Object.prototype.hasOwnProperty.call(a,r)&&(e[r]=a[r])}return e}).apply(this,arguments)}function l(e,t){if(null==e)return{};var a,r,n=function(e,t){if(null==e)return{};var a,r,n={},i=Object.keys(e);for(r=0;r<i.length;r++)a=i[r],t.indexOf(a)>=0||(n[a]=e[a]);return n}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r<i.length;r++)a=i[r],t.indexOf(a)>=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(n[a]=e[a])}return n}var u=Object(r.forwardRef)((function(e,t){var a=e.color,r=void 0===a?"currentColor":a,i=e.size,c=void 0===i?24:i,u=l(e,["color","size"]);return n.a.createElement("svg",o({ref:t,xmlns:"http://www.w3.org/2000/svg",width:c,height:c,viewBox:"0 0 24 24",fill:"none",stroke:r,strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"},u),n.a.createElement("polyline",{points:"23 4 23 10 17 10"}),n.a.createElement("path",{d:"M20.49 15a9 9 0 1 1-2.12-9.36L23 10"}))}));u.propTypes={color:c.a.string,size:c.a.oneOfType([c.a.string,c.a.number])},u.displayName="RotateCw",t.a=u},xrux:function(e,t,a){e.exports={rule:"_3eSLieOhVX",left:"_2n1pW09UvV",a:"t1XJIwvW7A",b:"_1fNf8kj0HA",type:"_3yJmN0tON0"}}}]);
(window.webpackJsonp=window.webpackJsonp||[]).push([[4],{BVyM:function(e,t,a){"use strict";a.r(t);var r=a("ODXe"),n=a("q1tI"),i=a.n(n),c=a("OAQO"),o=a("iR1w"),l=a("Kv4h"),u=a("OADI"),s=a("EwbC"),f=a("DKqX"),p=a("17x9"),d=a.n(p),m=a("xrux"),v=a.n(m),b={_default:"#59caf9",DIRECT:"#f5bc41",REJECT:"#cb3166"};function y(e){var t=e.type,a=e.payload,r=e.proxy,n=e.id,c=function(e){var t=e.proxy,a=b._default;return b[t]&&(a=b[t]),{color:a}}({proxy:r});return i.a.createElement("div",{className:v.a.rule},i.a.createElement("div",{className:v.a.left},n),i.a.createElement("div",null,i.a.createElement("div",{className:v.a.b},a),i.a.createElement("div",{className:v.a.a},i.a.createElement("div",{className:v.a.type},t),i.a.createElement("div",{style:c},r))))}y.propTypes={id:d.a.number,type:d.a.string,payload:d.a.string,proxy:d.a.string};var O=y,E=a("II4a"),h=a("WfPo"),w=Object(h.a)((function(e){return{searchText:Object(s.d)(e),updateSearchText:s.f}}))(E.a),g=a("9cvt"),x=i.a.memo,j=i.a.useEffect,k=i.a.useMemo,C=i.a.useCallback;function N(e,t){return t[e].id}var I=x((function(e){var t=e.index,a=e.style,r=e.data[t];return i.a.createElement("div",{style:a},i.a.createElement(O,r))}),o.b);t.default=Object(h.a)((function(e){return{apiConfig:Object(u.c)(e),rules:Object(s.c)(e)}}))((function(e){var t=e.dispatch,a=e.apiConfig,n=e.rules,u=C((function(){t(Object(s.a)(a))}),[a,t]);j((function(){t(Object(s.b)(a))}),[t,a]);var p=Object(l.a)(),d=Object(r.a)(p,2),m=d[0],v=d[1],b=k((function(){return i.a.createElement(c.a,{width:16})}),[]);return i.a.createElement("div",null,i.a.createElement(f.a,{title:"Rules"}),i.a.createElement(w,null),i.a.createElement("div",{ref:m,style:{paddingBottom:30}},i.a.createElement(o.a,{height:v-30,width:"100%",itemCount:n.length,itemSize:80,itemData:n,itemKey:N},I)),i.a.createElement(g.b,{icon:b,text:"Refresh",onClick:u,position:g.c}))}))},OAQO:function(e,t,a){"use strict";var r=a("q1tI"),n=a.n(r),i=a("17x9"),c=a.n(i);function o(){return(o=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var a=arguments[t];for(var r in a)Object.prototype.hasOwnProperty.call(a,r)&&(e[r]=a[r])}return e}).apply(this,arguments)}function l(e,t){if(null==e)return{};var a,r,n=function(e,t){if(null==e)return{};var a,r,n={},i=Object.keys(e);for(r=0;r<i.length;r++)a=i[r],t.indexOf(a)>=0||(n[a]=e[a]);return n}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r<i.length;r++)a=i[r],t.indexOf(a)>=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(n[a]=e[a])}return n}var u=Object(r.forwardRef)((function(e,t){var a=e.color,r=void 0===a?"currentColor":a,i=e.size,c=void 0===i?24:i,u=l(e,["color","size"]);return n.a.createElement("svg",o({ref:t,xmlns:"http://www.w3.org/2000/svg",width:c,height:c,viewBox:"0 0 24 24",fill:"none",stroke:r,strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"},u),n.a.createElement("polyline",{points:"23 4 23 10 17 10"}),n.a.createElement("path",{d:"M20.49 15a9 9 0 1 1-2.12-9.36L23 10"}))}));u.propTypes={color:c.a.string,size:c.a.oneOfType([c.a.string,c.a.number])},u.displayName="RotateCw",t.a=u},xrux:function(e,t,a){e.exports={rule:"_3eSLieOhVX",left:"_2n1pW09UvV",a:"t1XJIwvW7A",b:"_1fNf8kj0HA",type:"_3yJmN0tON0"}}}]);

View File

@@ -1 +1 @@
!function(e){function t(t){for(var n,o,i=t[0],l=t[1],s=t[2],f=t[3]||[],d=0,h=[];d<i.length;d++)o=i[d],Object.prototype.hasOwnProperty.call(a,o)&&a[o]&&h.push(a[o][0]),a[o]=0;for(n in l)Object.prototype.hasOwnProperty.call(l,n)&&(e[n]=l[n]);for(p&&p(t),c.push.apply(c,f);h.length;)h.shift()();return u.push.apply(u,s||[]),r()}function r(){for(var e,t=0;t<u.length;t++){for(var r=u[t],n=!0,o=1;o<r.length;o++){var s=r[o];0!==a[s]&&(n=!1)}n&&(u.splice(t--,1),e=l(l.s=r[0]))}return 0===u.length&&(c.forEach((function(e){if(void 0===a[e]){a[e]=null;var t=document.createElement("link");l.nc&&t.setAttribute("nonce",l.nc),t.rel="prefetch",t.as="script",t.href=i(e),document.head.appendChild(t)}})),c.length=0),e}var n={},o={5:0},a={5:0},u=[],c=[];function i(e){return l.p+""+({2:"proxies",4:"rules",6:"vendors~chartjs",7:"vendors~proxies"}[e]||e)+"."+{2:"6a0a58d936ddbebbba53",4:"9bb11b1b25fa6817b033",6:"236a69375b65655cd7e6",7:"a3e2ad41949ba3aa37c4",8:"57c375f07ca41659ac12"}[e]+".js"}function l(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,l),r.l=!0,r.exports}l.e=function(e){var t=[];o[e]?t.push(o[e]):0!==o[e]&&{2:1,4:1}[e]&&t.push(o[e]=new Promise((function(t,r){for(var n=({2:"proxies",4:"rules",6:"vendors~chartjs",7:"vendors~proxies"}[e]||e)+"."+{2:"30780ebe14695af063e3",4:"664f8b737878361937cb",6:"31d6cfe0d16ae931b73c",7:"31d6cfe0d16ae931b73c",8:"31d6cfe0d16ae931b73c"}[e]+".css",a=l.p+n,u=document.getElementsByTagName("link"),c=0;c<u.length;c++){var i=(f=u[c]).getAttribute("data-href")||f.getAttribute("href");if("stylesheet"===f.rel&&(i===n||i===a))return t()}var s=document.getElementsByTagName("style");for(c=0;c<s.length;c++){var f;if((i=(f=s[c]).getAttribute("data-href"))===n||i===a)return t()}var d=document.createElement("link");d.rel="stylesheet",d.type="text/css",d.onload=t,d.onerror=function(t){var n=t&&t.target&&t.target.src||a,u=new Error("Loading CSS chunk "+e+" failed.\n("+n+")");u.code="CSS_CHUNK_LOAD_FAILED",u.request=n,delete o[e],d.parentNode.removeChild(d),r(u)},d.href=a,document.getElementsByTagName("head")[0].appendChild(d)})).then((function(){o[e]=0})));var r=a[e];if(0!==r)if(r)t.push(r[2]);else{var n=new Promise((function(t,n){r=a[e]=[t,n]}));t.push(r[2]=n);var u,c=document.createElement("script");c.charset="utf-8",c.timeout=120,l.nc&&c.setAttribute("nonce",l.nc),c.src=i(e);var s=new Error;u=function(t){c.onerror=c.onload=null,clearTimeout(f);var r=a[e];if(0!==r){if(r){var n=t&&("load"===t.type?"missing":t.type),o=t&&t.target&&t.target.src;s.message="Loading chunk "+e+" failed.\n("+n+": "+o+")",s.name="ChunkLoadError",s.type=n,s.request=o,r[1](s)}a[e]=void 0}};var f=setTimeout((function(){u({type:"timeout",target:c})}),12e4);c.onerror=c.onload=u,document.head.appendChild(c)}return Promise.all(t)},l.m=e,l.c=n,l.d=function(e,t,r){l.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,t){if(1&t&&(e=l(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(l.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)l.d(r,n,function(t){return e[t]}.bind(null,n));return r},l.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(t,"a",t),t},l.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},l.p="",l.oe=function(e){throw console.error(e),e};var s=window.webpackJsonp=window.webpackJsonp||[],f=s.push.bind(s);s.push=t,s=s.slice();for(var d=0;d<s.length;d++)t(s[d]);var p=f;r()}([]);
!function(e){function t(t){for(var n,o,i=t[0],l=t[1],d=t[2],f=t[3]||[],s=0,h=[];s<i.length;s++)o=i[s],Object.prototype.hasOwnProperty.call(a,o)&&a[o]&&h.push(a[o][0]),a[o]=0;for(n in l)Object.prototype.hasOwnProperty.call(l,n)&&(e[n]=l[n]);for(p&&p(t),c.push.apply(c,f);h.length;)h.shift()();return u.push.apply(u,d||[]),r()}function r(){for(var e,t=0;t<u.length;t++){for(var r=u[t],n=!0,o=1;o<r.length;o++){var d=r[o];0!==a[d]&&(n=!1)}n&&(u.splice(t--,1),e=l(l.s=r[0]))}return 0===u.length&&(c.forEach((function(e){if(void 0===a[e]){a[e]=null;var t=document.createElement("link");l.nc&&t.setAttribute("nonce",l.nc),t.rel="prefetch",t.as="script",t.href=i(e),document.head.appendChild(t)}})),c.length=0),e}var n={},o={5:0},a={5:0},u=[],c=[];function i(e){return l.p+""+({2:"proxies",4:"rules",6:"vendors~chartjs",7:"vendors~proxies"}[e]||e)+"."+{2:"77fef0018a8d1766e6eb",4:"efd650d64b4afda8d49b",6:"e4543f10556636d64b75",7:"34b4cb2526b2e8ed766c",8:"872bd111e9c497e7acca"}[e]+".js"}function l(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,l),r.l=!0,r.exports}l.e=function(e){var t=[];o[e]?t.push(o[e]):0!==o[e]&&{2:1,4:1}[e]&&t.push(o[e]=new Promise((function(t,r){for(var n=({2:"proxies",4:"rules",6:"vendors~chartjs",7:"vendors~proxies"}[e]||e)+"."+{2:"1e31d885e6fe278da534",4:"664f8b737878361937cb",6:"31d6cfe0d16ae931b73c",7:"31d6cfe0d16ae931b73c",8:"31d6cfe0d16ae931b73c"}[e]+".css",a=l.p+n,u=document.getElementsByTagName("link"),c=0;c<u.length;c++){var i=(f=u[c]).getAttribute("data-href")||f.getAttribute("href");if("stylesheet"===f.rel&&(i===n||i===a))return t()}var d=document.getElementsByTagName("style");for(c=0;c<d.length;c++){var f;if((i=(f=d[c]).getAttribute("data-href"))===n||i===a)return t()}var s=document.createElement("link");s.rel="stylesheet",s.type="text/css",s.onload=t,s.onerror=function(t){var n=t&&t.target&&t.target.src||a,u=new Error("Loading CSS chunk "+e+" failed.\n("+n+")");u.code="CSS_CHUNK_LOAD_FAILED",u.request=n,delete o[e],s.parentNode.removeChild(s),r(u)},s.href=a,document.getElementsByTagName("head")[0].appendChild(s)})).then((function(){o[e]=0})));var r=a[e];if(0!==r)if(r)t.push(r[2]);else{var n=new Promise((function(t,n){r=a[e]=[t,n]}));t.push(r[2]=n);var u,c=document.createElement("script");c.charset="utf-8",c.timeout=120,l.nc&&c.setAttribute("nonce",l.nc),c.src=i(e);var d=new Error;u=function(t){c.onerror=c.onload=null,clearTimeout(f);var r=a[e];if(0!==r){if(r){var n=t&&("load"===t.type?"missing":t.type),o=t&&t.target&&t.target.src;d.message="Loading chunk "+e+" failed.\n("+n+": "+o+")",d.name="ChunkLoadError",d.type=n,d.request=o,r[1](d)}a[e]=void 0}};var f=setTimeout((function(){u({type:"timeout",target:c})}),12e4);c.onerror=c.onload=u,document.head.appendChild(c)}return Promise.all(t)},l.m=e,l.c=n,l.d=function(e,t,r){l.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,t){if(1&t&&(e=l(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(l.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)l.d(r,n,function(t){return e[t]}.bind(null,n));return r},l.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(t,"a",t),t},l.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},l.p="",l.oe=function(e){throw console.error(e),e};var d=window.webpackJsonp=window.webpackJsonp||[],f=d.push.bind(d);d.push=t,d=d.slice();for(var s=0;s<d.length;s++)t(d[s]);var p=f;r()}([]);

View File

@@ -645,12 +645,6 @@ cat >> "$SERVER_FILE" <<-EOF
- Proxy
- DIRECT
- Domestic
- name: AdBlock
type: select
proxies:
- REJECT
- DIRECT
- Proxy
- name: AsianTV
type: select
proxies:

View File

@@ -106,7 +106,7 @@ if [ "$2" != 0 ]; then
if [ -z "$(grep "$GlobalTV" /tmp/Proxy_Group)" ]\
|| [ -z "$(grep "$AsianTV" /tmp/Proxy_Group)" ]\
|| [ -z "$(grep "$Proxy" /tmp/Proxy_Group)" ]\
|| [ -z "$(grep "$AdBlock" /tmp/Proxy_Group)" ]\
# || [ -z "$(grep "$AdBlock" /tmp/Proxy_Group)" ]\
|| [ -z "$(grep "$Others" /tmp/Proxy_Group)" ]\
|| [ -z "$(grep "$Domestic" /tmp/Proxy_Group)" ]; then
echo "${1} Warning: Because of The Different Porxy-Group's Name, Stop Setting The Other Rules!" >>/tmp/openclash.log
@@ -188,7 +188,7 @@ if [ "$2" != 0 ]; then
if [ "$GlobalTV" != "$GlobalTV_YAML" ]\
|| [ "$AsianTV" != "$AsianTV_YAML" ]\
|| [ "$Proxy" != "$Proxy_YAML" ]\
|| [ "$AdBlock" != "$AdBlock_YAML" ]\
# || [ "$AdBlock" != "$AdBlock_YAML" ]\
|| [ "$Others" != "$Others_YAML" ]\
|| [ "$Domestic" != "$Domestic_YAML" ]; then
check_def=1
@@ -298,9 +298,9 @@ if [ "$2" != 0 ]; then
sed -i "s/,CN,DIRECT$/,China,${Domestic}#d/g" "/tmp/other_rule.yaml" 2>/dev/null
sed -i "s/,CN,DIRECT,no-resolve$/,China,${Domestic},no-resolve#d/g" "/tmp/other_rule.yaml" 2>/dev/null
sed -i "/rules:/a\##Domestic:${Domestic}" "/tmp/other_rule.yaml" 2>/dev/null
sed -i "s/,Unbreak,DIRECT$/,Unbreak,${AdBlock}#d/g" "/tmp/other_rule.yaml" 2>/dev/null
sed -i "s/,Unbreak,DIRECT,no-resolve$/,Unbreak,${AdBlock},no-resolve#d/g" "/tmp/other_rule.yaml" 2>/dev/null
sed -i "/rules:/a\##Others:${AdBlock}" "/tmp/other_rule.yaml" 2>/dev/null
#sed -i "s/,Unbreak,DIRECT$/,Unbreak,${AdBlock}#d/g" "/tmp/other_rule.yaml" 2>/dev/null
#sed -i "s/,Unbreak,DIRECT,no-resolve$/,Unbreak,${AdBlock},no-resolve#d/g" "/tmp/other_rule.yaml" 2>/dev/null
#sed -i "/rules:/a\##AdBlock:${AdBlock}" "/tmp/other_rule.yaml" 2>/dev/null
sed -i "s/,MATCH$/,${Others}#d/g" "/tmp/other_rule.yaml" 2>/dev/null
sed -i "s/,MATCH,no-resolve$/,${Others},no-resolve#d/g" "/tmp/other_rule.yaml" 2>/dev/null
sed -i "/rules:/a\##Others:${Others}" "/tmp/other_rule.yaml" 2>/dev/null

View File

@@ -8,8 +8,8 @@ include $(TOPDIR)/rules.mk
LUCI_TITLE:=Argon Theme
LUCI_DEPENDS:=
PKG_VERSION:=1.5.2
PKG_RELEASE:=01-2020401
PKG_VERSION:=1.5.3
PKG_RELEASE:=01-20200703
include $(TOPDIR)/feeds/luci/luci.mk

View File

@@ -511,7 +511,7 @@ footer>a {
}
#maincontent>.container>p {
color: #fff;
color: #32325d;
}
li {
@@ -1500,7 +1500,7 @@ header {
header::after {
content: "";
position: absolute;
height: 14rem;
height: 2.4rem;
width: 100%;
background-color: #5e72e4 !important;
}
@@ -1946,7 +1946,7 @@ div>.table>.tbody>.tr:nth-of-type(2n) {
header>.fill {
padding: 0.8rem 0;
border-bottom: 1px solid rgba(255, 255, 255, .08) !important;
border-bottom: 0px solid rgba(255, 255, 255, .08) !important;
}
@@ -2035,9 +2035,13 @@ h1 {
h2 {
margin: 0 0 1rem 0;
font-size: 1.5rem;
font-weight:bold;
letter-spacing: 0.1rem;
padding: 1rem 0 0 0 !important;
color: #ffffff;
padding: 1rem 1.5rem;
color: #32325d;
border-radius: .375rem;
background: #fff;
box-shadow: 0 4px 8px rgba(0,0,0,.03);
}
#cbi-dropbear h2,
@@ -2304,8 +2308,7 @@ form.inline+form.inline,
.cbi-value-field .cbi-button-add,
.cbi-value-field .cbi-button-remove,
.cbi-value-field .cbi-button-neutral {
height: 2.5rem;
line-height: 2.5rem;
min-width: 2.5rem !important;
}
@@ -2317,6 +2320,7 @@ form.inline+form.inline,
padding-left: 1rem;
background-color: #FFFFFF;
border-radius: 0.375rem;
box-shadow: 0 4px 8px rgba(0,0,0,.03);
}
.cbi-tabmenu>li,
@@ -2626,7 +2630,7 @@ select[multiple="multiple"] {
box-shadow: 0 0 0rem 0 rgba(136, 152, 170, .15);
overflow-x: auto;
overflow-y: hidden;
padding: 0.5rem 0;
padding: 0rem 0;
}
#cbi-network-lan.cbi-section-node {
@@ -2649,11 +2653,11 @@ select[multiple="multiple"] {
.cbi-value-field,
.cbi-value-description {
display: table-cell;
line-height: 1.25;
line-height: 1.6;
font-size: 0.875rem;
}
div.cbi-value-field {}
.cbi-value-helpicon>img {
@@ -2717,8 +2721,8 @@ div.cbi-value-field {}
}
.cbi-map-descr {
margin: 0rem 0 1rem 0rem;
color: #fff;
margin: 0rem 0 1rem 1.5rem;
color: #32325d;
font-size: small;
}
@@ -3486,11 +3490,27 @@ header>.container>.pull-right>* {
font-size: 0.875rem;
}
.node-network-diagnostics .cbi-section div{
padding:0 1.5rem;
}
.node-network-diagnostics #diag-rc-output pre{
padding:1rem;
font-size: 0.875rem;
}
/* fix nlbw/display*/
#detail-bubble.in{
z-index: 500;
}
.node-nlbw-display .cbi-section ul{
padding:0.875rem 1.5rem;
}
@media screen and (max-width: 1600px) {
.main-left {
@@ -3924,6 +3944,10 @@ header>.container>.pull-right>* {
h2 {
color: #ccc;
background: #333333;
}
.cbi-map-descr {
color: #ccc;
}
.cbi-section {
@@ -4222,6 +4246,12 @@ header>.container>.pull-right>* {
background-color: #333333;
}
#detail-bubble > div {
border: 1px solid #ccc;
border-radius: 2px;
padding: 5px;
background: #252525;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 388 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

View File

@@ -10,7 +10,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=rclone-ng
PKG_VERSION:=0.3.2
PKG_VERSION:=0.4.0
PKG_RELEASE:=1
PKG_LICENSE:=GPLv3
@@ -21,7 +21,7 @@ include $(INCLUDE_DIR)/package.mk
PKG_SOURCE:=RcloneNg-v$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://github.com/ElonH/RcloneNg/releases/download/v$(PKG_VERSION)/
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
PKG_HASH:=441fdfa577bdda7f58f62f6e75174a4a48e44d7c1361f7123d9699d791c08b5a
PKG_HASH:=d974d5476b89281dcc14b0081e26f286041dd799898bc6b163d349d504056bd1
define Package/$(PKG_NAME)
SECTION:=net

View File

@@ -10,16 +10,16 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=rclone
PKG_VERSION:=1.52.1
PKG_VERSION:=1.52.2
PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/rclone/rclone.git
PKG_SOURCE_DATE:=2020-06-10
PKG_SOURCE_VERSION:=31dc78905ea8dfb22c43724527b19e097e05c26a
PKG_SOURCE_DATE:=2020-06-24
PKG_SOURCE_VERSION:=d8144a7e84dc3fb4975adf1bce707a5be672fada
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz
PKG_MIRROR_HASH:=fa79f62babc2f42691c9ad83ef906e90061d4ca6adcc97150c2906605312db66
PKG_MIRROR_HASH:=8a6f3fd465e57f5f3b9cfe3958eeb020f72b4940bfbf4993b9580659041c3735
PKG_LICENSE:=GPLv3
PKG_MAINTAINER:=ElonH <elonhhuang@gmail.com>

View File

@@ -8,12 +8,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=kcptun
PKG_VERSION:=20200409
PKG_VERSION:=20200701
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/xtaci/kcptun/tar.gz/v$(PKG_VERSION)?
PKG_HASH:=312b8f438549225dfd1eac95444dd6a4c50610578ddcf7ff21e19c73a855d4bc
PKG_HASH:=d5b2d212c6806f1c4eba5fbce8797734eaa8ae0f8cdd90dd06d0844392888ff0
PKG_LICENSE:=MIT
PKG_LICENSE_FILES:=LICENSE.md

View File

@@ -8,26 +8,25 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=json-c
PKG_VERSION:=0.13.1
PKG_RELEASE:=2
PKG_VERSION:=0.14
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-nodoc.tar.gz
PKG_SOURCE_URL:=https://s3.amazonaws.com/json-c_releases/releases/
PKG_HASH:=94a26340c0785fcff4f46ff38609cf84ebcd670df0c8efd75d039cc951d80132
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_SOURCE_SUBDIR)
PKG_HASH:=99914e644a25201d82ccefa20430f7515c110923360f9ef46755527c02412afa
PKG_LICENSE:=MIT
PKG_LICENSE_FILES:=COPYING
PKG_CPE_ID:=cpe:/a:json-c_project:json-c
PKG_FIXUP:=autoreconf
PKG_INSTALL:=1
CMAKE_INSTALL:=1
CMAKE_OPTIONS += -DCMAKE_INSTALL_INCLUDEDIR=$(STAGING_DIR)/usr/include
PKG_MAINTAINER:=Felix Fietkau <nbd@nbd.name>
include $(INCLUDE_DIR)/package.mk
include $(INCLUDE_DIR)/host-build.mk
include $(INCLUDE_DIR)/cmake.mk
TARGET_CFLAGS += $(FPIC) -Wno-implicit-fallthrough
HOST_CFLAGS += -Wno-implicit-fallthrough
@@ -38,22 +37,13 @@ define Package/libjson-c
CATEGORY:=Libraries
TITLE:=javascript object notation
URL:=https://json-c.github.io/json-c/
ABI_VERSION:=4
ABI_VERSION:=5
endef
define Package/libjson-c/description
This package contains a library for javascript object notation backends.
endef
define Build/InstallDev
$(INSTALL_DIR) $(1)/usr/include
$(CP) $(PKG_INSTALL_DIR)/usr/include/json-c $(1)/usr/include/
$(INSTALL_DIR) $(1)/usr/lib
$(CP) $(PKG_INSTALL_DIR)/usr/lib/libjson-c.{a,so*} $(1)/usr/lib/
$(INSTALL_DIR) $(1)/usr/lib/pkgconfig
$(CP) $(PKG_INSTALL_DIR)/usr/lib/pkgconfig/json-c.pc $(1)/usr/lib/pkgconfig/
endef
define Package/libjson-c/install
$(INSTALL_DIR) $(1)/usr/lib
$(CP) $(PKG_INSTALL_DIR)/usr/lib/libjson-c.so.* $(1)/usr/lib/

View File

@@ -1,18 +1,3 @@
--- a/configure.ac
+++ b/configure.ac
@@ -76,12 +76,6 @@ AC_FUNC_VPRINTF
AC_FUNC_MEMCMP
AC_CHECK_FUNCS([realloc])
AC_CHECK_FUNCS(strcasecmp strdup strerror snprintf vsnprintf vasprintf open vsyslog strncasecmp setlocale)
-AC_CHECK_DECLS([INFINITY], [], [], [[#include <math.h>]])
-AC_CHECK_DECLS([nan], [], [], [[#include <math.h>]])
-AC_CHECK_DECLS([isnan], [], [], [[#include <math.h>]])
-AC_CHECK_DECLS([isinf], [], [], [[#include <math.h>]])
-AC_CHECK_DECLS([_isnan], [], [], [[#include <float.h>]])
-AC_CHECK_DECLS([_finite], [], [], [[#include <float.h>]])
AC_MSG_CHECKING(for GCC atomic builtins)
AC_LINK_IFELSE(
[
--- a/math_compat.h
+++ b/math_compat.h
@@ -6,31 +6,9 @@
@@ -22,17 +7,17 @@
-/* Define isnan, isinf, infinity and nan on Windows/MSVC */
-
-#ifndef HAVE_DECL_ISNAN
-# ifdef HAVE_DECL__ISNAN
-#ifdef HAVE_DECL__ISNAN
-#include <float.h>
-#define isnan(x) _isnan(x)
-# endif
-#endif
-#endif
-
-#ifndef HAVE_DECL_ISINF
-# ifdef HAVE_DECL__FINITE
-#ifdef HAVE_DECL__FINITE
-#include <float.h>
-#define isinf(x) (!_finite(x))
-# endif
-#endif
-#endif
-
-#ifndef HAVE_DECL_INFINITY

File diff suppressed because one or more lines are too long

View File

@@ -1,27 +0,0 @@
From 099016b7e8d70a6d5dd814e788bba08d33d48426 Mon Sep 17 00:00:00 2001
From: Tobias Stoeckmann <tobias@stoeckmann.org>
Date: Mon, 4 May 2020 19:41:16 +0200
Subject: [PATCH 1/2] Protect array_list_del_idx against size_t overflow.
If the assignment of stop overflows due to idx and count being
larger than SIZE_T_MAX in sum, out of boundary access could happen.
It takes invalid usage of this function for this to happen, but
I decided to add this check so array_list_del_idx is as safe against
bad usage as the other arraylist functions.
---
arraylist.c | 3 +++
1 file changed, 3 insertions(+)
--- a/arraylist.c
+++ b/arraylist.c
@@ -135,6 +135,9 @@ array_list_del_idx( struct array_list *a
{
size_t i, stop;
+ /* Avoid overflow in calculation with large indices. */
+ if (idx > SIZE_T_MAX - count)
+ return -1;
stop = idx + count;
if ( idx >= arr->length || stop > arr->length ) return -1;
for ( i = idx; i < stop; ++i ) {

View File

@@ -1,32 +0,0 @@
From 77d935b7ae7871a1940cd827e850e6063044ec45 Mon Sep 17 00:00:00 2001
From: Tobias Stoeckmann <tobias@stoeckmann.org>
Date: Mon, 4 May 2020 19:46:45 +0200
Subject: [PATCH 2/2] Prevent division by zero in linkhash.
If a linkhash with a size of zero is created, then modulo operations
are prone to division by zero operations.
Purely protective measure against bad usage.
---
linkhash.c | 3 +++
1 file changed, 3 insertions(+)
--- a/linkhash.c
+++ b/linkhash.c
@@ -12,6 +12,7 @@
#include "config.h"
+#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
@@ -498,6 +499,8 @@ struct lh_table* lh_table_new(int size,
int i;
struct lh_table *t;
+ /* Allocate space for elements to avoid divisions by zero. */
+ assert(size > 0);
t = (struct lh_table*)calloc(1, sizeof(struct lh_table));
if (!t)
return NULL;

View File

@@ -1,86 +0,0 @@
From d07b91014986900a3a75f306d302e13e005e9d67 Mon Sep 17 00:00:00 2001
From: Tobias Stoeckmann <tobias@stoeckmann.org>
Date: Mon, 4 May 2020 19:47:25 +0200
Subject: [PATCH] Fix integer overflows.
The data structures linkhash and printbuf are limited to 2 GB in size
due to a signed integer being used to track their current size.
If too much data is added, then size variable can overflow, which is
an undefined behaviour in C programming language.
Assuming that a signed int overflow just leads to a negative value,
like it happens on many sytems (Linux i686/amd64 with gcc), then
printbuf is vulnerable to an out of boundary write on 64 bit systems.
---
linkhash.c | 7 +++++--
printbuf.c | 19 ++++++++++++++++---
2 files changed, 21 insertions(+), 5 deletions(-)
--- a/linkhash.c
+++ b/linkhash.c
@@ -579,9 +579,12 @@ int lh_table_insert_w_hash(struct lh_tab
{
unsigned long n;
- if (t->count >= t->size * LH_LOAD_FACTOR)
- if (lh_table_resize(t, t->size * 2) != 0)
+ if (t->count >= t->size * LH_LOAD_FACTOR) {
+ /* Avoid signed integer overflow with large tables. */
+ int new_size = INT_MAX / 2 < t->size ? t->size * 2 : INT_MAX;
+ if (t->size == INT_MAX || lh_table_resize(t, new_size) != 0)
return -1;
+ }
n = h % t->size;
--- a/printbuf.c
+++ b/printbuf.c
@@ -15,6 +15,7 @@
#include "config.h"
+#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -65,9 +66,16 @@ static int printbuf_extend(struct printb
if (p->size >= min_size)
return 0;
- new_size = p->size * 2;
- if (new_size < min_size + 8)
- new_size = min_size + 8;
+ /* Prevent signed integer overflows with large buffers. */
+ if (min_size > INT_MAX - 8)
+ return -1;
+ if (p->size > INT_MAX / 2)
+ new_size = min_size + 8;
+ else {
+ new_size = p->size * 2;
+ if (new_size < min_size + 8)
+ new_size = min_size + 8;
+ }
#ifdef PRINTBUF_DEBUG
MC_DEBUG("printbuf_memappend: realloc "
"bpos=%d min_size=%d old_size=%d new_size=%d\n",
@@ -82,6 +90,9 @@ static int printbuf_extend(struct printb
int printbuf_memappend(struct printbuf *p, const char *buf, int size)
{
+ /* Prevent signed integer overflows with large buffers. */
+ if (size > INT_MAX - p->bpos - 1)
+ return -1;
if (p->size <= p->bpos + size + 1) {
if (printbuf_extend(p, p->bpos + size + 1) < 0)
return -1;
@@ -98,6 +109,9 @@ int printbuf_memset(struct printbuf *pb,
if (offset == -1)
offset = pb->bpos;
+ /* Prevent signed integer overflows with large buffers. */
+ if (len > INT_MAX - offset)
+ return -1;
size_needed = offset + len;
if (pb->size < size_needed)
{

View File

@@ -1,29 +0,0 @@
From 519dfe1591d85432986f9762d41d1a883198c157 Mon Sep 17 00:00:00 2001
From: Eric Haszlakiewicz <erh+git@nimenees.com>
Date: Sun, 10 May 2020 03:32:19 +0000
Subject: [PATCH] Issue #599: Fix the backwards check in
lh_table_insert_w_hash() that was preventing adding more than 11 objects. Add
a test to check for this too.
---
linkhash.c | 2 +-
tests/test4.c | 29 +++++++++++++++++++++++++++++
tests/test4.expected | 1 +
3 files changed, 31 insertions(+), 1 deletion(-)
diff --git a/linkhash.c b/linkhash.c
index 51e90b1..f930efd 100644
--- a/linkhash.c
+++ b/linkhash.c
@@ -582,7 +582,7 @@ int lh_table_insert_w_hash(struct lh_table *t, const void *k, const void *v, con
if (t->count >= t->size * LH_LOAD_FACTOR) {
/* Avoid signed integer overflow with large tables. */
- int new_size = INT_MAX / 2 < t->size ? t->size * 2 : INT_MAX;
+ int new_size = (t->size > INT_MAX / 2) ? INT_MAX : (t->size * 2);
if (t->size == INT_MAX || lh_table_resize(t, new_size) != 0)
return -1;
}
--
2.26.2

View File

@@ -1,13 +1,13 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=libubox
PKG_RELEASE=5
PKG_RELEASE=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL=$(PROJECT_GIT)/project/libubox.git
PKG_SOURCE_DATE:=2018-07-25
PKG_SOURCE_VERSION:=c83a84afbef2b24f960ddeda0b5e2ab01fba6981
PKG_MIRROR_HASH:=4a9594d2ae3706174d182a21fe815f1d18c20beca6593707cc757994975dc670
PKG_MIRROR_HASH:=3d58def8e415ceda8aacfd8453813d8bf2a05991c0df0c074744639ab04321ba
PKG_SOURCE_DATE:=2020-05-25
PKG_SOURCE_VERSION:=66195aee50424cbda0c2d858014e4cc58a2dc029
CMAKE_INSTALL:=1
PKG_LICENSE:=ISC
@@ -27,7 +27,7 @@ define Package/libubox
SECTION:=libs
CATEGORY:=Libraries
TITLE:=Basic utility library
ABI_VERSION:=20180725
ABI_VERSION:=20191228
DEPENDS:=
endef

View File

@@ -1,39 +0,0 @@
From 2acfe84e4c871fb994c38c9f2508eb9ebd296b74 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20=C5=A0tetiar?= <ynezz@true.cz>
Date: Tue, 19 Nov 2019 17:34:25 +0100
Subject: blobmsg_json: fix possible uninitialized struct member
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
clang-10 analyzer reports following:
blobmsg_json.c:285:2: warning: The expression is an uninitialized value. The computed value will also be garbage
s->indent_level++;
^~~~~~~~~~~~~~~~~
Signed-off-by: Petr Štetiar <ynezz@true.cz>
---
blobmsg_json.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
--- a/blobmsg_json.c
+++ b/blobmsg_json.c
@@ -316,7 +316,7 @@ static void setup_strbuf(struct strbuf *
char *blobmsg_format_json_with_cb(struct blob_attr *attr, bool list, blobmsg_json_format_t cb, void *priv, int indent)
{
- struct strbuf s;
+ struct strbuf s = {0};
bool array;
char *ret;
@@ -350,7 +350,7 @@ char *blobmsg_format_json_with_cb(struct
char *blobmsg_format_json_value_with_cb(struct blob_attr *attr, blobmsg_json_format_t cb, void *priv, int indent)
{
- struct strbuf s;
+ struct strbuf s = {0};
char *ret;
setup_strbuf(&s, attr, cb, priv, indent);

View File

@@ -1,39 +0,0 @@
From f27853d71a2cb99ec5de3881716a14611ada307c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20=C5=A0tetiar?= <ynezz@true.cz>
Date: Sat, 23 Nov 2019 22:48:25 +0100
Subject: jshn: fix off by one in jshn_parse_file
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Fixes following error:
Invalid read of size 1
at 0x4C32D04: strlen
by 0x5043367: json_tokener_parse_ex
by 0x5045316: json_tokener_parse_verbose
by 0x504537D: json_tokener_parse
by 0x401AB1: jshn_parse (jshn.c:179)
by 0x40190D: jshn_parse_file (jshn.c:370)
by 0x40190D: main (jshn.c:434)
Address 0x5848c4c is 0 bytes after a block of size 1,036 alloc'd
at 0x4C2FB0F: malloc
by 0x4018E2: jshn_parse_file (jshn.c:357)
by 0x4018E2: main (jshn.c:434)
Signed-off-by: Petr Štetiar <ynezz@true.cz>
---
jshn.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/jshn.c
+++ b/jshn.c
@@ -384,7 +384,7 @@ int main(int argc, char **argv)
close(fd);
return 3;
}
- if (!(fbuf = malloc(sb.st_size))) {
+ if (!(fbuf = calloc(1, sb.st_size+1))) {
fprintf(stderr, "Error allocating memory for %s\n", optarg);
close(fd);
return 3;

View File

@@ -1,97 +0,0 @@
From af2a074160e32692b570f8a3562b4370d38f34e7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20=C5=A0tetiar?= <ynezz@true.cz>
Date: Mon, 9 Dec 2019 13:53:27 +0100
Subject: blob: refactor attr parsing into separate function
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Making blob_parse easier to review.
Signed-off-by: Petr Štetiar <ynezz@true.cz>
---
blob.c | 61 +++++++++++++++++++++++++++++++++-------------------------
1 file changed, 35 insertions(+), 26 deletions(-)
--- a/blob.c
+++ b/blob.c
@@ -217,44 +217,53 @@ blob_check_type(const void *ptr, unsigne
return true;
}
-int
-blob_parse(struct blob_attr *attr, struct blob_attr **data, const struct blob_attr_info *info, int max)
+static int
+blob_parse_attr(struct blob_attr *attr, struct blob_attr **data, const struct blob_attr_info *info, int max)
{
- struct blob_attr *pos;
int found = 0;
- int rem;
+ int id = blob_id(attr);
+ size_t len = blob_len(attr);
- memset(data, 0, sizeof(struct blob_attr *) * max);
- blob_for_each_attr(pos, attr, rem) {
- int id = blob_id(pos);
- int len = blob_len(pos);
+ if (id >= max)
+ return 0;
- if (id >= max)
- continue;
+ if (info) {
+ int type = info[id].type;
- if (info) {
- int type = info[id].type;
+ if (type < BLOB_ATTR_LAST) {
+ if (!blob_check_type(blob_data(attr), len, type))
+ return 0;
+ }
- if (type < BLOB_ATTR_LAST) {
- if (!blob_check_type(blob_data(pos), len, type))
- continue;
- }
+ if (info[id].minlen && len < info[id].minlen)
+ return 0;
- if (info[id].minlen && len < info[id].minlen)
- continue;
+ if (info[id].maxlen && len > info[id].maxlen)
+ return 0;
- if (info[id].maxlen && len > info[id].maxlen)
- continue;
+ if (info[id].validate && !info[id].validate(&info[id], attr))
+ return 0;
+ }
- if (info[id].validate && !info[id].validate(&info[id], pos))
- continue;
- }
+ if (!data[id])
+ found++;
- if (!data[id])
- found++;
+ data[id] = attr;
+ return found;
+}
- data[id] = pos;
+int
+blob_parse(struct blob_attr *attr, struct blob_attr **data, const struct blob_attr_info *info, int max)
+{
+ struct blob_attr *pos;
+ int found = 0;
+ size_t rem;
+
+ memset(data, 0, sizeof(struct blob_attr *) * max);
+ blob_for_each_attr(pos, attr, rem) {
+ found += blob_parse_attr(pos, data, info, max);
}
+
return found;
}

View File

@@ -1,78 +0,0 @@
From b6a0a070f2e14808e835c2fcfa3820a55041902f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20=C5=A0tetiar?= <ynezz@true.cz>
Date: Mon, 9 Dec 2019 14:11:45 +0100
Subject: blob: introduce blob_parse_untrusted
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
blob_parse can be only used on trusted input as it has no possibility to
check the length of the provided input buffer, which might lead to
undefined behaviour and/or crashes when supplied with malformed,
corrupted or otherwise specially crafted input.
So this introduces blob_parse_untrusted variant which expects additional
input buffer length argument and thus should be able to process also
inputs from untrusted sources.
Signed-off-by: Petr Štetiar <ynezz@true.cz>
---
blob.c | 24 ++++++++++++++++++++++++
blob.h | 7 +++++++
2 files changed, 31 insertions(+)
--- a/blob.c
+++ b/blob.c
@@ -253,6 +253,30 @@ blob_parse_attr(struct blob_attr *attr,
}
int
+blob_parse_untrusted(struct blob_attr *attr, size_t attr_len, struct blob_attr **data, const struct blob_attr_info *info, int max)
+{
+ struct blob_attr *pos;
+ size_t len = 0;
+ int found = 0;
+ size_t rem;
+
+ if (!attr || attr_len < sizeof(struct blob_attr))
+ return 0;
+
+ len = blob_raw_len(attr);
+ if (len != attr_len)
+ return 0;
+
+ memset(data, 0, sizeof(struct blob_attr *) * max);
+ blob_for_each_attr_len(pos, attr, len, rem) {
+ found += blob_parse_attr(pos, rem, data, info, max);
+ }
+
+ return found;
+}
+
+/* use only on trusted input, otherwise consider blob_parse_untrusted */
+int
blob_parse(struct blob_attr *attr, struct blob_attr **data, const struct blob_attr_info *info, int max)
{
struct blob_attr *pos;
--- a/blob.h
+++ b/blob.h
@@ -199,6 +199,7 @@ extern void blob_nest_end(struct blob_bu
extern struct blob_attr *blob_put(struct blob_buf *buf, int id, const void *ptr, unsigned int len);
extern bool blob_check_type(const void *ptr, unsigned int len, int type);
extern int blob_parse(struct blob_attr *attr, struct blob_attr **data, const struct blob_attr_info *info, int max);
+extern int blob_parse_untrusted(struct blob_attr *attr, size_t attr_len, struct blob_attr **data, const struct blob_attr_info *info, int max);
extern struct blob_attr *blob_memdup(struct blob_attr *attr);
extern struct blob_attr *blob_put_raw(struct blob_buf *buf, const void *ptr, unsigned int len);
@@ -254,5 +255,11 @@ blob_put_u64(struct blob_buf *buf, int i
(blob_pad_len(pos) >= sizeof(struct blob_attr)); \
rem -= blob_pad_len(pos), pos = blob_next(pos))
+#define blob_for_each_attr_len(pos, attr, attr_len, rem) \
+ for (rem = attr ? blob_len(attr) : 0, \
+ pos = (struct blob_attr *) (attr ? blob_data(attr) : NULL); \
+ rem >= sizeof(struct blob_attr) && rem < attr_len && (blob_pad_len(pos) <= rem) && \
+ (blob_pad_len(pos) >= sizeof(struct blob_attr)); \
+ rem -= blob_pad_len(pos), pos = blob_next(pos))
#endif

View File

@@ -1,78 +0,0 @@
From 7425d421340594f50c717ff7129b6ee71280a447 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20=C5=A0tetiar?= <ynezz@true.cz>
Date: Mon, 9 Dec 2019 15:27:16 +0100
Subject: blob: fix OOB access in blob_check_type
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Found by fuzzer:
ERROR: AddressSanitizer: SEGV on unknown address 0x602100000455
The signal is caused by a READ memory access.
#0 in blob_check_type blob.c:214:43
#1 in blob_parse_attr blob.c:234:9
#2 in blob_parse_untrusted blob.c:272:12
#3 in fuzz_blob_parse tests/fuzzer/test-blob-parse-fuzzer.c:34:2
#4 in LLVMFuzzerTestOneInput tests/fuzzer/test-blob-parse-fuzzer.c:39:2
Caused by following line:
if (type == BLOB_ATTR_STRING && data[len - 1] != 0)
where len was pointing outside of the data buffer.
Signed-off-by: Petr Štetiar <ynezz@true.cz>
---
blob.c | 23 ++++++++++++++++++-----
1 file changed, 18 insertions(+), 5 deletions(-)
--- a/blob.c
+++ b/blob.c
@@ -218,20 +218,33 @@ blob_check_type(const void *ptr, unsigne
}
static int
-blob_parse_attr(struct blob_attr *attr, struct blob_attr **data, const struct blob_attr_info *info, int max)
+blob_parse_attr(struct blob_attr *attr, size_t attr_len, struct blob_attr **data, const struct blob_attr_info *info, int max)
{
+ int id;
+ size_t len;
int found = 0;
- int id = blob_id(attr);
- size_t len = blob_len(attr);
+ size_t data_len;
+ if (!attr || attr_len < sizeof(struct blob_attr))
+ return 0;
+
+ id = blob_id(attr);
if (id >= max)
return 0;
+ len = blob_raw_len(attr);
+ if (len > attr_len || len < sizeof(struct blob_attr))
+ return 0;
+
+ data_len = blob_len(attr);
+ if (data_len > len)
+ return 0;
+
if (info) {
int type = info[id].type;
if (type < BLOB_ATTR_LAST) {
- if (!blob_check_type(blob_data(attr), len, type))
+ if (!blob_check_type(blob_data(attr), data_len, type))
return 0;
}
@@ -285,7 +298,7 @@ blob_parse(struct blob_attr *attr, struc
memset(data, 0, sizeof(struct blob_attr *) * max);
blob_for_each_attr(pos, attr, rem) {
- found += blob_parse_attr(pos, data, info, max);
+ found += blob_parse_attr(pos, rem, data, info, max);
}
return found;

View File

@@ -1,32 +0,0 @@
From 0773eef13674964d890420673d2501342979d8bf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20=C5=A0tetiar?= <ynezz@true.cz>
Date: Tue, 10 Dec 2019 12:02:40 +0100
Subject: blobmsg: fix heap buffer overflow in blobmsg_parse
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Fixes following error found by the fuzzer:
==29774==ERROR: AddressSanitizer: heap-buffer-overflow
READ of size 1 at 0x6020004f1c56 thread T0
#0 strcmp sanitizer_common_interceptors.inc:442:3
#1 blobmsg_parse blobmsg.c:168:8
Signed-off-by: Petr Štetiar <ynezz@true.cz>
---
blobmsg.c | 3 +++
1 file changed, 3 insertions(+)
--- a/blobmsg.c
+++ b/blobmsg.c
@@ -52,6 +52,9 @@ bool blobmsg_check_attr(const struct blo
id = blob_id(attr);
len = blobmsg_data_len(attr);
+ if (len > blob_raw_len(attr))
+ return false;
+
data = blobmsg_data(attr);
if (id > BLOBMSG_TYPE_LAST)

View File

@@ -1,51 +0,0 @@
From cec3ed2550073abbfe0f1f6131c44f90c9d05aa8 Mon Sep 17 00:00:00 2001
From: Tobias Schramm <tobleminer@gmail.com>
Date: Wed, 28 Nov 2018 13:39:29 +0100
Subject: Ensure blob_attr length check does not perform out of bounds reads
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Before there might have been as little as one single byte left which
would result in 3 bytes of blob_attr->id_len being out of bounds.
Acked-by: Yousong Zhou <yszhou4tech@gmail.com>
Signed-off-by: Tobias Schramm <tobleminer@gmail.com>
[line wrapped < 72 chars]
Signed-off-by: Petr Štetiar <ynezz@true.cz>
---
blob.h | 4 ++--
blobmsg.h | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
--- a/blob.h
+++ b/blob.h
@@ -243,7 +243,7 @@ blob_put_u64(struct blob_buf *buf, int i
#define __blob_for_each_attr(pos, attr, rem) \
for (pos = (struct blob_attr *) attr; \
- rem > 0 && (blob_pad_len(pos) <= rem) && \
+ rem >= sizeof(struct blob_attr) && (blob_pad_len(pos) <= rem) && \
(blob_pad_len(pos) >= sizeof(struct blob_attr)); \
rem -= blob_pad_len(pos), pos = blob_next(pos))
@@ -251,7 +251,7 @@ blob_put_u64(struct blob_buf *buf, int i
#define blob_for_each_attr(pos, attr, rem) \
for (rem = attr ? blob_len(attr) : 0, \
pos = (struct blob_attr *) (attr ? blob_data(attr) : NULL); \
- rem > 0 && (blob_pad_len(pos) <= rem) && \
+ rem >= sizeof(struct blob_attr) && (blob_pad_len(pos) <= rem) && \
(blob_pad_len(pos) >= sizeof(struct blob_attr)); \
rem -= blob_pad_len(pos), pos = blob_next(pos))
--- a/blobmsg.h
+++ b/blobmsg.h
@@ -266,7 +266,7 @@ int blobmsg_printf(struct blob_buf *buf,
#define blobmsg_for_each_attr(pos, attr, rem) \
for (rem = attr ? blobmsg_data_len(attr) : 0, \
pos = (struct blob_attr *) (attr ? blobmsg_data(attr) : NULL); \
- rem > 0 && (blob_pad_len(pos) <= rem) && \
+ rem >= sizeof(struct blob_attr) && (blob_pad_len(pos) <= rem) && \
(blob_pad_len(pos) >= sizeof(struct blob_attr)); \
rem -= blob_pad_len(pos), pos = blob_next(pos))

View File

@@ -1,132 +0,0 @@
From 8b6a401638317906b6d9039417c1c19ea8cfeab0 Mon Sep 17 00:00:00 2001
From: Tobias Schramm <tobleminer@gmail.com>
Date: Tue, 13 Nov 2018 04:16:12 +0100
Subject: Replace use of blobmsg_check_attr by blobmsg_check_attr_len
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
blobmsg_check_attr_len adds a length limit specifying the max offset
from attr that can be read safely.
Signed-off-by: Tobias Schramm <tobleminer@gmail.com>
[rebased and reworked, line wrapped commit message, _safe -> _len]
Signed-off-by: Petr Štetiar <ynezz@true.cz>
---
blobmsg.c | 59 +++++++++++++++++++++++++++++++++++++++++++------------
blobmsg.h | 2 ++
2 files changed, 48 insertions(+), 13 deletions(-)
--- a/blobmsg.c
+++ b/blobmsg.c
@@ -33,37 +33,70 @@ blobmsg_namelen(const struct blobmsg_hdr
bool blobmsg_check_attr(const struct blob_attr *attr, bool name)
{
+ return blobmsg_check_attr_len(attr, name, blob_raw_len(attr));
+}
+
+static bool blobmsg_check_name(const struct blob_attr *attr, size_t len, bool name)
+{
+ char *limit = (char *) attr + len;
const struct blobmsg_hdr *hdr;
- const char *data;
- int id, len;
- if (blob_len(attr) < sizeof(struct blobmsg_hdr))
+ hdr = blob_data(attr);
+ if (name && !hdr->namelen)
return false;
- hdr = (void *) attr->data;
- if (!hdr->namelen && name)
+ if ((char *) hdr->name + blobmsg_namelen(hdr) > limit)
return false;
- if (blobmsg_namelen(hdr) > blob_len(attr) - sizeof(struct blobmsg_hdr))
+ if (blobmsg_namelen(hdr) > (blob_len(attr) - sizeof(struct blobmsg_hdr)))
return false;
if (hdr->name[blobmsg_namelen(hdr)] != 0)
return false;
- id = blob_id(attr);
- len = blobmsg_data_len(attr);
- if (len > blob_raw_len(attr))
- return false;
+ return true;
+}
+
+static const char* blobmsg_check_data(const struct blob_attr *attr, size_t len, size_t *data_len)
+{
+ char *limit = (char *) attr + len;
+ const char *data;
+
+ *data_len = blobmsg_data_len(attr);
+ if (*data_len > blob_raw_len(attr))
+ return NULL;
data = blobmsg_data(attr);
+ if (data + *data_len > limit)
+ return NULL;
+ return data;
+}
+
+bool blobmsg_check_attr_len(const struct blob_attr *attr, bool name, size_t len)
+{
+ const char *data;
+ size_t data_len;
+ int id;
+
+ if (len < sizeof(struct blob_attr))
+ return false;
+
+ if (!blobmsg_check_name(attr, len, name))
+ return false;
+
+ id = blob_id(attr);
if (id > BLOBMSG_TYPE_LAST)
return false;
if (!blob_type[id])
return true;
- return blob_check_type(data, len, blob_type[id]);
+ data = blobmsg_check_data(attr, len, &data_len);
+ if (!data)
+ return false;
+
+ return blob_check_type(data, data_len, blob_type[id]);
}
int blobmsg_check_array(const struct blob_attr *attr, int type)
@@ -114,7 +147,7 @@ int blobmsg_parse_array(const struct blo
blob_id(attr) != policy[i].type)
continue;
- if (!blobmsg_check_attr(attr, false))
+ if (!blobmsg_check_attr_len(attr, false, len))
return -1;
if (tb[i])
@@ -161,7 +194,7 @@ int blobmsg_parse(const struct blobmsg_p
if (blobmsg_namelen(hdr) != pslen[i])
continue;
- if (!blobmsg_check_attr(attr, true))
+ if (!blobmsg_check_attr_len(attr, true, len))
return -1;
if (tb[i])
--- a/blobmsg.h
+++ b/blobmsg.h
@@ -107,6 +107,8 @@ static inline int blobmsg_len(const stru
bool blobmsg_check_attr(const struct blob_attr *attr, bool name);
bool blobmsg_check_attr_list(const struct blob_attr *attr, int type);
+bool blobmsg_check_attr_len(const struct blob_attr *attr, bool name, size_t len);
+
/*
* blobmsg_check_array: validate array/table and return size
*

View File

@@ -1,157 +0,0 @@
From ad29d0304983e283d4aec4ee5462942eaf5c03ac Mon Sep 17 00:00:00 2001
From: Tobias Schramm <tobleminer@gmail.com>
Date: Thu, 15 Nov 2018 03:42:48 +0100
Subject: blobmsg: add _len variants for all attribute checking methods
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Introduce _len variants of blobmsg attribute checking functions which
aims to provide safer implementation as those functions should limit all
memory accesses performed on the blob to the range [attr, attr + len]
(upper bound non inclusive) and thus should be suited for checking of
untrusted blob attributes.
While at it add some comments in order to make it clear.
Signed-off-by: Tobias Schramm <tobleminer@gmail.com>
[_safe -> _len, blobmsg_check_array_len fix, commit subject/desc facelift]
Signed-off-by: Petr Štetiar <ynezz@true.cz>
---
blobmsg.c | 21 ++++++++++++++++++---
blobmsg.h | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 72 insertions(+), 4 deletions(-)
--- a/blobmsg.c
+++ b/blobmsg.c
@@ -101,11 +101,21 @@ bool blobmsg_check_attr_len(const struct
int blobmsg_check_array(const struct blob_attr *attr, int type)
{
+ return blobmsg_check_array_len(attr, type, blob_raw_len(attr));
+}
+
+int blobmsg_check_array_len(const struct blob_attr *attr, int type, size_t len)
+{
struct blob_attr *cur;
bool name;
- int rem;
int size = 0;
+ if (type > BLOBMSG_TYPE_LAST)
+ return -1;
+
+ if (!blobmsg_check_attr_len(attr, false, len))
+ return -1;
+
switch (blobmsg_type(attr)) {
case BLOBMSG_TYPE_TABLE:
name = true;
@@ -117,11 +127,11 @@ int blobmsg_check_array(const struct blo
return -1;
}
- blobmsg_for_each_attr(cur, attr, rem) {
+ __blobmsg_for_each_attr(cur, attr, len) {
if (type != BLOBMSG_TYPE_UNSPEC && blobmsg_type(cur) != type)
return -1;
- if (!blobmsg_check_attr(cur, name))
+ if (!blobmsg_check_attr_len(cur, name, len))
return -1;
size++;
@@ -135,6 +145,11 @@ bool blobmsg_check_attr_list(const struc
return blobmsg_check_array(attr, type) >= 0;
}
+bool blobmsg_check_attr_list_len(const struct blob_attr *attr, int type, size_t len)
+{
+ return blobmsg_check_array_len(attr, type, len) >= 0;
+}
+
int blobmsg_parse_array(const struct blobmsg_policy *policy, int policy_len,
struct blob_attr **tb, void *data, unsigned int len)
{
--- a/blobmsg.h
+++ b/blobmsg.h
@@ -104,19 +104,66 @@ static inline int blobmsg_len(const stru
return blobmsg_data_len(attr);
}
+/*
+ * blobmsg_check_attr: validate a list of attributes
+ *
+ * This method may be used with trusted data only. Providing
+ * malformed blobs will cause out of bounds memory access.
+ */
bool blobmsg_check_attr(const struct blob_attr *attr, bool name);
-bool blobmsg_check_attr_list(const struct blob_attr *attr, int type);
+/*
+ * blobmsg_check_attr_len: validate a list of attributes
+ *
+ * This method should be safer implementation of blobmsg_check_attr.
+ * It will limit all memory access performed on the blob to the
+ * range [attr, attr + len] (upper bound non inclusive) and is
+ * thus suited for checking of untrusted blob attributes.
+ */
bool blobmsg_check_attr_len(const struct blob_attr *attr, bool name, size_t len);
/*
+ * blobmsg_check_attr_list: validate a list of attributes
+ *
+ * This method may be used with trusted data only. Providing
+ * malformed blobs will cause out of bounds memory access.
+ */
+bool blobmsg_check_attr_list(const struct blob_attr *attr, int type);
+
+/*
+ * blobmsg_check_attr_list_len: validate a list of untrusted attributes
+ *
+ * This method should be safer implementation of blobmsg_check_attr_list.
+ * It will limit all memory access performed on the blob to the
+ * range [attr, attr + len] (upper bound non inclusive) and is
+ * thus suited for checking of untrusted blob attributes.
+ */
+bool blobmsg_check_attr_list_len(const struct blob_attr *attr, int type, size_t len);
+
+/*
* blobmsg_check_array: validate array/table and return size
*
* Checks if all elements of an array or table are valid and have
* the specified type. Returns the number of elements in the array
+ *
+ * This method may be used with trusted data only. Providing
+ * malformed blobs will cause out of bounds memory access.
*/
int blobmsg_check_array(const struct blob_attr *attr, int type);
+/*
+ * blobmsg_check_array_len: validate untrusted array/table and return size
+ *
+ * Checks if all elements of an array or table are valid and have
+ * the specified type. Returns the number of elements in the array.
+ *
+ * This method should be safer implementation of blobmsg_check_array.
+ * It will limit all memory access performed on the blob to the
+ * range [attr, attr + len] (upper bound non inclusive) and is
+ * thus suited for checking of untrusted blob attributes.
+ */
+int blobmsg_check_array_len(const struct blob_attr *attr, int type, size_t len);
+
int blobmsg_parse(const struct blobmsg_policy *policy, int policy_len,
struct blob_attr **tb, void *data, unsigned int len);
int blobmsg_parse_array(const struct blobmsg_policy *policy, int policy_len,
@@ -271,5 +318,11 @@ int blobmsg_printf(struct blob_buf *buf,
rem >= sizeof(struct blob_attr) && (blob_pad_len(pos) <= rem) && \
(blob_pad_len(pos) >= sizeof(struct blob_attr)); \
rem -= blob_pad_len(pos), pos = blob_next(pos))
+
+#define __blobmsg_for_each_attr(pos, attr, rem) \
+ for (pos = (struct blob_attr *) (attr ? blobmsg_data(attr) : NULL); \
+ rem >= sizeof(struct blob_attr) && (blob_pad_len(pos) <= rem) && \
+ (blob_pad_len(pos) >= sizeof(struct blob_attr)); \
+ rem -= blob_pad_len(pos), pos = blob_next(pos))
#endif

View File

@@ -1,39 +0,0 @@
From 44d9e85ef058fbb9981d53218cafdc451afa5535 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20=C5=A0tetiar?= <ynezz@true.cz>
Date: Wed, 25 Dec 2019 10:27:59 +0100
Subject: blobmsg: fix array out of bounds GCC 10 warning
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Fixes following warning reported by GCC 10.0.0 20191203:
blobmsg.c:234:2: error: 'strcpy' offset 6 from the object at 'attr' is out of the bounds of referenced subobject 'name' with type 'uint8_t[0]' {aka 'unsigned char[0]'} at offset 6 [-Werror=array-bounds]
234 | strcpy((char *) hdr->name, (const char *)name);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from blobmsg.c:16:
blobmsg.h:42:10: note: subobject 'name' declared here
42 | uint8_t name[];
| ^~~~
Reported-by: Khem Raj <raj.khem@gmail.com>
Signed-off-by: Petr Štetiar <ynezz@true.cz>
---
blobmsg.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
--- a/blobmsg.c
+++ b/blobmsg.c
@@ -246,7 +246,10 @@ blobmsg_new(struct blob_buf *buf, int ty
attr->id_len |= be32_to_cpu(BLOB_ATTR_EXTENDED);
hdr = blob_data(attr);
hdr->namelen = cpu_to_be16(namelen);
- strcpy((char *) hdr->name, (const char *)name);
+
+ memcpy(hdr->name, name, namelen);
+ hdr->name[namelen] = '\0';
+
pad_end = *data = blobmsg_data(attr);
pad_start = (char *) &hdr->name[namelen];
if (pad_start < pad_end)

View File

@@ -1,38 +0,0 @@
From d0f05d5e6873b30315127d47abbf4ac9f3c8bfb7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20=C5=A0tetiar?= <ynezz@true.cz>
Date: Sat, 28 Dec 2019 19:00:39 +0100
Subject: blobmsg: fix wrong payload len passed from blobmsg_check_array
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Fix incorrect use of blob_raw_len() on passed blobmsg to
blobmsg_check_array_len() introduced in commit b0e21553ae8c ("blobmsg:
add _len variants for all attribute checking methods") by using correct
blobmsg_len().
This wrong (higher) length was then for example causing issues in
procd's instance_config_parse_command() where blobmsg_check_attr_list()
was failing sanity checking of service command, thus resulting in the
startup failures of some services like collectd, nlbwmon and samba4.
Ref: http://lists.infradead.org/pipermail/openwrt-devel/2019-December/020840.html
Fixes: b0e21553ae8c ("blobmsg: add _len variants for all attribute checking methods")
Reported-by: Hannu Nyman <hannu.nyman@welho.com>
Tested-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
Signed-off-by: Petr Štetiar <ynezz@true.cz>
---
blobmsg.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/blobmsg.c
+++ b/blobmsg.c
@@ -101,7 +101,7 @@ bool blobmsg_check_attr_len(const struct
int blobmsg_check_array(const struct blob_attr *attr, int type)
{
- return blobmsg_check_array_len(attr, type, blob_raw_len(attr));
+ return blobmsg_check_array_len(attr, type, blobmsg_len(attr));
}
int blobmsg_check_array_len(const struct blob_attr *attr, int type, size_t len)

View File

@@ -1,61 +0,0 @@
From 31778937b4153492955495e550435c8bbf7cfde8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20=C5=A0tetiar?= <ynezz@true.cz>
Date: Tue, 14 Jan 2020 08:55:34 +0100
Subject: jshn: prefer snprintf usage
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Better safe than sorry.
Reviewed-by: Jo-Philipp Wich <jo@mein.io>
Signed-off-by: Petr Štetiar <ynezz@true.cz>
---
jshn.c | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
--- a/jshn.c
+++ b/jshn.c
@@ -68,7 +68,7 @@ static int add_json_array(struct array_l
int ret;
for (i = 0, len = array_list_length(a); i < len; i++) {
- sprintf(seq, "%d", i);
+ snprintf(seq, sizeof(seq), "%d", i);
ret = add_json_element(seq, array_list_get_idx(a, i));
if (ret)
return ret;
@@ -197,25 +197,27 @@ static char *getenv_avl(const char *key)
static char *get_keys(const char *prefix)
{
char *keys;
+ size_t len = var_prefix_len + strlen(prefix) + sizeof("K_") + 1;
- keys = alloca(var_prefix_len + strlen(prefix) + sizeof("K_") + 1);
- sprintf(keys, "%sK_%s", var_prefix, prefix);
+ keys = alloca(len);
+ snprintf(keys, len, "%sK_%s", var_prefix, prefix);
return getenv_avl(keys);
}
static void get_var(const char *prefix, const char **name, char **var, char **type)
{
char *tmpname, *varname;
+ size_t len = var_prefix_len + strlen(prefix) + 1 + strlen(*name) + 1 + sizeof("T_");
- tmpname = alloca(var_prefix_len + strlen(prefix) + 1 + strlen(*name) + 1 + sizeof("T_"));
+ tmpname = alloca(len);
- sprintf(tmpname, "%s%s_%s", var_prefix, prefix, *name);
+ snprintf(tmpname, len, "%s%s_%s", var_prefix, prefix, *name);
*var = getenv_avl(tmpname);
- sprintf(tmpname, "%sT_%s_%s", var_prefix, prefix, *name);
+ snprintf(tmpname, len, "%sT_%s_%s", var_prefix, prefix, *name);
*type = getenv_avl(tmpname);
- sprintf(tmpname, "%sN_%s_%s", var_prefix, prefix, *name);
+ snprintf(tmpname, len, "%sN_%s_%s", var_prefix, prefix, *name);
varname = getenv_avl(tmpname);
if (varname)
*name = varname;

View File

@@ -1,38 +0,0 @@
From 935bb933e4a74de7326a4373340fd50655712334 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20=C5=A0tetiar?= <ynezz@true.cz>
Date: Tue, 14 Jan 2020 08:57:05 +0100
Subject: blobmsg: blobmsg_vprintf: prefer vsnprintf
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Better safe than sorry and while at it add handling of possible
*printf() failures.
Reviewed-by: Jo-Philipp Wich <jo@mein.io>
Signed-off-by: Petr Štetiar <ynezz@true.cz>
---
blobmsg.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
--- a/blobmsg.c
+++ b/blobmsg.c
@@ -296,10 +296,17 @@ blobmsg_vprintf(struct blob_buf *buf, co
len = vsnprintf(&cbuf, sizeof(cbuf), format, arg2);
va_end(arg2);
+ if (len < 0)
+ return -1;
+
sbuf = blobmsg_alloc_string_buffer(buf, name, len + 1);
if (!sbuf)
return -1;
- ret = vsprintf(sbuf, format, arg);
+
+ ret = vsnprintf(sbuf, len + 1, format, arg);
+ if (ret < 0)
+ return -1;
+
blobmsg_add_string_buffer(buf);
return ret;

View File

@@ -1,41 +0,0 @@
From 1cc755d7c3989b399bf0c60535a858d22819ca27 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20=C5=A0tetiar?= <ynezz@true.cz>
Date: Sun, 12 Jan 2020 22:40:18 +0100
Subject: blobmsg_json: fix int16 serialization
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
int16 blobmsg type is currently being serialized as uint16_t due to
missing cast during JSON output.
Following blobmsg content:
bar-min: -32768 (i16)
bar-max: 32767 (i16)
Produces following JSON:
{ "bar-min":32768,"bar-max":32767 }
Whereas one would expect:
{ "bar-min":-32768,"bar-max":32767 }
Reviewed-by: Jo-Philipp Wich <jo@mein.io>
Signed-off-by: Petr Štetiar <ynezz@true.cz>
---
blobmsg_json.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/blobmsg_json.c
+++ b/blobmsg_json.c
@@ -250,7 +250,7 @@ static void blobmsg_format_element(struc
sprintf(buf, "%s", *(uint8_t *)data ? "true" : "false");
break;
case BLOBMSG_TYPE_INT16:
- sprintf(buf, "%d", be16_to_cpu(*(uint16_t *)data));
+ sprintf(buf, "%d", (int16_t) be16_to_cpu(*(uint16_t *)data));
break;
case BLOBMSG_TYPE_INT32:
sprintf(buf, "%d", (int32_t) be32_to_cpu(*(uint32_t *)data));

View File

@@ -1,66 +0,0 @@
From 0e330ec3662795aea42ac36ecf7a9f32a249c36d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20=C5=A0tetiar?= <ynezz@true.cz>
Date: Tue, 14 Jan 2020 09:05:02 +0100
Subject: blobmsg_json: prefer snprintf usage
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Better safe than sorry and while at it prefer use of PRId16 and PRId32
formatting constants as well.
Reviewed-by: Jo-Philipp Wich <jo@mein.io>
Signed-off-by: Petr Štetiar <ynezz@true.cz>
---
blobmsg_json.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
--- a/blobmsg_json.c
+++ b/blobmsg_json.c
@@ -203,7 +203,7 @@ static void blobmsg_format_string(struct
buf[1] = escape;
if (escape == 'u') {
- sprintf(buf + 4, "%02x", (unsigned char) *p);
+ snprintf(buf + 4, sizeof(buf) - 4, "%02x", (unsigned char) *p);
len = 6;
} else {
len = 2;
@@ -220,7 +220,7 @@ static void blobmsg_format_json_list(str
static void blobmsg_format_element(struct strbuf *s, struct blob_attr *attr, bool without_name, bool head)
{
const char *data_str;
- char buf[32];
+ char buf[317];
void *data;
int len;
@@ -244,22 +244,22 @@ static void blobmsg_format_element(struc
data_str = buf;
switch(blob_id(attr)) {
case BLOBMSG_TYPE_UNSPEC:
- sprintf(buf, "null");
+ snprintf(buf, sizeof(buf), "null");
break;
case BLOBMSG_TYPE_BOOL:
- sprintf(buf, "%s", *(uint8_t *)data ? "true" : "false");
+ snprintf(buf, sizeof(buf), "%s", *(uint8_t *)data ? "true" : "false");
break;
case BLOBMSG_TYPE_INT16:
- sprintf(buf, "%d", (int16_t) be16_to_cpu(*(uint16_t *)data));
+ snprintf(buf, sizeof(buf), "%" PRId16, (int16_t) be16_to_cpu(*(uint16_t *)data));
break;
case BLOBMSG_TYPE_INT32:
- sprintf(buf, "%d", (int32_t) be32_to_cpu(*(uint32_t *)data));
+ snprintf(buf, sizeof(buf), "%" PRId32, (int32_t) be32_to_cpu(*(uint32_t *)data));
break;
case BLOBMSG_TYPE_INT64:
- sprintf(buf, "%" PRId64, (int64_t) be64_to_cpu(*(uint64_t *)data));
+ snprintf(buf, sizeof(buf), "%" PRId64, (int64_t) be64_to_cpu(*(uint64_t *)data));
break;
case BLOBMSG_TYPE_DOUBLE:
- sprintf(buf, "%lf", blobmsg_get_double(attr));
+ snprintf(buf, sizeof(buf), "%lf", blobmsg_get_double(attr));
break;
case BLOBMSG_TYPE_STRING:
blobmsg_format_string(s, data);

View File

@@ -1,110 +0,0 @@
From 6289e2d29883d5d9510b6a15c18c597478967a42 Mon Sep 17 00:00:00 2001
From: Juraj Vijtiuk <juraj.vijtiuk@sartura.hr>
Date: Sun, 12 Jan 2020 12:26:18 +0100
Subject: blobmsg: blobmsg_parse and blobmsg_parse_array oob read fixes
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Fix out of bounds read in blobmsg_parse and blobmsg_check_name. The
out of bounds read happens because blob_attr and blobmsg_hdr have
flexible array members, whose size is 0 in the corresponding sizeofs.
For example the __blob_for_each_attr macro checks whether rem >=
sizeof(struct blob_attr). However, what LibFuzzer discovered was,
if the input data was only 4 bytes, the data would be casted to blob_attr,
and later on blob_data(attr) would be called even though attr->data was empty.
The same issue could appear with data larger than 4 bytes, where data
wasn't empty, but contained only the start of the blobmsg_hdr struct,
and blobmsg_hdr name was empty. The bugs were discovered by fuzzing
blobmsg_parse and blobmsg_array_parse with LibFuzzer.
CC: Luka Perkov <luka.perkov@sartura.hr>
Reviewed-by: Jo-Philipp Wich <jo@mein.io>
Signed-off-by: Juraj Vijtiuk <juraj.vijtiuk@sartura.hr>
[refactored some checks, added fuzz inputs, adjusted unit test results]
Signed-off-by: Petr Štetiar <ynezz@true.cz>
---
blobmsg.c | 40 ++++++++++++++++++++++++++++++++--------
1 file changed, 32 insertions(+), 8 deletions(-)
--- a/blobmsg.c
+++ b/blobmsg.c
@@ -36,16 +36,38 @@ bool blobmsg_check_attr(const struct blo
return blobmsg_check_attr_len(attr, name, blob_raw_len(attr));
}
+static const struct blobmsg_hdr* blobmsg_hdr_from_blob(const struct blob_attr *attr, size_t len)
+{
+ if (len < sizeof(struct blob_attr) + sizeof(struct blobmsg_hdr))
+ return NULL;
+
+ return blob_data(attr);
+}
+
+static bool blobmsg_hdr_valid_namelen(const struct blobmsg_hdr *hdr, size_t len)
+{
+ if (len < sizeof(struct blob_attr) + sizeof(struct blobmsg_hdr) + blobmsg_namelen(hdr) + 1)
+ return false;
+
+ return true;
+}
+
static bool blobmsg_check_name(const struct blob_attr *attr, size_t len, bool name)
{
char *limit = (char *) attr + len;
const struct blobmsg_hdr *hdr;
- hdr = blob_data(attr);
+ hdr = blobmsg_hdr_from_blob(attr, len);
+ if (!hdr)
+ return false;
+
if (name && !hdr->namelen)
return false;
- if ((char *) hdr->name + blobmsg_namelen(hdr) > limit)
+ if (name && !blobmsg_hdr_valid_namelen(hdr, len))
+ return false;
+
+ if ((char *) hdr->name + blobmsg_namelen(hdr) + 1 > limit)
return false;
if (blobmsg_namelen(hdr) > (blob_len(attr) - sizeof(struct blobmsg_hdr)))
@@ -79,9 +101,6 @@ bool blobmsg_check_attr_len(const struct
size_t data_len;
int id;
- if (len < sizeof(struct blob_attr))
- return false;
-
if (!blobmsg_check_name(attr, len, name))
return false;
@@ -176,11 +195,10 @@ int blobmsg_parse_array(const struct blo
return 0;
}
-
int blobmsg_parse(const struct blobmsg_policy *policy, int policy_len,
struct blob_attr **tb, void *data, unsigned int len)
{
- struct blobmsg_hdr *hdr;
+ const struct blobmsg_hdr *hdr;
struct blob_attr *attr;
uint8_t *pslen;
int i;
@@ -197,7 +215,13 @@ int blobmsg_parse(const struct blobmsg_p
}
__blob_for_each_attr(attr, data, len) {
- hdr = blob_data(attr);
+ hdr = blobmsg_hdr_from_blob(attr, len);
+ if (!hdr)
+ return -1;
+
+ if (!blobmsg_hdr_valid_namelen(hdr, len))
+ return -1;
+
for (i = 0; i < policy_len; i++) {
if (!policy[i].name)
continue;

View File

@@ -1,33 +0,0 @@
From 75e300aeec25e032a9778bea34c713969960d1f0 Mon Sep 17 00:00:00 2001
From: Chris Nisbet <nischris@gmail.com>
Date: Wed, 12 Feb 2020 21:00:31 +1300
Subject: [PATCH] blobmsg: fix wrong payload len passed from
blobmsg_check_array
Fix incorrect use of blobmsg_len() on passed blobmsg to
blobmsg_check_array_len() introduced in commit 379cd33d1992
("fix wrong payload len passed from blobmsg_check_array") by using correct
blob_len().
By using blobmsg_len() a value too small was passed to blobmsg_check_array()
which could lead to this function returning an error when there is none.
Fixes: 379cd33d1992 ("fix wrong payload len passed from blobmsg_check_array")
Signed-off-by: Chris Nisbet <nischris@gmail.com>
[add fixes tag, rewrap commit message]
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
---
blobmsg.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/blobmsg.c
+++ b/blobmsg.c
@@ -120,7 +120,7 @@ bool blobmsg_check_attr_len(const struct
int blobmsg_check_array(const struct blob_attr *attr, int type)
{
- return blobmsg_check_array_len(attr, type, blobmsg_len(attr));
+ return blobmsg_check_array_len(attr, type, blob_len(attr));
}
int blobmsg_check_array_len(const struct blob_attr *attr, int type, size_t len)

View File

@@ -1,38 +0,0 @@
From c42f11cc7c0f0ec6571af06ada6ff0e8882f4fde Mon Sep 17 00:00:00 2001
From: =?utf8?q?Petr=20=C5=A0tetiar?= <ynezz@true.cz>
Date: Tue, 19 Nov 2019 12:34:14 +0100
Subject: [PATCH] jshn: main: fix leak of memory pointed to by 'vars'
MIME-Version: 1.0
Content-Type: text/plain; charset=utf8
Content-Transfer-Encoding: 8bit
Fixes following leak of memory:
6,016 bytes in 1 blocks are possibly lost in loss record 1 of 1
at 0x4C31B25: calloc
by 0x1098F8: main (jshn.c:353)
Signed-off-by: Petr Å tetiar <ynezz@true.cz>
---
jshn.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/jshn.c b/jshn.c
index 2d1748e..00293f2 100644
--- a/jshn.c
+++ b/jshn.c
@@ -410,8 +410,11 @@
indent = true;
break;
default:
+ free(vars);
return usage(argv[0]);
}
}
+
+ free(vars);
return usage(argv[0]);
}
--
2.20.1

View File

@@ -1,56 +0,0 @@
From cb698e35409b898aedbbc3c673a0055dc1520ef6 Mon Sep 17 00:00:00 2001
From: =?utf8?q?Petr=20=C5=A0tetiar?= <ynezz@true.cz>
Date: Tue, 19 Nov 2019 14:09:43 +0100
Subject: [PATCH 1/1] jshn: jshn_parse: fix leaks of memory pointed to by 'obj'
MIME-Version: 1.0
Content-Type: text/plain; charset=utf8
Content-Transfer-Encoding: 8bit
Fixes following leaks of memory:
352 (72 direct, 280 indirect) bytes in 1 blocks are definitely lost in loss record 3 of 3
at 0x4C31B25: calloc
by 0x5042E1F: json_object_new_array
by 0x5044B02: json_tokener_parse_ex
by 0x5045316: json_tokener_parse_verbose
by 0x504537D: json_tokener_parse
by 0x401AA9: jshn_parse (jshn.c:179)
by 0x401977: main (jshn.c:378)
752 (72 direct, 680 indirect) bytes in 1 blocks are definitely lost in loss record 6 of 6
at 0x4C31B25: calloc
by 0x50424CF: json_object_new_object
by 0x5044B38: json_tokener_parse_ex
by 0x5045316: json_tokener_parse_verbose
by 0x504537D: json_tokener_parse
by 0x401AA9: jshn_parse (jshn.c:179)
by 0x401977: main (jshn.c:380)
Signed-off-by: Petr Å tetiar <ynezz@true.cz>
---
jshn.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/jshn.c b/jshn.c
index 2eebe6c..9639951 100644
--- a/jshn.c
+++ b/jshn.c
@@ -178,12 +178,15 @@ static int jshn_parse(const char *str)
obj = json_tokener_parse(str);
if (!obj || json_object_get_type(obj) != json_type_object) {
+ if (obj)
+ json_object_put(obj);
fprintf(stderr, "Failed to parse message data\n");
return 1;
}
fprintf(stdout, "json_init;\n");
add_json_object(obj);
fflush(stdout);
+ json_object_put(obj);
return 0;
}
--
2.20.1

View File

@@ -0,0 +1,98 @@
# Copyright (C) 2020 Lienol <lawlienol@gmail.com>
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-nginx-pingos
PKG_VERSION:=1.19.0
PKG_RELEASE:=3
PKG_SOURCE:=nginx-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://nginx.org/download/
PKG_HASH:=44a616171fcd7d7ad7c6af3e6f3ad0879b54db5a5d21be874cd458b5691e36c8
PKG_DIR:=$(BUILD_DIR)/$(PKG_NAME)
PKG_BUILD_DIR:=$(PKG_DIR)/nginx-$(PKG_VERSION)
PKG_FIXUP:=autoreconf
PKG_BUILD_PARALLEL:=1
PKG_INSTALL:=1
include $(INCLUDE_DIR)/package.mk
define Package/$(PKG_NAME)
CATEGORY:=LuCI
SUBMENU:=3. Applications
TITLE:=PingOS server
PKGARCH:=all
URL:=https://pingos.io/
DEPENDS:=+libpcre +libopenssl +zlib +libpthread
endef
define Package/$(PKG_NAME)/conffiles
endef
define Build/Prepare
rm -r $(PKG_BUILD_DIR)
mkdir -p $(PKG_BUILD_DIR)
tar -zxvf $(DL_DIR)/$(PKG_SOURCE) -C $(PKG_DIR)
$(CP) -pR ./modules $(PKG_BUILD_DIR)/modules
$(call Build/Prepare/Default,)
endef
ADDITIONAL_MODULES:= --with-http_ssl_module \
--add-module=./modules/nginx-rtmp-module \
--add-module=./modules/nginx-client-module \
--add-module=./modules/nginx-multiport-module \
--add-module=./modules/nginx-toolkit-module
TARGET_CFLAGS += -fvisibility=hidden -ffunction-sections -fdata-sections -DNGX_LUA_NO_BY_LUA_BLOCK
TARGET_LDFLAGS += -Wl,--gc-sections
CONFIGURE_ARGS += \
--crossbuild=Linux::$(ARCH) \
--prefix=/usr \
--conf-path=/usr/share/pingos/conf/nginx.conf \
$(ADDITIONAL_MODULES) \
--error-log-path=/var/etc/pingos/error.log \
--pid-path=/var/etc/pingos/pingos.pid \
--lock-path=/var/etc/pingos/pingos.lock \
--http-log-path=/var/etc/pingos/access.log \
--http-client-body-temp-path=/var/etc/pingos/lib/body \
--http-proxy-temp-path=/var/etc/pingos/lib/proxy \
--with-cc="$(TARGET_CC)" \
--with-cc-opt="$(TARGET_CPPFLAGS) $(TARGET_CFLAGS)" \
--with-ld-opt="$(TARGET_LDFLAGS)" \
--without-http_upstream_zone_module
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/etc
$(INSTALL_CONF) ./root/etc/pingos.template $(1)/etc/pingos.template
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_CONF) ./root/etc/config/pingos $(1)/etc/config/pingos
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) ./root/etc/init.d/pingos $(1)/etc/init.d/pingos
$(INSTALL_DIR) $(1)/etc/uci-defaults
$(INSTALL_CONF) ./root/etc/uci-defaults/* $(1)/etc/uci-defaults
$(INSTALL_DIR) $(1)/usr/share/pingos
cp -pR $(PKG_INSTALL_DIR)/usr/share/pingos/conf $(1)/usr/share/pingos
$(INSTALL_DATA) ./root/resource/conf-template/nginx.conf $(1)/usr/share/pingos/conf/nginx.conf
cp -pR $(PKG_INSTALL_DIR)/usr/html $(1)/usr/share/pingos/html
$(INSTALL_DATA) ./root/resource/crossdomain.xml $(1)/usr/share/pingos/html/crossdomain.xml
$(INSTALL_DATA) ./root/resource/stat.xsl $(1)/usr/share/pingos/html/stat.xsl
$(INSTALL_DIR) $(1)/usr/lib/lua/luci
cp -pR ./luasrc/* $(1)/usr/lib/lua/luci/
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/i18n
po2lmo ./po/zh-cn/pingos.po $(1)/usr/lib/lua/luci/i18n/pingos.zh-cn.lmo
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/nginx $(1)/usr/sbin/pingos
endef
$(eval $(call BuildPackage,$(PKG_NAME)))

View File

@@ -0,0 +1,17 @@
-- Copyright 2020 Lienol <lawlienol@gmail.com>
module("luci.controller.pingos", package.seeall)
function index()
if not nixio.fs.access("/etc/config/pingos") then return end
entry({"admin", "nas"}, firstchild(), "NAS", 44).dependent = false
entry({"admin", "nas", "pingos"}, cbi("pingos"), _("PingOS"), 3).dependent = true
entry({"admin", "nas", "pingos", "status"}, call("act_status")).leaf = true
end
function act_status()
local e = {}
e.status = luci.sys.call("ps -w | grep pingos | grep nginx | grep -v grep > /dev/null") == 0
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end

View File

@@ -0,0 +1,65 @@
m = Map("pingos", translate("PingOS"))
m:append(Template("pingos/status"))
s = m:section(TypedSection, "global")
s.anonymous = true
s.addremove = false
s:tab("global", translate("Global Settings"))
s:tab("template", translate("Edit Template"))
nginx = s:taboption("template", Value, "_nginx", translatef("Edit the template that is used for generating the %s configuration.", "nginx"),
translatef("This is the content of the file '%s'", "/etc/pingos.template") .. "<br />" ..
translatef("Values enclosed by pipe symbols ('|') should not be changed. They get their values from the '%s' tab.", translate("Global Settings")))
nginx.template = "cbi/tvalue"
nginx.rows = 30
function nginx.cfgvalue(self, section)
return nixio.fs.readfile("/etc/pingos.template")
end
function nginx.write(self, section, value)
value = value:gsub("\r\n?", "\n")
nixio.fs.writefile("/etc/pingos.template", value)
end
o = s:taboption("global", Flag, "enable", translate("Enable"))
o.rmempty = false
o = s:taboption("global", Value, "http_port", "HTTP(S)" ..translate("Port"))
o.datatype = "port"
o.default = 8082
o.rmempty = false
o = s:taboption("global", Flag, "https", translate("HTTPS"))
o.rmempty = false
o = s:taboption("global", FileUpload, "certificate", translate("certificate"))
o:depends("https", 1)
o = s:taboption("global", FileUpload, "key", translate("key"))
o:depends("https", 1)
o = s:taboption("global", Value, "rtmp_port", "RTMP" ..translate("Port"))
o.datatype = "port"
o.default = 1935
o.rmempty = false
o = s:taboption("global", Flag, "hls", translate("HLS"))
o.rmempty = false
o = s:taboption("global", Flag, "hls2", translate("HLS2"))
o.rmempty = false
o = s:taboption("global", Flag, "ts_record", "TS " .. translate("Record"))
o.rmempty = false
o = s:taboption("global", Flag, "flv_record", "FLV " .. translate("Record"))
o.rmempty = false
o = s:taboption("global", Value, "record_path", translate("Record") .. translate("Path"))
o.default = "/tmp/record"
o:depends("ts_record", 1)
o:depends("flv_record", 1)
return m

View File

@@ -0,0 +1,52 @@
<fieldset class="cbi-section">
<legend><%:Running Status%></legend>
<fieldset class="cbi-section">
<div class="cbi-value">
<label class="cbi-value-title">Nginx <%:Status%></label>
<div class="cbi-value-field" id="_nginx_status"><%:Collecting data...%></div>
</div>
<div class="cbi-value">
<label class="cbi-value-title"></label>
<div class="cbi-value-field">
<font color="red" id="tips"></font>
<ul id="tips2">
<li>rtmp rtmp://ip/live/<%:Stream name%></li>
<li>http(s)-flv http(s)://ip/flv/<%:Stream name%></li>
<li>hls http(s)://ip/hls/<%:Stream name%>.m3u8</li>
<li>hls+ http(s)://ip/hls2/<%:Stream name%>.m3u8</li>
<li>http(s)-ts http(s)://ip/ts/<%:Stream name%></li>
</ul>
</div>
</div>
</fieldset>
</fieldset>
<script type="text/javascript">//<![CDATA[
var nginx_status = document.getElementById('_nginx_status');
XHR.poll(3, '<%=url([[admin]], [[nas]], [[pingos]], [[status]])%>', null,
function(x, json) {
if (x && x.status == 200) {
if (nginx_status) {
var str = "";
if (json.status) {
document.getElementById("tips").innerHTML = '<%:If you need external network access, please open the port by yourself.%>';
str = '<font color="green"><%:RUNNING%> ✓</font><input type="button" class="cbi-button cbi-input-apply" value="<%:Enter interface%>" onclick="open_web()" />'
} else {
document.getElementById("tips").innerHTML = '';
str = '<font color="red"><%:NOT RUNNING%> X</font>';
}
nginx_status.innerHTML = str;
}
}
}
);
function open_web(){
var port = '<%=luci.sys.exec("uci -q get pingos.@global[0].http_port"):gsub("^%s*(.-)%s*$", "%1")%>';
var ishttps = '<%=luci.sys.exec("uci -q get pingos.@global[0].https"):gsub("^%s*(.-)%s*$", "%1")%>';
var protocol = (ishttps == "1") ? "https://" : "http://";
var hostname = location.hostname;
var url = protocol + hostname + ":" + port;
window.open(url, 'target', '');
}
//]]></script>

View File

@@ -0,0 +1,24 @@
Copyright (C) 2016-2020, by Jie Wu "AlexWoo" <wj19840501@gmail.com>.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

View File

@@ -0,0 +1,19 @@
ngx_addon_name=ngx_client_module
CORE_MODULES="$CORE_MODULES \
ngx_client_module \
ngx_http_client_module \
"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS \
$ngx_addon_dir/ngx_client.c \
$ngx_addon_dir/ngx_http_client.c \
"
NGX_ADDON_DEPS="$NGX_ADDON_DEPS \
$ngx_addon_dir/ngx_client.h \
$ngx_addon_dir/ngx_http_client.h \
"
CFLAGS="$CFLAGS -I $ngx_addon_dir"

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,165 @@
/*
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
*/
#ifndef _NGX_CLIENT_H_INCLUDED_
#define _NGX_CLIENT_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_http.h>
typedef struct ngx_client_session_s ngx_client_session_t;
typedef void (* ngx_client_connect_pt)(ngx_client_session_t *s);
typedef void (* ngx_client_recv_pt)(ngx_client_session_t *s);
typedef void (* ngx_client_send_pt)(ngx_client_session_t *s);
typedef void (* ngx_client_closed_pt)(ngx_client_session_t *s);
struct ngx_client_session_s {
ngx_peer_connection_t peer;
ngx_str_t server; /* server original address */
in_port_t port; /* server port */
ngx_connection_t *connection;
ngx_pool_t *pool;
ngx_log_t log;
void *data; /* save ctx for callback */
ngx_chain_t *out; /* save data unsend */
/* configured part */
/* timer for connecting to server */
ngx_msec_t connect_timeout;
/* timer for sending buffer full */
ngx_msec_t send_timeout;
/*
* data will be postponed until nginx has at least
* postpone_output bytes of data to send
*/
size_t postpone_output;
/* use dynamic resolver mechanism for resolving domain */
unsigned dynamic_resolver:1;
unsigned tcp_nodelay:1; /* TCP_NODELAY */
unsigned tcp_nopush:1; /* TCP_CORK */
/* runtime part */
size_t recv; /* client recv bytes */
unsigned connected:1; /* client connected to server */
unsigned closed:1; /* client has been closed */
ngx_event_t close; /* for async close */
/* callback */
ngx_client_connect_pt client_connected; /* connect successd */
ngx_client_recv_pt client_recv; /* recv msg from peer */
ngx_client_send_pt client_send; /* send msg to peer */
ngx_client_closed_pt client_closed; /* finalize connection */
};
/*
* create a client session
*
* return value:
* return client session for successd, return NULL for failed
* paras:
* peer: server address and port, address could be domain or ip
* local: set if need to bind local address, or set NULL
* udp: set 1, use udp, set 0, use tcp
* log: for logging error when create client session failed
*/
ngx_client_session_t *ngx_client_create(ngx_str_t *peer, ngx_str_t *local,
ngx_flag_t udp, ngx_log_t *log);
/*
* connect to client server, should use client session created by
* ngx_client_create. before connect to server, user can set paras in
* configured part.
*
* return value:
* void
* paras:
* s: client session created by ngx_client_create
*/
void ngx_client_connect(ngx_client_session_t *s);
/*
* send data to server
*
* return value:
* NGX_ERROR: write error, client session will be closed
* NGX_AGAIN: data not sent completely, it will save in client session out
* NGX_OK: data sent completely
* paras:
* s: client session
* out: data for sending
*/
ngx_int_t ngx_client_write(ngx_client_session_t *s, ngx_chain_t *out);
/*
* read data from server
*
* return value:
* NGX_ERROR: read error, client session will be closed
* NGX_DECLINED: buf for receiving data is full
* NGX_AGAIN: no data for reading
* 0: server closed
* >0: bytes read into buffer
* paras:
* s: client session
* b: buffer for receiving data
*/
ngx_int_t ngx_client_read(ngx_client_session_t *s, ngx_buf_t *b);
/*
* keepalive client connection, and destroy session
* if use client connect the same ip:port,
* new client session will reuse the connection
*
* return value:
* void
* paras:
* s: client session
*/
void ngx_client_set_keepalive(ngx_client_session_t *s);
/*
* close client session
*
* return value:
* void
* paras:
* s: client session
*/
void ngx_client_close(ngx_client_session_t *s);
/*
* paras:
* r: http request to query status of client
*/
ngx_chain_t *ngx_client_state(ngx_http_request_t *r, unsigned detail);
#endif

View File

@@ -0,0 +1,323 @@
/*
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
*/
#ifndef _NGX_HTTP_CLIENT_H_INCLUDE_
#define _NGX_HTTP_CLIENT_H_INCLUDE_
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include "ngx_client.h"
#include "ngx_toolkit_misc.h"
// http client method
#define NGX_HTTP_CLIENT_GET 0
#define NGX_HTTP_CLIENT_HEAD 1
#define NGX_HTTP_CLIENT_POST 2
#define NGX_HTTP_CLIENT_PUT 3
#define NGX_HTTP_CLIENT_DELETE 4
#define NGX_HTTP_CLIENT_MKCOL 5
#define NGX_HTTP_CLIENT_COPY 6
#define NGX_HTTP_CLIENT_MOVE 7
#define NGX_HTTP_CLIENT_OPTIONS 8
#define NGX_HTTP_CLIENT_PROPFIND 9
#define NGX_HTTP_CLIENT_PROPPATCH 10
#define NGX_HTTP_CLIENT_LOCK 11
#define NGX_HTTP_CLIENT_UNLOCK 12
#define NGX_HTTP_CLIENT_PATCH 13
#define NGX_HTTP_CLIENT_TRACE 14
// http client version
#define NGX_HTTP_CLIENT_VERSION_9 0
#define NGX_HTTP_CLIENT_VERSION_10 1
#define NGX_HTTP_CLIENT_VERSION_11 2
#define NGX_HTTP_CLIENT_VERSION_20 3
// http client opt
#define NGX_HTTP_CLIENT_OPT_CONNECT_TIMEOUT 0
#define NGX_HTTP_CLIENT_OPT_SEND_TIMEOUT 1
#define NGX_HTTP_CLIENT_OPT_POSTPONE_OUTPUT 2
#define NGX_HTTP_CLIENT_OPT_DYNAMIC_RESOLVER 3
#define NGX_HTTP_CLIENT_OPT_TCP_NODELAY 4
#define NGX_HTTP_CLIENT_OPT_TCP_NOPUSH 5
#define NGX_HTTP_CLIENT_OPT_HEADER_TIMEOUT 6
typedef void (* ngx_http_client_handler_pt)(void *r, ngx_http_request_t *hcr);
/* create and set http request */
/*
* create a http request for sending to server
*
* return value:
* return http request for successd, return NULL for failed
*
* paras:
* log: error in create will use this log
* method: http client method
* url: full request url like "http://test.com/index.html?hello=world"
* headers: http request header for sending
* send_body: callback for sending body
* request: who send http request
*/
ngx_http_request_t *ngx_http_client_create(ngx_log_t *log,
ngx_uint_t method, ngx_str_t *url, ngx_keyval_t *headers,
ngx_http_client_handler_pt send_body, void *request);
/*
* add cleanup as ngx_http_cleanup_add
*/
ngx_http_cleanup_t *ngx_http_client_cleanup_add(ngx_http_request_t *r,
size_t size);
/*
* set read handler for http client, should set before send request,
* otherwise body from server will discard
*
* return value:
* void
*
* paras:
* r: http client request
* read_handler: handler for setting
*/
void ngx_http_client_set_read_handler(ngx_http_request_t *r,
ngx_http_client_handler_pt read_handler);
/*
* set http headers
*
* return value:
* NGX_OK for successd, NGX_ERROR for failed
*
* paras:
* r: http client request
* headers: headers set into r
* if value is not null, will set or modify the header
* if value is null string, will delete the header
*/
ngx_int_t ngx_http_client_set_headers(ngx_http_request_t *r,
ngx_keyval_t *headers);
/*
* set write handler for http client, if set,
* will use this handler for sending body,
*
* return value:
* void
*
* paras:
* r: http client request
* write_handler: handler for setting
*/
void ngx_http_client_set_write_handler(ngx_http_request_t *r,
ngx_http_client_handler_pt write_handler);
/*
* set write handler for http client, if set,
* will use this handler for sending body,
*
* return value:
* void
*
* paras:
* r: http client request
* write_handler: handler for setting
*/
void ngx_http_client_set_version(ngx_http_request_t *r, ngx_uint_t version);
/*
* set http client option
*
* return value:
* void
*
* paras:
* r: http client request
* opt: http client opt
* NGX_HTTP_CLIENT_OPT_CONNECT_TIMEOUT:
* connect server timer
* NGX_HTTP_CLIENT_OPT_SEND_TIMEOUT:
* send data timer when buffer full
* NGX_HTTP_CLIENT_OPT_POSTPONE_OUTPUT:
* size threshold to send
* NGX_HTTP_CLIENT_OPT_DYNAMIC_RESOLVER:
* whether use dynamic resolver to resolv domain
* NGX_HTTP_CLIENT_OPT_TCP_NODELAY:
* whether set TCP_NODELAY
* NGX_HTTP_CLIENT_OPT_TCP_NOPUSH:
* whether set TCP_CORK
* NGX_HTTP_CLIENT_OPT_HEADER_TIMEOUT:
* timer for waiting response header from server
* value: http client opt value want to set
*/
void ngx_http_client_setopt(ngx_http_request_t *r, unsigned opt,
ngx_uint_t value);
/* send http request */
/*
* send http request
*
* return value:
* NGX_OK for successd, NGX_ERROR for failed
*
* paras:
* r: http request for seding, create by ngx_http_client_create
*/
ngx_int_t ngx_http_client_send(ngx_http_request_t *r);
/*
* create and send http GET request to server
*
* return value:
* return http request for successd, return NULL for failed
*
* paras:
* log: error in create will use this log
* url: full request url like "http://test.com/index.html?hello=world"
* headers: http request header for sending
* request: who send http request
*/
ngx_http_request_t *ngx_http_client_get(ngx_log_t *log, ngx_str_t *url,
ngx_keyval_t *headers, void *request);
/*
* create and send http HEAD request to server
*
* return value:
* return http request for successd, return NULL for failed
*
* paras:
* log: error in create will use this log
* url: full request url like "http://test.com/index.html?hello=world"
* headers: http request header for sending
* request: who send http request
*/
ngx_http_request_t *ngx_http_client_head(ngx_log_t *log, ngx_str_t *url,
ngx_keyval_t *headers, void *request);
/*
* create and send http POST request to server
*
* return value:
* return http request for successd, return NULL for failed
*
* paras:
* log: error in create will use this log
* url: full request url like "http://test.com/index.html?hello=world"
* headers: http request header for sending
* send_body: callback for sending body
* request: who send http request
*/
ngx_http_request_t *ngx_http_client_post(ngx_log_t *log, ngx_str_t *url,
ngx_keyval_t *headers, ngx_http_client_handler_pt send_body, void *request);
/* get response */
/*
* get http response version
*
* return value:
* http response version
*
* paras:
* r: http client request
*/
ngx_uint_t ngx_http_client_http_version(ngx_http_request_t *r);
/*
* get http response status code
*
* return value:
* http response status code like 200, 500
*
* paras:
* r: http client request
*/
ngx_uint_t ngx_http_client_status_code(ngx_http_request_t *r);
/*
* get http response header's value
*
* return value:
* http response header's value
*
* paras:
* r: http client request
* key: http header like "Host", "Content-Type"
*/
ngx_str_t *ngx_http_client_header_in(ngx_http_request_t *r, ngx_str_t *key);
/*
* read http response body
*
* return value:
* NGX_AGAIN: read part of data
* 0: tcp connection disconnect, need finalize request with 1
* NGX_ERROR: tcp connection error disconnect, need finalize request with 1
* NGX_DONE: response body has been read, could finalize request with 0
*
* paras:
* r: http client request
* in: where read data put
*/
ngx_int_t ngx_http_client_read_body(ngx_http_request_t *r, ngx_chain_t **in);
/*
* get receive bytes
*
* return value:
* bytes receive from server
*
* paras:
* r: http client request
*/
off_t ngx_http_client_rbytes(ngx_http_request_t *r);
/*
* get send bytes
*
* return value:
* bytes send to server
*
* paras:
* r: http client request
*/
off_t ngx_http_client_wbytes(ngx_http_request_t *r);
/* end request */
/*
* detach http client request with it's creator,
* all read and write handler will not be triggered
*
* return value:
* bytes send to server
*
* paras:
* r: http client request
*/
void ngx_http_client_detach(ngx_http_request_t *r);
/*
* finalize http client request
*
* return value:
* void
*
* paras:
* r: http client request
* closed: set to 1, will close connection to server
* set to 0, will keep connection to server alive
*/
void ngx_http_client_finalize_request(ngx_http_request_t *r, ngx_flag_t closed);
#endif

View File

@@ -0,0 +1,13 @@
ngx_addon_name=ngx_client_test_module
HTTP_MODULES="$HTTP_MODULES \
ngx_client_test_module \
ngx_http_client_test_module \
ngx_client_stat_module \
"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS
$ngx_addon_dir/ngx_client_test_module.c \
$ngx_addon_dir/ngx_http_client_test_module.c \
$ngx_addon_dir/ngx_client_stat_module.c \
"

View File

@@ -0,0 +1,57 @@
user root;
worker_processes 4;
#error_log logs/error.log;
#error_log logs/error.log notice;
error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
resolver 192.168.84.254;
dynamic_refresh_interval 5m;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
location /client_test {
client_test;
}
location /http_client_test {
http_client_test;
}
location /client_stat {
client_stat;
}
}
}

View File

@@ -0,0 +1,148 @@
/*
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_http.h>
#include "ngx_client.h"
#include "ngx_rbuf.h"
#include "ngx_poold.h"
#include "ngx_timerd.h"
#include "ngx_event_timer_module.h"
#include "ngx_event_resolver.h"
#include "ngx_dynamic_resolver.h"
static char *ngx_client_stat(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_command_t ngx_client_stat_commands[] = {
{ ngx_string("client_stat"),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_client_stat,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_client_stat_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t ngx_client_stat_module = {
NGX_MODULE_V1,
&ngx_client_stat_module_ctx, /* module context */
ngx_client_stat_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_int_t
ngx_client_stat_handler(ngx_http_request_t *r)
{
ngx_chain_t **ll, *out;
ngx_buf_t *b;
size_t len;
r->headers_out.status = NGX_HTTP_OK;
ngx_http_send_header(r);
ll = &out;
len = sizeof("--------------------------------------------------\n") - 1
+ sizeof("ngx_worker: ngx_process_slot: pid: \n") - 1
+ 3 * NGX_OFF_T_LEN;
*ll = ngx_alloc_chain_link(r->pool);
if (*ll == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
(*ll)->next = NULL;
b = ngx_create_temp_buf(r->pool, len);
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
(*ll)->buf = b;
b->last = ngx_snprintf(b->last, len,
"--------------------------------------------------\n"
"ngx_worker: %i ngx_process_slot: %i pid: %i\n",
ngx_worker, ngx_process_slot, ngx_pid);
if (*ll) {
ll = &(*ll)->next;
}
*ll = ngx_rbuf_state(r, 1);
if (*ll) {
ll = &(*ll)->next;
}
*ll = ngx_event_timer_state(r);
if (*ll) {
ll = &(*ll)->next;
}
*ll = ngx_event_resolver_state(r);
if (*ll) {
ll = &(*ll)->next;
}
*ll = ngx_dynamic_resolver_state(r);
if (*ll) {
ll = &(*ll)->next;
}
*ll = ngx_poold_state(r, 1);
if (*ll) {
ll = &(*ll)->next;
}
*ll = ngx_timerd_state(r, 1);
if (*ll) {
ll = &(*ll)->next;
}
*ll = ngx_client_state(r, 1);
(*ll)->buf->last_buf = 1;
return ngx_http_output_filter(r, out);
}
static char *
ngx_client_stat(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf;
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_client_stat_handler;
return NGX_CONF_OK;
}

View File

@@ -0,0 +1,188 @@
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include "ngx_client.h"
static char *ngx_client_test(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_command_t ngx_client_test_commands[] = {
{ ngx_string("client_test"),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_client_test,
0,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_client_test_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t ngx_client_test_module = {
NGX_MODULE_V1,
&ngx_client_test_module_ctx, /* module context */
ngx_client_test_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static void
ngx_client_test_connected(ngx_client_session_t *s)
{
ngx_buf_t *b;
size_t len;
ngx_chain_t out;
ngx_http_request_t *r;
ngx_event_t *wev;
ngx_log_error(NGX_LOG_ERR, &s->log, 0, "client connected");
r = s->data;
wev = s->peer.connection->write;
len = sizeof("nginx client test\n") - 1;
b = ngx_create_temp_buf(s->pool, len);
if (b == NULL) {
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
b->last = ngx_copy(b->last, "nginx client test\n", len);
b->last_buf = 1;
out.buf = b;
out.next = NULL;
ngx_client_write(s, &out);
ngx_handle_write_event(wev, 0);
}
static void
ngx_client_test_recv(ngx_client_session_t *s)
{
ngx_buf_t *b;
ngx_int_t n;
ngx_connection_t *c;
ngx_str_t recv;
ngx_http_request_t *r;
c = s->peer.connection;
r = s->data;
b = ngx_create_temp_buf(s->pool, 4096);
if (b == NULL) {
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
n = c->recv(c, b->pos, b->end - b->last);
if (n == NGX_AGAIN) {
ngx_log_error(NGX_LOG_ERR, &s->log, 0, "client recv NGX_AGAIN");
return;
}
if (n == NGX_ERROR || n == 0) {
ngx_log_error(NGX_LOG_ERR, &s->log, 0, "client recv NGX_ERROR");
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
ngx_client_close(s);
return;
}
b->last += n;
recv.data = b->pos;
recv.len = b->last - b->pos;
ngx_log_error(NGX_LOG_ERR, &s->log, 0, "client recv %d: %V, %z",
n, &recv, recv.len);
ngx_http_finalize_request(r, NGX_HTTP_FORBIDDEN);
ngx_client_set_keepalive(s);
return;
}
static void
ngx_client_test_send(ngx_client_session_t *s)
{
ngx_log_error(NGX_LOG_ERR, &s->log, 0, "client send");
}
static void
ngx_client_test_closed(ngx_client_session_t *s)
{
ngx_log_error(NGX_LOG_ERR, &s->log, 0, "client closed");
}
static ngx_int_t
ngx_client_test_handler(ngx_http_request_t *r)
{
ngx_client_session_t *s;
ngx_str_t echo;
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "client test handler");
if (ngx_http_arg(r, (u_char *) "echo", sizeof("echo") - 1, &echo)
!= NGX_OK)
{
return NGX_HTTP_BAD_REQUEST;
}
s = ngx_client_create(&echo, NULL, 0, r->connection->log);
if (s == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
//ci->dynamic_resolver = 0;
//ci->recvbuf = 4096;
s->client_connected = ngx_client_test_connected;
s->client_recv = ngx_client_test_recv;
s->client_send = ngx_client_test_send;
s->client_closed = ngx_client_test_closed;
s->data = r;
ngx_client_connect(s);
++r->count;
return NGX_DONE;
}
static char *
ngx_client_test(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf;
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_client_test_handler;
return NGX_CONF_OK;
}

View File

@@ -0,0 +1,203 @@
/*
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
*/
#include "ngx_http_client.h"
#include "ngx_rbuf.h"
static char *ngx_http_client_test(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_command_t ngx_http_client_test_commands[] = {
{ ngx_string("http_client_test"),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_http_client_test,
0,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_client_test_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t ngx_http_client_test_module = {
NGX_MODULE_V1,
&ngx_http_client_test_module_ctx, /* module context */
ngx_http_client_test_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static void
ngx_http_client_test_recv_body(void *request, ngx_http_request_t *hcr)
{
ngx_http_request_t *r;
ngx_chain_t *cl = NULL;
ngx_chain_t **ll;
ngx_int_t rc;
r = request;
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"http client test recv body");
rc = ngx_http_client_read_body(hcr, &cl);
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"http client test recv body, rc %i %i, %O",
rc, ngx_errno, ngx_http_client_rbytes(hcr));
if (rc == 0) {
goto done;
}
if (rc == NGX_ERROR) {
goto done;
}
if (rc == NGX_DONE) {
for (ll = &cl; (*ll)->next; ll = &(*ll)->next);
(*ll)->buf->last_buf = 1;
}
ngx_http_output_filter(r, cl);
ngx_http_run_posted_requests(r->connection);
if (rc == NGX_AGAIN) {
return;
}
if (rc == NGX_DONE) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"all body has been read");
ngx_http_client_finalize_request(hcr, 0);
}
done:
ngx_http_finalize_request(r, NGX_OK);
}
static void
ngx_http_client_test_recv(void *request, ngx_http_request_t *hcr)
{
ngx_http_request_t *r;
static ngx_str_t content_type = ngx_string("Content-Type");
static ngx_str_t connection = ngx_string("Connection");
static ngx_str_t unknown = ngx_string("Unknown");
ngx_str_t *ct, *con;
r = request;
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"http client test recv, connection: %p", hcr->connection);
r->headers_out.status = 200;
ngx_http_client_set_read_handler(hcr, ngx_http_client_test_recv_body);
ngx_http_send_header(r);
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"status_code: %ui http_version: %ui",
ngx_http_client_status_code(hcr),
ngx_http_client_http_version(hcr));
ct = ngx_http_client_header_in(hcr, &content_type);
con = ngx_http_client_header_in(hcr, &connection);
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "Content-Type: %V", ct);
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "Connection: %V", con);
if (ngx_http_client_header_in(hcr, &unknown) == NULL) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "no header Unknown");
}
ngx_http_client_test_recv_body(request, hcr);
}
static ngx_int_t
ngx_http_client_test_handler(ngx_http_request_t *r)
{
ngx_http_request_t *hcr;
static ngx_str_t request_url = ngx_string("http://101.200.241.232/");
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"http client test handler");
// Default header Host, User-Agent, Connection(below HTTP/1.1), Accept, Date
hcr = ngx_http_client_create(r->connection->log, NGX_HTTP_CLIENT_GET,
&request_url, NULL, NULL, r);
// add Connection, delete Date, Modify Host, add new header
ngx_str_t value;
value.data = (u_char *) "World";
value.len = sizeof("World") - 1;
ngx_keyval_t headers[] = {
{ ngx_string("Host"), ngx_string("www.test.com") },
{ ngx_string("Connection"), ngx_string("upgrade") },
{ ngx_string("Date"), ngx_null_string },
{ ngx_string("Hello"), value },
{ ngx_null_string, ngx_null_string } // must end with null str
};
ngx_http_client_set_headers(hcr, headers);
ngx_http_client_set_read_handler(hcr, ngx_http_client_test_recv);
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"http client test before send");
ngx_http_client_send(hcr);
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"http client test after send");
// ngx_http_client_detach(hcr);
// return NGX_HTTP_FORBIDDEN;
++r->count;
return NGX_DONE;
}
static char *
ngx_http_client_test(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf;
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_http_client_test_handler;
return NGX_CONF_OK;
}

View File

@@ -0,0 +1,41 @@
package main
import (
"fmt"
"net"
)
func handleConnection(c net.Conn) {
b := make([]byte, 4096)
for {
n, err := c.Read(b)
if err != nil {
fmt.Print("Read Error ", err)
c.Close()
return
}
fmt.Print("recv ", n, " data:", string(b))
c.Write(b[0:n])
}
}
func main() {
ln, err := net.Listen("tcp", ":10000")
if err != nil {
fmt.Print("Listen Error ", err)
return
}
for {
conn, err := ln.Accept()
if err != nil {
fmt.Print("Accept Error ", err)
continue
}
go handleConnection(conn)
}
}

View File

@@ -0,0 +1,24 @@
Copyright (C) 2016-2020, by Jie Wu "AlexWoo" <wj19840501@gmail.com>.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

View File

@@ -0,0 +1,181 @@
# Module nginx-multiport-module
---
## Instructions
Every worker process can bind own port, user can visit specific worker process by using the port.
- [ngx-stream-zone-module](doc/ngx-stream-zone-module.md)
Record stream's owner worker process slot
- [ngx-http-broadcast-module](doc/ngx-http-broadcast-module.md)
Broadcast HTTP request to all worker processes when receive HTTP request
## Directives
### multi\_listen
Syntax : multi_listen multiport relationport;
Default : None;
Context : events
multiport can configured as below:
address:port
port
unix:path
when configured with IPv4 or IPv6 port, worker process listen port plus with worker process's slot. For Example, we start four workers, add configured multiport with 9000. worker 0 will listen 9000, worker 1 will listen 9001, worker 2 will listen 9002, worker 3 will listen 9003
when configured with unix path, worker will listen path plus with suffix of worker process's slot. For Example, we start four workers, add configured multiport with unix:/tmp/http. worker 0 will listen /tmp/http.0, worker 1 will listen /tmp/http.1, worker 2 will listen /tmp/http.2, worker 3 will listen /tmp/http.3
relationport must configured same as listen directives in http server, rtmp server, stream server or other server
### inner\_proxy
Syntax : inner_proxy multiport uri;
Default : None;
Context : http, server, location
- multiport: configured in multi_listen
- uri: uri for inner_proxy, configured as below
location /multiport_test/ {
inner_proxy unix:/tmp/http.sock.80 /inner_proxy;
multiport_test;
}
location /inner_proxy/ {
rewrite ^/inner_proxy/(.*):/(.*) /$2 break;
proxy_pass http://$1:;
}
As example above, if send subrequest to process whose workerid is 0, the uri will change to /inner_proxy/unix:/tmp/http.sock.80.0:/multiport_test/xxx
proxy_pass will send current request to process 0 as inner proxy request.
## API
- ngx\_multiport\_get\_port
ngx_int_t ngx_event_multiport_get_port(ngx_pool_t *pool, ngx_str_t *port, ngx_str_t *multiport, ngx_int_t pslot);
- para:
pool: pool for port memory alloc
port: process real listen port while process\_slot is pslot
multiport: port configure for processes, format as below:
port only: port
IPv4: host:port host must be ipaddr of IPv4 or *
IPv6: [host]:port host must be ipaddr of IPv6
Unix: unix:/path
pslot: process\_slot, process\_slot of other worker process can get through ngx\_process\_slot\_get\_slot
- return value:
NGX\_OK for successd, NGX\_ERROR for failed
- ngx\_multiport\_get\_slot
ngx_int_t ngx_multiport_get_slot(ngx_uint_t wpid);
- para:
wpid: worker process id, 0 to ccf->worker_processes - 1
- return value:
ngx_process_slot for successd, NGX_ERROR for failed
- ngx\_http\_inner\_proxy\_request
ngx_int_t ngx_http_inner_proxy_request(ngx_http_request_t *r, ngx_int_t pslot);
send a inner proxy request to specific process, must use with directives inner\_proxy
- paras:
- r: http request for send inner request to sibling worker
- pslot: sibling worker ngx_process_slot
- return values:
- NGX_OK: for successd
- NGX_ERROR: for failed
- NGX_DECLINED: for not configured or send inner proxy to self
## Build
cd to NGINX source directory & run this:
./configure --add-module=/path/to/nginx-multiport-module/
make && make install
## Example
See t/ngx\_http\_process\_slot\_test\_module.c as reference
**Build**:
./configure --with-debug --with-ipv6 --add-module=/path/to/nginx-multiport-module/t/ --add-module=/path/to/nginx-multiport-module/ --add-module=/path/to/echo-nginx-module/
make && make install
**Configure**:
worker_processes 4;
events {
...
multi_listen 9000 80;
multi_listen unix:/tmp/http.sock.80 80;
}
http {
...
server {
...
location /multiport_test/ {
inner_proxy unix:/tmp/http.sock.80 /inner_proxy;
multiport_test;
}
location /inner_proxy/ {
rewrite ^/inner_proxy/(.*):/(.*) /$2 break;
proxy_pass http://$1:;
}
}
}
**Test for API**:
$ curl http://192.168.84.254/multiport_test/123
TEST cases 19, 19 pass
If request send to worker1 to worker3, the request will proxy to worker 0. will get log as below:
2017/10/14 20:45:44 [error] 20065#0: *6 multiport test handler, client: 192.168.84.1, server: localhost, request: "GET /multiport_test/123 HTTP/1.1", host: "192.168.84.254:9003"
2017/10/14 20:45:44 [error] 20065#0: *6 inner proxy return 0, client: 192.168.84.1, server: localhost, request: "GET /multiport_test/123 HTTP/1.1", host: "192.168.84.254:9003"
2017/10/14 20:45:44 [error] 20062#0: *8 multiport test handler, client: unix:, server: localhost, request: "GET //multiport_test/123 HTTP/1.0", host: "localhost"
**Test for multiport**:
curl -v http://127.0.0.1/
curl -v http://127.0.0.1:9000/
curl -v http://127.0.0.1:9001/
curl -v http://127.0.0.1:9002/
curl -v http://127.0.0.1:9003/
curl -v --unix-socket /tmp/http.sock.80.0 http:/
curl -v --unix-socket /tmp/http.sock.80.1 http:/
curl -v --unix-socket /tmp/http.sock.80.2 http:/
curl -v --unix-socket /tmp/http.sock.80.3 http:/
Tests will get the same result, for port 9000 will always send to worker process 0, 9001 to worker process 1 and so on

View File

@@ -0,0 +1,31 @@
ngx_addon_name=ngx_multiport_module
EVENT_MODULES="$EVENT_MODULES \
ngx_event_multiport_module \
"
CORE_MODULES="$CORE_MODULES \
ngx_process_slot_module \
ngx_stream_zone_module \
"
HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES \
ngx_http_broadcast_module \
ngx_http_inner_proxy_module \
"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS \
$ngx_addon_dir/ngx_multiport_misc.c \
$ngx_addon_dir/ngx_event_multiport_module.c \
$ngx_addon_dir/ngx_process_slot_module.c \
$ngx_addon_dir/ngx_stream_zone_module.c \
$ngx_addon_dir/ngx_http_broadcast_module.c \
$ngx_addon_dir/ngx_http_inner_proxy_module.c \
"
NGX_ADDON_DEPS="$NGX_ADDON_DEPS \
$ngx_addon_dir/ngx_multiport.h \
$ngx_addon_dir/ngx_stream_zone_module.h \
"
CFLAGS="$CFLAGS -I $ngx_addon_dir"

View File

@@ -0,0 +1,77 @@
# ngx-http-broadcast-module
---
## Instructions
Broadcast HTTP request to all worker processes when receive HTTP request
## Directives
### broadcast
Syntax : broadcast multiport uri;
Default : None;
Context : location
- multiport is multi_listen port configured in event
- uri is http proxy_pass uri configured as below
location /auth_proxy/ {
rewrite ^/auth_proxy/(.*) /auth break;
proxy_pass http://$1:;
}
## Build
cd to NGINX source directory & run this:
./configure --add-module=/path/to/nginx-multiport-module/
make && make install
## Example
**Build**:
./configure --with-debug --with-ipv6 --add-module=/path/to/nginx-multiport-module/t/ --add-module=/path/to/nginx-multiport-module/ --add-module=/path/to/echo-nginx-module/
make && make install
**Configure**:
events {
...
multi_listen unix:/tmp/http.sock.80 80;
}
http {
...
server {
listen 80;
server_name localhost;
...
location / {
broadcast unix:/tmp/http.sock.80 /auth_proxy;
}
location /auth_proxy/ {
rewrite ^/auth_proxy/(.*) /auth break;
proxy_pass http://$1:;
}
location /auth {
# return 403;
echo "auth";
echo $scheme://$host$uri?$args;
}
}
}
**Test**:
curl -v 'http://192.168.84.254/aa?a=b&c=d'
curl will get all response content if worker not return non 200 response

View File

@@ -0,0 +1,92 @@
# ngx-stream-zone-module
---
## Instructions
Record stream's owner worker process slot
## Directives
### stream\_zone
Syntax : stream_zone buckets=$nbuckets streams=$nstreams;
Default : None;
Context : main
nbuckets is hash buckect number, nstreams is max streams system can store
nbuckets is recommended use a prime number
## API
**header file**
For using this API, You should include the header file as below:
#include "ngx_stream_zone_module.h"
**ngx\_stream\_zone\_insert\_stream**
ngx_int_t ngx_stream_zone_insert_stream(ngx_str_t *name);
- para:
name: stream name
- return value:
process\_slot for owner of stream, NGX\_ERROR for error
**ngx\_stream\_zone\_delete\_stream**
void ngx_stream_zone_delete_stream(ngx_str_t *name);
- para:
name: stream name
**ngx\_stream\_zone\_state**
ngx_chain_t *ngx_stream_zone_state(ngx_http_request_t *r, ngx_flag_t detail);
- para:
- r: http request to query status of rbuf
- detail: print stream detail in log
- return value:
chain of stream zone state for returning to http client
## Build
cd to NGINX source directory & run this:
./configure --add-module=/path/to/nginx-multiport-module/
make && make install
## Example
See t/ngx\_stream\_zone\_test\_module.c as reference
**Build**:
./configure --with-debug --with-ipv6 --add-module=/path/to/nginx-multiport-module/t/ --add-module=/path/to/nginx-multiport-module/
make && make install
**Configure**:
stream_zone buckets=10007 streams=10000;
**Test**:
curl -XPOST -v "http://127.0.0.1:9001/stream_zone_test/ab?stream=test"
curl -XPOST -v "http://127.0.0.1:9002/stream_zone_test/ab?stream=test1"
curl -XPOST -v "http://127.0.0.1:9003/stream_zone_test/ab?stream=test2"
curl -XPOST -v "http://127.0.0.1:9003/stream_zone_test/ab?stream=test"
curl -XDELETE -v "http://127.0.0.1:9000/stream_zone_test/ab?stream=test3"
curl -XDELETE -v "http://127.0.0.1:9002/stream_zone_test/ab?stream=test1"
curl -XDELETE -v "http://127.0.0.1:9001/stream_zone_test/ab?stream=test2"
curl -XGET -v "http://127.0.0.1:9001/stream_zone_test/ab"

View File

@@ -0,0 +1,263 @@
/*
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include "ngx_multiport.h"
typedef struct {
ngx_str_t multiport;
ngx_str_t uri;
} ngx_http_broadcast_conf_t;
typedef struct {
ngx_int_t workerid;
ngx_http_request_t *sr;
} ngx_http_broadcast_ctx_t;
static ngx_int_t ngx_http_broadcast_filter_init(ngx_conf_t *cf);
static void *ngx_http_broadcast_create_conf(ngx_conf_t *cf);
static char *ngx_http_broadcast_merge_conf(ngx_conf_t *cf,
void *parent, void *child);
static char *ngx_http_broadcast(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_command_t ngx_http_broadcast_commands[] = {
{ ngx_string("broadcast"),
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
ngx_http_broadcast,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_broadcast_module_ctx = {
NULL, /* preconfiguration */
ngx_http_broadcast_filter_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_broadcast_create_conf, /* create location configuration */
ngx_http_broadcast_merge_conf /* merge location configuration */
};
ngx_module_t ngx_http_broadcast_module = {
NGX_MODULE_V1,
&ngx_http_broadcast_module_ctx, /* module context */
ngx_http_broadcast_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
static void *
ngx_http_broadcast_create_conf(ngx_conf_t *cf)
{
ngx_http_broadcast_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_broadcast_conf_t));
if (conf == NULL) {
return NULL;
}
return conf;
}
static char *
ngx_http_broadcast_merge_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_broadcast_conf_t *prev = parent;
ngx_http_broadcast_conf_t *conf = child;
ngx_conf_merge_str_value(conf->multiport, prev->multiport, "");
ngx_conf_merge_str_value(conf->uri, prev->uri, "");
return NGX_CONF_OK;
}
static char *
ngx_http_broadcast(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_broadcast_conf_t *hbcf;
ngx_str_t *value;
hbcf = conf;
if (hbcf->multiport.data != NULL) {
return "is duplicate";
}
value = cf->args->elts;
hbcf->multiport = value[1];
hbcf->uri = value[2];
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_broadcast_header_filter(ngx_http_request_t *r)
{
ngx_http_broadcast_conf_t *hbcf;
hbcf = ngx_http_get_module_loc_conf(r, ngx_http_broadcast_module);
if (hbcf == NULL || hbcf->multiport.len == 0) { /* not configured */
goto next;
}
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"broadcast header filter, r:%p r->main:%p, %ui, %O",
r, r->main, r->headers_out.status, r->headers_out.content_length_n);
r->headers_out.status = NGX_HTTP_OK;
ngx_http_clear_content_length(r);
ngx_http_clear_accept_ranges(r);
next:
return ngx_http_next_header_filter(r);
}
static ngx_int_t
ngx_http_broadcast_send_subrequest(ngx_http_request_t *r, ngx_int_t pslot)
{
ngx_http_broadcast_conf_t *hbcf;
ngx_str_t uri;
ngx_str_t port;
ngx_http_request_t *sr;
ngx_int_t rc;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"broadcast send subrequest to %i", pslot);
hbcf = ngx_http_get_module_loc_conf(r, ngx_http_broadcast_module);
if (ngx_multiport_get_port(r->pool, &port, &hbcf->multiport, pslot)
== NGX_ERROR)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"broadcast get port error, %V %i", &hbcf->multiport, pslot);
return NGX_ERROR;
}
uri.len = hbcf->uri.len + 1 + port.len;
uri.data = ngx_pcalloc(r->pool, uri.len);
ngx_snprintf(uri.data, uri.len, "%V/%V", &hbcf->uri, &port);
rc = ngx_http_subrequest(r, &uri, &r->args, &sr, NULL, 0);
sr->method = r->method;
sr->method_name = r->method_name;
return rc;
}
static ngx_int_t
ngx_http_broadcast_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
ngx_http_broadcast_conf_t *hbcf;
ngx_http_broadcast_ctx_t *ctx;
ngx_core_conf_t *ccf;
ngx_int_t rc;
ngx_buf_t *b;
ngx_chain_t cl;
hbcf = ngx_http_get_module_loc_conf(r->main, ngx_http_broadcast_module);
if (hbcf == NULL || hbcf->multiport.len == 0) { /* not configured */
return ngx_http_next_body_filter(r, in);
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"broadcast body filter, r:%p r->main:%p", r, r->main);
if (r != r->main) { /* send subrequest */
if (r->headers_out.status != NGX_HTTP_OK) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"broadcast subrequest send non 200 response: %i",
r->headers_out.status);
return NGX_OK;
}
return ngx_http_next_body_filter(r, in);
}
ctx = ngx_http_get_module_ctx(r, ngx_http_broadcast_module);
if (ctx == NULL) {
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_broadcast_ctx_t));
if (ctx == NULL) {
return NGX_ERROR;
}
ngx_http_set_ctx(r, ctx, ngx_http_broadcast_module);
}
/* send to all process */
ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
ngx_core_module);
while (ctx->workerid < ccf->worker_processes) {
rc = ngx_http_broadcast_send_subrequest(r,
ngx_multiport_get_slot(ctx->workerid));
++ctx->workerid;
return rc;
}
b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
b->last_buf = 1;
cl.buf = b;
cl.next = NULL;
return ngx_http_next_body_filter(r, &cl);
}
static ngx_int_t
ngx_http_broadcast_filter_init(ngx_conf_t *cf)
{
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_broadcast_header_filter;
ngx_http_next_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = ngx_http_broadcast_body_filter;
return NGX_OK;
}

View File

@@ -0,0 +1,254 @@
/*
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include "ngx_multiport.h"
typedef struct {
ngx_str_t multiport;
ngx_str_t uri;
} ngx_http_inner_proxy_conf_t;
typedef struct {
ngx_str_t port;
ngx_flag_t last;
} ngx_http_inner_proxy_ctx_t;
static ngx_int_t ngx_http_inner_proxy_filter_init(ngx_conf_t *cf);
static void *ngx_http_inner_proxy_create_conf(ngx_conf_t *cf);
static char *ngx_http_inner_proxy_merge_conf(ngx_conf_t *cf,
void *parent, void *child);
static char *ngx_http_inner_proxy(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_command_t ngx_http_inner_proxy_commands[] = {
{ ngx_string("inner_proxy"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
ngx_http_inner_proxy,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_inner_proxy_module_ctx = {
NULL, /* preconfiguration */
ngx_http_inner_proxy_filter_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_inner_proxy_create_conf, /* create location configuration */
ngx_http_inner_proxy_merge_conf /* merge location configuration */
};
ngx_module_t ngx_http_inner_proxy_module = {
NGX_MODULE_V1,
&ngx_http_inner_proxy_module_ctx, /* module context */
ngx_http_inner_proxy_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
static void *
ngx_http_inner_proxy_create_conf(ngx_conf_t *cf)
{
ngx_http_inner_proxy_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_inner_proxy_conf_t));
if (conf == NULL) {
return NULL;
}
return conf;
}
static char *
ngx_http_inner_proxy_merge_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_inner_proxy_conf_t *prev = parent;
ngx_http_inner_proxy_conf_t *conf = child;
ngx_conf_merge_str_value(conf->multiport, prev->multiport, "");
ngx_conf_merge_str_value(conf->uri, prev->uri, "");
return NGX_CONF_OK;
}
static char *
ngx_http_inner_proxy(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_inner_proxy_conf_t *hipcf;
ngx_str_t *value;
hipcf = conf;
if (hipcf->multiport.data != NULL) {
return "is duplicate";
}
value = cf->args->elts;
hipcf->multiport = value[1];
hipcf->uri = value[2];
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_inner_proxy_header_filter(ngx_http_request_t *r)
{
ngx_http_inner_proxy_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r->main, ngx_http_inner_proxy_module);
if (ctx == NULL) { /* not configured */
return ngx_http_next_header_filter(r);
}
if (r == r->main) {
return NGX_OK;
}
r->main->headers_out = r->headers_out;
return ngx_http_next_header_filter(r->main);
}
static ngx_int_t
ngx_http_inner_proxy_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
ngx_http_inner_proxy_ctx_t *ctx;
ngx_chain_t *cl, l;
ngx_buf_t *b;
ctx = ngx_http_get_module_ctx(r->main, ngx_http_inner_proxy_module);
if (ctx == NULL) { /* not configured */
return ngx_http_next_body_filter(r, in);
}
if (r == r->main) {
if (ctx->last == 0) {
return NGX_OK;
}
b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
b->last_buf = 1;
l.buf = b;
l.next = NULL;
return ngx_http_next_body_filter(r, &l);
}
for (cl = in; cl; cl = cl->next) {
if (cl->buf->last_in_chain) {
ctx->last = 1;
}
}
return ngx_http_next_body_filter(r, in);
}
static ngx_int_t
ngx_http_inner_proxy_filter_init(ngx_conf_t *cf)
{
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_inner_proxy_header_filter;
ngx_http_next_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = ngx_http_inner_proxy_body_filter;
return NGX_OK;
}
ngx_int_t
ngx_http_inner_proxy_request(ngx_http_request_t *r, ngx_int_t pslot)
{
ngx_http_inner_proxy_conf_t *hipcf;
ngx_http_inner_proxy_ctx_t *ctx;
ngx_http_request_t *sr;
ngx_str_t uri;
ngx_int_t rc;
hipcf = ngx_http_get_module_loc_conf(r, ngx_http_inner_proxy_module);
if (hipcf == NULL || hipcf->multiport.len == 0) { /* not configured */
return NGX_DECLINED;
}
if (pslot == ngx_process_slot) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"inner proxy send request to self: %i", ngx_process_slot);
return NGX_DECLINED;
}
ctx = ngx_http_get_module_ctx(r, ngx_http_inner_proxy_module);
if (ctx) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"inner proxy has been called in this request");
return NGX_ERROR;
}
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_inner_proxy_ctx_t));
if (ctx == NULL) {
return NGX_ERROR;
}
ngx_http_set_ctx(r, ctx, ngx_http_inner_proxy_module);
if (ngx_multiport_get_port(r->pool, &ctx->port, &hipcf->multiport, pslot)
== NGX_ERROR)
{
return NGX_ERROR;
}
uri.len = hipcf->uri.len + 1 + ctx->port.len + 2 + r->uri.len;
uri.data = ngx_pcalloc(r->pool, uri.len);
ngx_snprintf(uri.data, uri.len, "%V/%V:/%V",
&hipcf->uri, &ctx->port, &r->uri);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"inner proxy send request to %V", &ctx->port);
rc = ngx_http_subrequest(r, &uri, &r->args, &sr, NULL, 0);
sr->method = r->method;
sr->method_name = r->method_name;
return rc;
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
*/
#ifndef _NGX_MULTIPORT_H_INCLUDED_
#define _NGX_MULTIPORT_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
/*
* return value:
* NGX_OK for success, NGX_ERROR for failed
* paras:
* pool: pool for port memory alloc
* port: process real listen port while process_slot is pslot
* multiport: port configure for processes, format as below:
* port only: port
* IPv4: host:port host must be ipaddr of IPv4 or *
* IPv6: [host]:port host must be ipaddr of IPv6
* Unix: unix:/path
* pslot: process_slot
*/
ngx_int_t ngx_multiport_get_port(ngx_pool_t *pool, ngx_str_t *port,
ngx_str_t *multiport, ngx_int_t pslot);
/*
* return value:
* ngx_process_slot for successd, NGX_ERROR for failed
* paras:
* wpid: worker process id, 0 to ccf->worker_processes - 1
*/
ngx_int_t ngx_multiport_get_slot(ngx_uint_t wpid);
/*
* return value:
* NGX_OK : for successd
* NGX_ERROR : for failed
* NGX_DECLINED: for not configured or send inner proxy to self
* paras:
* r : http request for send inner request to sibling worker
* pslot: sibling worker ngx_process_slot
*/
ngx_int_t ngx_http_inner_proxy_request(ngx_http_request_t *r, ngx_int_t pslot);
#endif

View File

@@ -0,0 +1,165 @@
#include <ngx_config.h>
#include <ngx_core.h>
static ngx_int_t
ngx_multiport_get_port_unix(ngx_pool_t *pool, ngx_str_t *port,
ngx_str_t *multiport, ngx_int_t pslot)
{
#if (NGX_HAVE_UNIX_DOMAIN)
u_char *p;
size_t len;
len = multiport->len + 5; /* unix:/path -> unix:/path.127\0 */
port->data = ngx_pcalloc(pool, len);
if (port->data == NULL) {
return NGX_ERROR;
}
p = ngx_snprintf(port->data, len, "%V.%i", multiport, pslot);
*p = 0;
port->len = p - port->data;
return NGX_OK;
#else
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
"the unix domain sockets not support");
return NGX_ERROR;
#endif
}
static ngx_int_t
ngx_multiport_get_port_inet6(ngx_pool_t *pool, ngx_str_t *port,
ngx_str_t *multiport, ngx_int_t pslot)
{
#if (NGX_HAVE_INET6)
u_char *p, *last;
ngx_str_t addr;
size_t len;
ngx_int_t n;
last = multiport->data + multiport->len;
p = ngx_strlchr(multiport->data, last, ']');
if (p == NULL) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "invalid INET6 host");
return NGX_ERROR;
}
++p;
if (p == last || *p != ':') {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "no INET6 port");
return NGX_ERROR;
}
++p;
addr.data = multiport->data;
addr.len = p - multiport->data;
len = last - p;
n = ngx_atoi(p, len);
/* 65408 + 127 = 65535, pslot in [0, 127] */
if (n < 1 || n + pslot > 65408) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "invalid INET6 port");
return NGX_ERROR;
}
n += pslot;
len = multiport->len + 3; /* [::]:1 -> [::]:128\0 */
port->data = ngx_pcalloc(pool, len);
if (port->data == NULL) {
return NGX_ERROR;
}
p = port->data;
p = ngx_snprintf(p, len, "%V%i", &addr, n);
port->len = p - port->data;
return NGX_OK;
#else
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
"the INET6 sockets not support");
return NGX_ERROR;
#endif
}
static ngx_int_t
ngx_multiport_get_port_inet(ngx_pool_t *pool, ngx_str_t *port,
ngx_str_t *multiport, ngx_int_t pslot)
{
u_char *p, *last;
ngx_str_t addr;
size_t len;
ngx_int_t n;
last = multiport->data + multiport->len;
p = ngx_strlchr(multiport->data, last, ':');
if (p == NULL) { /* port */
p = multiport->data;
addr.len = 0;
} else { /* host:port */
++p;
if (p == last) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "no port");
return NGX_ERROR;
}
addr.data = multiport->data;
addr.len = p - multiport->data;
}
len = last - p;
n = ngx_atoi(p, len);
/* 65408 + 127 = 65535, pslot in [0, 127] */
if (n < 1 || n + pslot > 65408) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "invalid port");
return NGX_ERROR;
}
n += pslot;
len = multiport->len + 3; /* 127.0.0.1:1 -> 127.0.0.1:128\0 */
port->data = ngx_pcalloc(pool, len);
if (port->data == NULL) {
return NGX_ERROR;
}
p = port->data;
if (addr.len == 0) {
p = ngx_snprintf(p, len, "%i", n);
} else {
p = ngx_snprintf(p, len, "%V%i", &addr, n);
}
port->len = p - port->data;
return NGX_OK;
}
ngx_int_t
ngx_multiport_get_port(ngx_pool_t *pool, ngx_str_t *port,
ngx_str_t *multiport, ngx_int_t pslot)
{
u_char *p;
size_t len;
p = multiport->data;
len = multiport->len;
if (pslot < 0 || pslot > 127) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "invalid pslot: %i",
pslot);
return NGX_ERROR;
}
if (len >= 5 && ngx_strncasecmp(p, (u_char *) "unix:", 5) == 0) {
return ngx_multiport_get_port_unix(pool, port, multiport, pslot);
}
if (len && p[0] == '[') {
return ngx_multiport_get_port_inet6(pool, port, multiport, pslot);
}
return ngx_multiport_get_port_inet(pool, port, multiport, pslot);
}

View File

@@ -0,0 +1,167 @@
/*
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
*/
#include <ngx_config.h>
#include <ngx_core.h>
static ngx_int_t ngx_process_slot_module_init(ngx_cycle_t *cycle);
static ngx_int_t ngx_process_slot_process_init(ngx_cycle_t *cycle);
static void ngx_process_slot_process_exit(ngx_cycle_t *cycle);
static void *ngx_process_slot_module_create_conf(ngx_cycle_t *cycle);
static char *ngx_process_slot_module_init_conf(ngx_cycle_t *cycle, void *conf);
#define MAX_PROCESSES 128
typedef struct {
ngx_atomic_int_t process_slot[MAX_PROCESSES];
} ngx_process_slot_ctx_t;
typedef struct {
ngx_process_slot_ctx_t *ctx;
} ngx_process_slot_conf_t;
static ngx_command_t ngx_process_slot_commands[] = {
ngx_null_command
};
static ngx_core_module_t ngx_process_slot_module_ctx = {
ngx_string("process_slot"),
ngx_process_slot_module_create_conf,
ngx_process_slot_module_init_conf
};
ngx_module_t ngx_process_slot_module = {
NGX_MODULE_V1,
&ngx_process_slot_module_ctx, /* module context */
ngx_process_slot_commands, /* module directives */
NGX_CORE_MODULE, /* module type */
NULL, /* init master */
ngx_process_slot_module_init, /* init module */
ngx_process_slot_process_init, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
ngx_process_slot_process_exit, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static void *
ngx_process_slot_module_create_conf(ngx_cycle_t *cycle)
{
ngx_process_slot_conf_t *pscf;
pscf = ngx_palloc(cycle->pool, sizeof(ngx_process_slot_conf_t));
if (pscf == NULL) {
return NULL;
}
return pscf;
}
static char *
ngx_process_slot_module_init_conf(ngx_cycle_t *cycle, void *conf)
{
return NGX_CONF_OK;
}
static ngx_int_t
ngx_process_slot_module_init(ngx_cycle_t *cycle)
{
ngx_process_slot_conf_t *pscf;
ngx_shm_t shm;
ngx_uint_t i;
pscf = (ngx_process_slot_conf_t *) ngx_get_conf(cycle->conf_ctx,
ngx_process_slot_module);
shm.size = sizeof(ngx_process_slot_ctx_t);
shm.name.len = sizeof("process_slot_zone") - 1;
shm.name.data = (u_char *) "process_slot_zone";
shm.log = cycle->log;
if (ngx_shm_alloc(&shm) != NGX_OK) {
return NGX_ERROR;
}
pscf->ctx = (ngx_process_slot_ctx_t *) shm.addr;
for (i = 0; i < MAX_PROCESSES; ++i) {
pscf->ctx->process_slot[i] = -1;
}
return NGX_OK;
}
static ngx_int_t
ngx_process_slot_process_init(ngx_cycle_t *cycle)
{
ngx_process_slot_conf_t *pscf;
ngx_process_slot_ctx_t *ctx;
if (ngx_process != NGX_PROCESS_WORKER) {
return NGX_OK;
}
pscf = (ngx_process_slot_conf_t *) ngx_get_conf(cycle->conf_ctx,
ngx_process_slot_module);
ctx = pscf->ctx;
for (;;) {
if (ngx_atomic_cmp_set((ngx_atomic_t *) &ctx->process_slot[ngx_worker],
(ngx_atomic_uint_t)ctx->process_slot[ngx_worker], ngx_process_slot))
{
break;
}
}
return NGX_OK;
}
static void
ngx_process_slot_process_exit(ngx_cycle_t *cycle)
{
ngx_process_slot_conf_t *pscf;
ngx_process_slot_ctx_t *ctx;
if (ngx_process != NGX_PROCESS_WORKER) {
return;
}
pscf = (ngx_process_slot_conf_t *) ngx_get_conf(cycle->conf_ctx,
ngx_process_slot_module);
ctx = pscf->ctx;
ngx_atomic_cmp_set((ngx_atomic_t *) &ctx->process_slot[ngx_worker],
(ngx_atomic_uint_t)ngx_process_slot, -1);
}
ngx_int_t
ngx_multiport_get_slot(ngx_uint_t wpid)
{
ngx_process_slot_conf_t *pscf;
ngx_process_slot_ctx_t *ctx;
ngx_core_conf_t *ccf;
ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
ngx_core_module);
if (wpid >= (ngx_uint_t)ccf->worker_processes) {
return -1;
}
pscf = (ngx_process_slot_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
ngx_process_slot_module);
ctx = pscf->ctx;
return ctx->process_slot[wpid];
}

View File

@@ -0,0 +1,551 @@
/*
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
typedef struct ngx_stream_zone_hash_s ngx_stream_zone_hash_t;
typedef struct ngx_stream_zone_node_s ngx_stream_zone_node_t;
typedef struct ngx_stream_zone_conf_s ngx_stream_zone_conf_t;
static ngx_int_t
ngx_stream_zone_init_process(ngx_cycle_t *cycle);
static void
ngx_stream_zone_exit_process(ngx_cycle_t *cycle);
static void *
ngx_stream_zone_create_conf(ngx_cycle_t *cf);
static char *
ngx_stream_zone_init_conf(ngx_cycle_t *cycle, void *conf);
static char *
ngx_stream_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char *
ngx_stream_zone_shm_init(ngx_shm_t *shm, ngx_stream_zone_conf_t *szcf,
ngx_cycle_t *cycle);
#define NAME_LEN 1024
static ngx_str_t stream_zone_key = ngx_string("stream_zone");
struct ngx_stream_zone_node_s {
u_char name[NAME_LEN];
ngx_int_t slot; /* process slot */
ngx_int_t idx;
ngx_int_t next; /* idx of stream node */
};
struct ngx_stream_zone_hash_s {
ngx_shmtx_t mutex;
ngx_shmtx_sh_t lock;
ngx_int_t node; /* idx of stream node */
};
struct ngx_stream_zone_conf_s {
ngx_int_t nbuckets;
ngx_int_t nstreams;
ngx_pool_t *pool;
ngx_shmtx_t *mutex;
ngx_shmtx_sh_t *lock;
ngx_stream_zone_hash_t *hash; /* hash in shm */
ngx_stream_zone_node_t *stream_node;/* node in shm */
ngx_int_t *free_node; /* free node chain */
ngx_int_t *alloc; /* node number in use*/
};
static ngx_command_t ngx_stream_zone_commands[] = {
{ ngx_string("stream_zone"),
NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE2,
ngx_stream_zone,
0,
0,
NULL },
ngx_null_command
};
static ngx_core_module_t ngx_stream_zone_module_ctx = {
ngx_string("rtmp_stream_zone"),
ngx_stream_zone_create_conf, /* create conf */
ngx_stream_zone_init_conf /* init conf */
};
ngx_module_t ngx_stream_zone_module = {
NGX_MODULE_V1,
&ngx_stream_zone_module_ctx, /* module context */
ngx_stream_zone_commands, /* module directives */
NGX_CORE_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
ngx_stream_zone_init_process, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
ngx_stream_zone_exit_process, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_stream_zone_node_t *
ngx_stream_zone_get_node(ngx_str_t *name, ngx_int_t pslot)
{
ngx_stream_zone_conf_t *szcf;
ngx_stream_zone_node_t *node;
szcf = (ngx_stream_zone_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
ngx_stream_zone_module);
ngx_shmtx_lock(szcf->mutex);
if (*szcf->free_node == -1) {
ngx_shmtx_unlock(szcf->mutex);
return NULL;
}
node = &szcf->stream_node[*szcf->free_node];
*szcf->free_node = node->next;
*ngx_copy(node->name, name->data, ngx_min(NAME_LEN - 1, name->len)) = '\0';
node->slot = pslot;
node->next = -1;
++*szcf->alloc;
ngx_shmtx_unlock(szcf->mutex);
return node;
}
static void
ngx_stream_zone_put_node(ngx_int_t idx)
{
ngx_stream_zone_conf_t *szcf;
ngx_stream_zone_node_t *node;
szcf = (ngx_stream_zone_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
ngx_stream_zone_module);
ngx_shmtx_lock(szcf->mutex);
node = &szcf->stream_node[idx];
node->next = *szcf->free_node;
*szcf->free_node = idx;
--*szcf->alloc;
ngx_shmtx_unlock(szcf->mutex);
}
static void *
ngx_stream_zone_create_conf(ngx_cycle_t *cf)
{
ngx_stream_zone_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_zone_conf_t));
if (conf == NULL) {
return NULL;
}
conf->nbuckets = NGX_CONF_UNSET;
conf->nstreams = NGX_CONF_UNSET;
conf->pool = ngx_create_pool(4096, cf->log);
return conf;
}
static char *
ngx_stream_zone_init_conf(ngx_cycle_t *cycle, void *conf)
{
size_t len;
ngx_shm_t shm;
ngx_stream_zone_conf_t *szcf = conf;
ngx_conf_init_value(szcf->nbuckets, 512);
ngx_conf_init_value(szcf->nstreams, 40960);
/* create shm zone */
len = sizeof(ngx_shmtx_t) + sizeof(ngx_shmtx_sh_t)
+ sizeof(ngx_stream_zone_hash_t) * szcf->nbuckets
+ sizeof(ngx_stream_zone_node_t) * szcf->nstreams
+ sizeof(ngx_int_t) + sizeof(ngx_int_t);
shm.size = len;
shm.name = stream_zone_key;
shm.log = cycle->log;
if (ngx_shm_alloc(&shm) != NGX_OK) {
return NGX_CONF_ERROR;
}
return ngx_stream_zone_shm_init(&shm, szcf, cycle);
}
static void
ngx_stream_zone_clear(ngx_cycle_t *cycle)
{
ngx_stream_zone_conf_t *szcf;
volatile ngx_int_t idx, cur, next;
szcf = (ngx_stream_zone_conf_t *) ngx_get_conf(cycle->conf_ctx,
ngx_stream_zone_module);
if (szcf->nbuckets <= 0 || szcf->nstreams <= 0) {
return;
}
for (idx = 0; idx < szcf->nbuckets; ++idx) {
ngx_shmtx_lock(&szcf->hash[idx].mutex);
cur = -1;
while (1) {
if (cur == -1) {
next = szcf->hash[idx].node;
} else {
next = szcf->stream_node[cur].next;
}
if (next == -1) {
break;
}
if (szcf->stream_node[next].slot == ngx_process_slot) {
if (cur == -1) {
szcf->hash[idx].node = szcf->stream_node[next].next;
} else {
szcf->stream_node[cur].next = szcf->stream_node[next].next;
}
ngx_stream_zone_put_node(next);
continue;
}
cur = next;
}
ngx_shmtx_unlock(&szcf->hash[idx].mutex);
}
}
static ngx_int_t
ngx_stream_zone_init_process(ngx_cycle_t *cycle)
{
ngx_stream_zone_clear(cycle);
return NGX_OK;
}
static void
ngx_stream_zone_exit_process(ngx_cycle_t *cycle)
{
ngx_stream_zone_clear(cycle);
}
static char *
ngx_stream_zone_shm_init(ngx_shm_t *shm, ngx_stream_zone_conf_t *szcf,
ngx_cycle_t *cycle)
{
u_char *p;
ngx_int_t i, next;
p = shm->addr;
szcf->mutex = (ngx_shmtx_t *) p;
p += sizeof(ngx_shmtx_t);
szcf->lock = (ngx_shmtx_sh_t *) p;
p += sizeof(ngx_shmtx_sh_t);
szcf->hash = (ngx_stream_zone_hash_t *) p;
p += sizeof(ngx_stream_zone_hash_t) * szcf->nbuckets;
szcf->stream_node = (ngx_stream_zone_node_t *) p;
p += sizeof(ngx_stream_zone_node_t) * szcf->nstreams;
szcf->free_node = (ngx_int_t *) p;
p += sizeof(ngx_int_t);
szcf->alloc = (ngx_int_t *) p;
/* init shm zone */
#if (NGX_HAVE_ATOMIC_OPS)
p = NULL;
#else
p = ngx_pnalloc(szcf->pool, cycle->lock_file.len
+ stream_zone_key.len);
if (p == NULL) {
return NGX_CONF_ERROR;
}
*ngx_sprintf(p, "%V%V", &cycle->lock_file, &stream_zone_key) = 0;
#endif
if (ngx_shmtx_create(szcf->mutex, szcf->lock, p) != NGX_OK) {
return NGX_CONF_ERROR;
}
for (i = 0; i < szcf->nbuckets; ++i) {
#if (NGX_HAVE_ATOMIC_OPS)
p = NULL;
#else
p = ngx_pnalloc(szcf->pool, cycle->lock_file.len + stream_zone_key.len
+ NGX_INT32_LEN);
if (p == NULL) {
return NGX_CONF_ERROR;
}
*ngx_sprintf(p, "%V%V%d", &cycle->lock_file,
&stream_zone_key, i) = 0;
#endif
if (ngx_shmtx_create(&szcf->hash[i].mutex, &szcf->hash[i].lock, p)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
szcf->hash[i].node = -1;
}
next = -1;
i = szcf->nstreams;
do {
--i;
szcf->stream_node[i].slot = -1;
szcf->stream_node[i].idx = i;
szcf->stream_node[i].next = next;
next = i;
} while (i);
*szcf->free_node = i;
*szcf->alloc = 0;
return NGX_CONF_OK;
}
static char *
ngx_stream_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_uint_t i;
ngx_str_t *value;
ngx_stream_zone_conf_t *szcf = conf;
value = cf->args->elts;
for (i = 1; i < cf->args->nelts; ++i) {
if (ngx_strncmp(value[i].data, "buckets=", 8) == 0) {
szcf->nbuckets = ngx_atoi(value[i].data + 8, value[i].len - 8);
if (szcf->nbuckets <= 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid buckets \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
continue;
}
if (ngx_strncmp(value[i].data, "streams=", 8) == 0) {
szcf->nstreams = ngx_atoi(value[i].data + 8, value[i].len - 8);
if (szcf->nstreams <= 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid streams \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
continue;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
ngx_int_t
ngx_stream_zone_insert_stream(ngx_str_t *name)
{
ngx_stream_zone_conf_t *szcf;
volatile ngx_uint_t idx;
volatile ngx_int_t i, pslot;
ngx_stream_zone_node_t *node;
szcf = (ngx_stream_zone_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
ngx_stream_zone_module);
if (szcf->nbuckets <= 0 || szcf->nstreams <= 0) {
return NGX_ERROR;
}
if (name->len >= NAME_LEN) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
"stream name(%V) too long", name);
return NGX_ERROR;
}
idx = ngx_hash_key(name->data, name->len) % szcf->nbuckets;
ngx_shmtx_lock(&szcf->hash[idx].mutex);
i = szcf->hash[idx].node;
pslot = -1;
while (i != -1) {
if (ngx_strlen(szcf->stream_node[i].name) == name->len
&& ngx_memcmp(szcf->stream_node[i].name, name->data, name->len)
== 0)
{
pslot = szcf->stream_node[i].slot;
break;
}
i = szcf->stream_node[i].next;
}
if (i == -1) { /* stream not in hash */
node = ngx_stream_zone_get_node(name, ngx_process_slot);
if (node == NULL) {
ngx_shmtx_unlock(&szcf->hash[idx].mutex);
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
"stream zone get node failed");
return NGX_ERROR;
}
node->slot = ngx_process_slot;
node->next = szcf->hash[idx].node;
szcf->hash[idx].node = node->idx;
pslot = ngx_process_slot;
}
ngx_shmtx_unlock(&szcf->hash[idx].mutex);
return pslot;
}
void
ngx_stream_zone_delete_stream(ngx_str_t *name)
{
ngx_stream_zone_conf_t *szcf;
volatile ngx_uint_t idx;
volatile ngx_int_t cur, next;
szcf = (ngx_stream_zone_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
ngx_stream_zone_module);
if (szcf->nbuckets <= 0 || szcf->nstreams <= 0) {
return;
}
idx = ngx_hash_key(name->data, name->len) % szcf->nbuckets;
ngx_shmtx_lock(&szcf->hash[idx].mutex);
cur = -1;
next = szcf->hash[idx].node;
while (next != -1) {
if (ngx_strlen(szcf->stream_node[next].name) == name->len
&& ngx_memcmp(szcf->stream_node[next].name, name->data, name->len)
== 0)
{
if (szcf->stream_node[next].slot != ngx_process_slot) {
break;
}
if (cur == -1) { /* link header */
szcf->hash[idx].node = szcf->stream_node[next].next;
} else {
szcf->stream_node[cur].next = szcf->stream_node[next].next;
}
ngx_stream_zone_put_node(next);
break;
}
cur = next;
next = szcf->stream_node[next].next;
}
ngx_shmtx_unlock(&szcf->hash[idx].mutex);
}
ngx_chain_t *
ngx_stream_zone_state(ngx_http_request_t *r, ngx_flag_t detail)
{
ngx_stream_zone_conf_t *szcf;
ngx_chain_t *cl;
ngx_buf_t *b;
size_t len;
volatile ngx_int_t idx, next;
szcf = (ngx_stream_zone_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
ngx_stream_zone_module);
if (szcf->nbuckets <= 0 || szcf->nstreams <= 0) {
return NULL;
}
len = sizeof("##########stream zone state##########\n") - 1
+ sizeof("ngx_stream_zone buckets: \n") - 1 + NGX_OFF_T_LEN
+ sizeof("ngx_stream_zone streams: \n") - 1 + NGX_OFF_T_LEN
+ sizeof("ngx_stream_zone alloc: \n") - 1 + NGX_OFF_T_LEN;
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NULL;
}
cl->next = NULL;
b = ngx_create_temp_buf(r->pool, len);
if (b == NULL) {
return NULL;
}
cl->buf = b;
b->last = ngx_snprintf(b->last, len,
"##########stream zone state##########\n"
"ngx_stream_zone buckets: %i\nngx_stream_zone streams: %i\n"
"ngx_stream_zone alloc: %i\n",
szcf->nbuckets, szcf->nstreams, *szcf->alloc);
if (detail) {
for (idx = 0; idx < szcf->nbuckets; ++idx) {
ngx_shmtx_lock(&szcf->hash[idx].mutex);
next = szcf->hash[idx].node;
if (next == -1) {
ngx_shmtx_unlock(&szcf->hash[idx].mutex);
continue;
}
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "slot: %i", idx);
while (next != -1) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"\t\tname:%s, slot:%i, idx:%i, next:%i",
szcf->stream_node[next].name,
szcf->stream_node[next].slot,
szcf->stream_node[next].idx,
szcf->stream_node[next].next);
next = szcf->stream_node[next].next;
}
ngx_shmtx_unlock(&szcf->hash[idx].mutex);
}
}
return cl;
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
*/
#ifndef _NGX_STREAM_ZONE_MODULE_H_INCLUDED_
#define _NGX_STREAM_ZONE_MODULE_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
/*
* return value:
* process_slot for owner of stream, NGX_ERROR for error
* name:
* stream name
*/
ngx_int_t ngx_stream_zone_insert_stream(ngx_str_t *name);
/*
* name:
* stream name
*/
void ngx_stream_zone_delete_stream(ngx_str_t *name);
/*
* return value:
* chain of stream zone state for returning to http client
* paras:
* r: http request to query status of rbuf
* detail: print stream detail in log
*/
ngx_chain_t *ngx_stream_zone_state(ngx_http_request_t *r, ngx_flag_t detail);
#endif

View File

@@ -0,0 +1,12 @@
ngx_addon_name=ngx_multiport_test_module
HTTP_MODULES="$HTTP_MODULES \
ngx_stream_zone_test_module \
ngx_multiport_test_module \
"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS
$ngx_addon_dir/ngx_stream_zone_test_module.c \
$ngx_addon_dir/ngx_multiport_test_module.c \
"

View File

@@ -0,0 +1,71 @@
user root;
worker_processes 4;
#error_log logs/error.log;
#error_log logs/error.log notice;
error_log logs/error.log info;
#pid logs/nginx.pid;
stream_zone buckets=10007 streams=10000;
events {
worker_connections 1024;
multi_listen 9000 80;
multi_listen unix:/tmp/http.sock.80 80;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 80;
server_name localhost;
location /stream_zone_test/ {
stream_zone_test;
}
location /multiport_test/ {
inner_proxy unix:/tmp/http.sock.80 /inner_proxy;
multiport_test;
}
location /inner_proxy/ {
rewrite ^/inner_proxy/(.*):/(.*) /$2 break;
proxy_pass http://$1:;
}
location / {
broadcast unix:/tmp/http.sock.80 /auth_proxy;
}
location /auth_proxy/ {
rewrite ^/auth_proxy/(.*) /auth break;
proxy_pass http://$1:;
}
location /auth {
# return 403;
echo "auth";
echo $scheme://$host$uri?$args;
}
}
}

View File

@@ -0,0 +1,168 @@
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include "ngx_multiport.h"
#include "ngx_test_macro.h"
static char *ngx_multiport_test(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_command_t ngx_multiport_test_commands[] = {
{ ngx_string("multiport_test"),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_multiport_test,
0,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_multiport_test_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t ngx_multiport_test_module = {
NGX_MODULE_V1,
&ngx_multiport_test_module_ctx, /* module context */
ngx_multiport_test_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
ngx_int_t
ngx_multiport_test_get_port(ngx_http_request_t *r, char *multiport,
ngx_int_t pslot, char *expect)
{
ngx_str_t port;
ngx_int_t rc;
ngx_str_t mp;
mp.data = (u_char *) multiport;
mp.len = ngx_strlen(multiport);
ngx_memzero(&port, sizeof(ngx_str_t));
rc = ngx_multiport_get_port(r->pool, &port, &mp, pslot);
if (port.len) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "port: %V, %s, %d",
&port, port.data, port.len);
}
if (expect == NULL && rc == NGX_ERROR) {
return 1;
} else {
return ngx_test_str(&port, expect);
}
return 0;
}
static ngx_int_t
ngx_multiport_test_handler(ngx_http_request_t *r)
{
ngx_buf_t *b;
ngx_chain_t cl;
size_t len;
ngx_int_t rc;
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"multiport test handler");
rc = ngx_http_inner_proxy_request(r, ngx_multiport_get_slot(0));
if (rc != NGX_DECLINED) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"inner proxy return %i", rc);
return rc;
}
NGX_TEST_INIT
/* test ngx_multiport_get_port */
/* normal format */
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "10", 127, "137"));
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "127.0.0.1:55635", 4,
"127.0.0.1:55639"));
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "[::127.0.0.1]:1024", 0,
"[::127.0.0.1]:1024"));
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "unix:/tmp.test", 7,
"unix:/tmp.test.7"));
/* inet6 format error */
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "[::127.0.0.1:1024", 0, NULL));
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "[::127.0.0.1]:abcd",
0, NULL));
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "[::127.0.0.1]:65409",
0, NULL));
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "[::127.0.0.1]:", 0, NULL));
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "[::127.0.0.1]", 0, NULL));
/* inet format error */
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "127.0.0.1:", 4, NULL));
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "abcd", 4, NULL));
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "65410", 4, NULL));
/* pslot error */
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "65410", -1, NULL));
NGX_TEST_ISOK(ngx_multiport_test_get_port(r, "65410", 128, NULL));
/* test ngx_multiport_get_slot */
NGX_TEST_ISOK(ngx_multiport_get_slot(4) == -1);
NGX_TEST_ISOK(ngx_multiport_get_slot(0) == 0);
NGX_TEST_ISOK(ngx_multiport_get_slot(1) == 1);
NGX_TEST_ISOK(ngx_multiport_get_slot(2) == 2);
NGX_TEST_ISOK(ngx_multiport_get_slot(3) == 3);
r->headers_out.status = NGX_HTTP_OK;
ngx_http_send_header(r);
len = sizeof("pslot: %i TEST cases 4294967296, 4294967296 pass\n") - 1;
b = ngx_create_temp_buf(r->pool, len);
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
b->last = ngx_snprintf(b->last, len, "pslot: %i TEST cases %d, %d pass\n",
ngx_process_slot, count, pass);
b->last_buf = 1;
b->last_in_chain = 1;
cl.buf = b;
cl.next = NULL;
return ngx_http_output_filter(r, &cl);
}
static char *
ngx_multiport_test(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf;
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_multiport_test_handler;
return NGX_CONF_OK;
}

View File

@@ -0,0 +1,134 @@
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include "ngx_stream_zone_module.h"
static char *ngx_stream_zone_test(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_command_t ngx_stream_zone_test_commands[] = {
{ ngx_string("stream_zone_test"),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_stream_zone_test,
0,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_stream_zone_test_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t ngx_stream_zone_test_module = {
NGX_MODULE_V1,
&ngx_stream_zone_test_module_ctx, /* module context */
ngx_stream_zone_test_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_int_t
ngx_stream_zone_test_handler(ngx_http_request_t *r)
{
ngx_chain_t cl, *out;
ngx_buf_t *b;
size_t len;
ngx_str_t stream;
ngx_int_t rc;
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "stream zone test handler");
rc = -1;
if (r->method == NGX_HTTP_GET) {
out = ngx_stream_zone_state(r, 1);
out->buf->last_buf = 1;
out->buf->last_in_chain = 1;
r->headers_out.status = NGX_HTTP_OK;
ngx_http_send_header(r);
return ngx_http_output_filter(r, out);
} else if (r->method == NGX_HTTP_DELETE) {
if (ngx_http_arg(r, (u_char *) "stream", 6, &stream) != NGX_OK) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"stream zone test, no stream in http args");
return NGX_HTTP_BAD_REQUEST;
}
len = sizeof("delete stream=\n") - 1 + stream.len;
ngx_stream_zone_delete_stream(&stream);
} else if (r->method == NGX_HTTP_POST) {
if (ngx_http_arg(r, (u_char *) "stream", 6, &stream) != NGX_OK) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"stream zone test, no stream in http args");
return NGX_HTTP_BAD_REQUEST;
}
len = sizeof("stream= in process:4294967296\n") - 1 + stream.len;
rc = ngx_stream_zone_insert_stream(&stream);
} else {
return NGX_HTTP_BAD_REQUEST;
}
r->headers_out.status = NGX_HTTP_OK;
ngx_http_send_header(r);
b = ngx_create_temp_buf(r->pool, len);
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (r->method == NGX_HTTP_DELETE) {
b->last = ngx_snprintf(b->last, len, "delete stream=%V\n", &stream);
} else {
b->last = ngx_snprintf(b->last, len,
"stream=%V in process:%i\n", &stream, rc);
}
b->last_buf = 1;
b->last_in_chain = 1;
cl.buf = b;
cl.next = NULL;
return ngx_http_output_filter(r, &cl);
}
static char *
ngx_stream_zone_test(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf;
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_stream_zone_test_handler;
return NGX_CONF_OK;
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
*/
#ifndef _NGX_TEST_MACRO_H_INCLUDE_
#define _NGX_TEST_MACRO_H_INCLUDE_
#include <ngx_config.h>
#include <ngx_core.h>
static ngx_int_t count = 0;
static ngx_int_t pass = 0;
#define NGX_TEST_INIT count = 0, pass = 0;
#define NGX_TEST_ISOK(testcase) \
{ \
ngx_int_t __ret = testcase; \
++count; \
if (__ret) ++pass; \
ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0, " TEST "#testcase"%s",\
(__ret ? " ...OK" : " ...ERROR")); \
}
#define NGX_TEST_INT(di, si) \
(di == si)
static ngx_inline ngx_int_t
ngx_test_str(ngx_str_t *nstr, char *cstr)
{
size_t len;
len = ngx_strlen(cstr);
return (nstr->len == len && ngx_memcmp(nstr->data, cstr, len) == 0);
}
#endif

View File

@@ -0,0 +1,9 @@
Project author:
Roman Arutyunyan
Moscow, Russia, Pingo
Contacts:
arut@qip.ru
arutyunyan.roman@gmail.com
cczjp89@gmail.com

Some files were not shown because too many files have changed in this diff Show More