Ready for v0.2.3
Signed-off-by: Izuru Yakumo <yakumo.izuru@chaotic.ninja> git-svn-id: file:///srv/svn/repo/yukari/trunk@149 f3bd38d9-da89-464d-a02a-eb04e43141b5
This commit is contained in:
parent
3720388827
commit
d1d65a0b21
12
CHANGELOG.md
12
CHANGELOG.md
@ -1,6 +1,14 @@
|
||||
# v0.2.3 - 2024.03.21
|
||||
* Document the configuration file format, which is INI-style (which is compatible to the old format in the codebase, though it's now called as `config.Config.<key>`)
|
||||
* Manual page has been rewritten (using `mdoc(7)`)
|
||||
* 'YukariSukima' is an incorrect transliteration, use 'Yukari no Sukima' to indicate possession/ownership
|
||||
* Remove the 'proxified and sanitized view' text as it should already be obvious
|
||||
* The font family used earlier is horrible, changed it to `sans-serif`
|
||||
* Bump required Go toolchain version to 1.16 in order to use `//go:embed`
|
||||
* Rename some all-uppercase constants/variables to camelCase (I think?), also rename CLIENT to Gap (lol)
|
||||
|
||||
# v0.2.1 - 2023.08.26
|
||||
Applied some suggestions from the issue tracker, and
|
||||
rebrand this fork.
|
||||
Applied some suggestions from the [issue tracker](https://github.com/asciimoo/morty/issues), and rebrand this fork.
|
||||
|
||||
# v0.2.0 - 2018.05.28
|
||||
|
||||
|
2
Makefile
2
Makefile
@ -22,7 +22,7 @@ yukari: vendor
|
||||
$(GO) build $(GOFLAGS) ./cmd/yukari
|
||||
clean:
|
||||
$(RM) -f yukari
|
||||
install:
|
||||
install: all
|
||||
$(MKDIR) -p $(DESTDIR)$(PREFIX)/$(BINDIR)
|
||||
$(MKDIR) -p $(DESTDIR)$(PREFIX)/$(MANDIR)/man1
|
||||
$(CP) -f yukari $(DESTDIR)$(PREFIX)/$(BINDIR)
|
||||
|
34
README.md
34
README.md
@ -19,41 +19,15 @@ Features:
|
||||
|
||||
|
||||
## Installation and setup
|
||||
Requirement: Go version 1.16 or higher.
|
||||
Requirement: Go version 1.16 or higher (thus making it incompatible with MortyProxy's own requirement, but also to use `go embed`)
|
||||
|
||||
```
|
||||
$ go install marisa.chaotic.ninja/yukari@latest
|
||||
$ go install marisa.chaotic.ninja/yukari/cmd/yukari@latest
|
||||
$ "$GOPATH/bin/yukari" --help
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
```
|
||||
-debug
|
||||
Debug mode (default true)
|
||||
-followredirect
|
||||
Follow HTTP GET redirect
|
||||
-hashparam string
|
||||
User-defined requesting string HASH parameter name (ie: '/?hash=...' or '/?h=...') (default "yukarihash")
|
||||
-ipv6
|
||||
Allow IPv6 HTTP requests (default true)
|
||||
-key string
|
||||
HMAC url validation key (base64 encoded) - leave blank to disable validation
|
||||
-listen string
|
||||
Listen address (default "127.0.0.1:3000")
|
||||
-proxy string
|
||||
Use the specified HTTP proxy (ie: '[user:pass@]hostname:port'). Overrides -socks5, -ipv6.
|
||||
-proxyenv
|
||||
Use a HTTP proxy as set in the environment (HTTP_PROXY, HTTPS_PROXY and NO_PROXY). Overrides -proxy, -socks5, -ipv6.
|
||||
-socks5 string
|
||||
Use a SOCKS5 proxy (ie: 'hostname:port'). Overrides -ipv6.
|
||||
-timeout uint
|
||||
Request timeout (default 5)
|
||||
-urlparam string
|
||||
User-defined requesting string URL parameter name (ie: '/?url=...' or '/?u=...') (default "yukariurl")
|
||||
-version
|
||||
Show version
|
||||
```
|
||||
See `yukari(1)`
|
||||
|
||||
### Environment variables
|
||||
|
||||
@ -81,4 +55,4 @@ $ go test -benchmem -bench .
|
||||
|
||||
|
||||
## Bugs
|
||||
Bugs or suggestions? Visit the [issue tracker](https://git.chaotic.ninja/yakumo.izuru/yukari/issues).
|
||||
Bugs or suggestions? Mail [yukari-dev@chaotic.ninja](mailto:yukari-dev@chaotic.ninja)
|
||||
|
@ -39,14 +39,14 @@ const (
|
||||
STATE_IN_NOSCRIPT int = 2
|
||||
)
|
||||
|
||||
const MAX_REDIRECT_COUNT = 5
|
||||
const MaxRedirectCount = 5
|
||||
|
||||
var CLIENT *fasthttp.Client = &fasthttp.Client{
|
||||
var Gap *fasthttp.Client = &fasthttp.Client{
|
||||
MaxResponseBodySize: 10 * 1024 * 1024, // 10M
|
||||
ReadBufferSize: 16 * 1024, // 16K
|
||||
}
|
||||
|
||||
var CSS_URL_REGEXP *regexp.Regexp = regexp.MustCompile("url\\((['\"]?)[ \\t\\f]*([\u0009\u0021\u0023-\u0026\u0028\u002a-\u007E]+)(['\"]?)\\)?")
|
||||
var cssURLRegex *regexp.Regexp = regexp.MustCompile("url\\((['\"]?)[ \\t\\f]*([\u0009\u0021\u0023-\u0026\u0028\u002a-\u007E]+)(['\"]?)\\)?")
|
||||
|
||||
type Proxy struct {
|
||||
Key []byte
|
||||
@ -76,36 +76,33 @@ type HTMLMainPageFormParam struct {
|
||||
URLParamName string
|
||||
}
|
||||
|
||||
var FAVICON_BYTES []byte
|
||||
var HTML_FORM_EXTENSION *template.Template
|
||||
var HTML_BODY_EXTENSION *template.Template
|
||||
var HTML_MAIN_PAGE_FORM *template.Template
|
||||
var faviconBytes []byte
|
||||
var htmlFormExtension *template.Template
|
||||
var htmlBodyExtension *template.Template
|
||||
var htmlMainPageForm *template.Template
|
||||
|
||||
//go:embed templates/yukari_content_type.html
|
||||
var HTML_HEAD_CONTENT_TYPE string
|
||||
var htmlHeadContentType string
|
||||
//go:embed templates/yukari_start.html
|
||||
var YUKARI_HTML_PAGE_START string
|
||||
var htmlPageStart string
|
||||
//go:embed templates/yukari_stop.html
|
||||
var YUKARI_HTML_PAGE_END string
|
||||
var htmlPageStop string
|
||||
|
||||
func init() {
|
||||
FaviconBase64 := "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABmJLR0T///////8JWPfcAAAACXBIWXMAAABIAAAASABGyWs+AAAAF0lEQVRIx2NgGAWjYBSMglEwCkbBSAcACBAAAeaR9cIAAAAASUVORK5CYII"
|
||||
|
||||
FAVICON_BYTES, _ = base64.StdEncoding.DecodeString(FaviconBase64)
|
||||
faviconBytes, _ = base64.StdEncoding.DecodeString(FaviconBase64)
|
||||
var err error
|
||||
HTML_FORM_EXTENSION, err = template.New("html_form_extension").Parse(
|
||||
htmlFormExtension, err = template.New("html_form_extension").Parse(
|
||||
`<input type="hidden" name="yukariurl" value="{{.BaseURL}}" />{{if .YukariHash}}<input type="hidden" name="yukarihash" value="{{.YukariHash}}" />{{end}}`)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
HTML_BODY_EXTENSION, err = template.New("html_body_extension").Parse(`
|
||||
<input type="checkbox" id="yukaritoggle" autocomplete="off" />
|
||||
htmlBodyExtension, err = template.New("html_body_extension").Parse(`
|
||||
<div id="yukariheader">
|
||||
<form method="get">
|
||||
<label for="yukaritoggle">hide</label>
|
||||
<span><a href="/">Yukari's Gap</a></span>
|
||||
<input type="url" value="{{.BaseURL}}" name="{{.URLParamName}}" {{if .HasYukariKey }}readonly="true"{{end}} />
|
||||
This is a proxified and sanitized view of the page, visit <a href="{{.BaseURL}}" rel="noreferrer">original site.
|
||||
</form>
|
||||
</div>
|
||||
<style>
|
||||
@ -116,15 +113,13 @@ body{ position: absolute !important; top: 42px !important; left: 0 !important; r
|
||||
#yukariheader a { color: #8934DB; font-weight: bold; display: inline; }
|
||||
#yukariheader label { text-align: right; cursor: pointer; position: fixed; right: 4px; top: 4px; display: block; color: #444; }
|
||||
#yukariheader > form > span { font-size: 24px; font-weight: bold; margin-right: 20px; margin-left: 20px; }
|
||||
input[type=checkbox]#yukaritoggle { display: none; }
|
||||
input[type=checkbox]#yukaritoggle:checked ~ div { display: none; visibility: hidden; }
|
||||
#yukariheader input[type=url] { width: 50%; padding: 4px; font-size: 16px; }
|
||||
</style>
|
||||
`)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
HTML_MAIN_PAGE_FORM, err = template.New("html_main_page_form").Parse(`
|
||||
htmlMainPageForm, err = template.New("html_main_page_form").Parse(`
|
||||
<form action="post">
|
||||
Visit url: <input placeholder="https://url.." name="{{.URLParamName}}" autofocus />
|
||||
<input type="submit" value="go" />
|
||||
@ -214,7 +209,7 @@ func (p *Proxy) ProcessUri(ctx *fasthttp.RequestCtx, requestURIStr string, redir
|
||||
req.SetBody(ctx.PostBody())
|
||||
}
|
||||
|
||||
err = CLIENT.DoTimeout(req, resp, p.RequestTimeout)
|
||||
err = Gap.DoTimeout(req, resp, p.RequestTimeout)
|
||||
|
||||
if err != nil {
|
||||
if err == fasthttp.ErrTimeout {
|
||||
@ -234,7 +229,7 @@ func (p *Proxy) ProcessUri(ctx *fasthttp.RequestCtx, requestURIStr string, redir
|
||||
if loc != nil {
|
||||
if p.FollowRedirect && ctx.IsGet() {
|
||||
// GET method: Yukari follows the redirect
|
||||
if redirectCount < MAX_REDIRECT_COUNT {
|
||||
if redirectCount < MaxRedirectCount {
|
||||
if config.Config.Debug {
|
||||
log.Println("follow redirect to", string(loc))
|
||||
}
|
||||
@ -344,7 +339,7 @@ func (p *Proxy) ProcessUri(ctx *fasthttp.RequestCtx, requestURIStr string, redir
|
||||
if len(rc.Key) > 0 {
|
||||
p.HasYukariKey = true
|
||||
}
|
||||
err := HTML_BODY_EXTENSION.Execute(ctx, p)
|
||||
err := htmlBodyExtension.Execute(ctx, p)
|
||||
if err != nil {
|
||||
if config.Config.Debug {
|
||||
fmt.Println("failed to inject body extension", err)
|
||||
@ -393,7 +388,7 @@ func appRequestHandler(ctx *fasthttp.RequestCtx) bool {
|
||||
// server favicon.ico
|
||||
if bytes.Equal(ctx.Path(), []byte("/favicon.ico")) {
|
||||
ctx.SetContentType("image/png")
|
||||
ctx.Write(FAVICON_BYTES)
|
||||
ctx.Write(faviconBytes)
|
||||
return true
|
||||
}
|
||||
|
||||
@ -415,7 +410,7 @@ func popRequestParam(ctx *fasthttp.RequestCtx, paramName []byte) []byte {
|
||||
func sanitizeCSS(rc *RequestConfig, out io.Writer, css []byte) {
|
||||
// TODO
|
||||
|
||||
urlSlices := CSS_URL_REGEXP.FindAllSubmatchIndex(css, -1)
|
||||
urlSlices := cssURLRegex.FindAllSubmatchIndex(css, -1)
|
||||
|
||||
if urlSlices == nil {
|
||||
out.Write(css)
|
||||
@ -531,7 +526,7 @@ func sanitizeHTML(rc *RequestConfig, out io.Writer, htmlDoc []byte) {
|
||||
}
|
||||
|
||||
if bytes.Equal(tag, []byte("head")) {
|
||||
fmt.Fprintf(out, HTML_HEAD_CONTENT_TYPE)
|
||||
fmt.Fprintf(out, htmlHeadContentType)
|
||||
}
|
||||
|
||||
if bytes.Equal(tag, []byte("form")) {
|
||||
@ -551,7 +546,7 @@ func sanitizeHTML(rc *RequestConfig, out io.Writer, htmlDoc []byte) {
|
||||
if rc.Key != nil {
|
||||
key = hash(urlStr, rc.Key)
|
||||
}
|
||||
err := HTML_FORM_EXTENSION.Execute(out, HTMLFormExtParam{urlStr, key, config.Config.UrlParameter, config.Config.HashParameter})
|
||||
err := htmlFormExtension.Execute(out, HTMLFormExtParam{urlStr, key, config.Config.UrlParameter, config.Config.HashParameter})
|
||||
if err != nil {
|
||||
if config.Config.Debug {
|
||||
fmt.Println("failed to inject body extension", err)
|
||||
@ -568,7 +563,7 @@ func sanitizeHTML(rc *RequestConfig, out io.Writer, htmlDoc []byte) {
|
||||
if len(rc.Key) > 0 {
|
||||
p.HasYukariKey = true
|
||||
}
|
||||
err := HTML_BODY_EXTENSION.Execute(out, p)
|
||||
err := htmlBodyExtension.Execute(out, p)
|
||||
if err != nil {
|
||||
if config.Config.Debug {
|
||||
fmt.Println("failed to inject body extension", err)
|
||||
@ -872,20 +867,20 @@ func verifyRequestURI(uri, hashMsg, key []byte) bool {
|
||||
func (p *Proxy) serveExitYukariPage(ctx *fasthttp.RequestCtx, uri *url.URL) {
|
||||
ctx.SetContentType("text/html")
|
||||
ctx.SetStatusCode(403)
|
||||
ctx.Write([]byte(YUKARI_HTML_PAGE_START))
|
||||
ctx.Write([]byte(htmlPageStart))
|
||||
ctx.Write([]byte("<h2>You are about to exit Yukari no Sukima</h2>"))
|
||||
ctx.Write([]byte("<p>Following</p><p><a href=\""))
|
||||
ctx.Write([]byte(html.EscapeString(uri.String())))
|
||||
ctx.Write([]byte("\" rel=\"noreferrer\">"))
|
||||
ctx.Write([]byte(html.EscapeString(uri.String())))
|
||||
ctx.Write([]byte("</a></p><p>the content of this URL will be <b>NOT</b> sanitized.</p>"))
|
||||
ctx.Write([]byte(YUKARI_HTML_PAGE_END))
|
||||
ctx.Write([]byte(htmlPageStop))
|
||||
}
|
||||
|
||||
func (p *Proxy) serveMainPage(ctx *fasthttp.RequestCtx, statusCode int, err error) {
|
||||
ctx.SetContentType("text/html; charset=UTF-8")
|
||||
ctx.SetStatusCode(statusCode)
|
||||
ctx.Write([]byte(YUKARI_HTML_PAGE_START))
|
||||
ctx.Write([]byte(htmlPageStart))
|
||||
if err != nil {
|
||||
if config.Config.Debug {
|
||||
log.Println("error:", err)
|
||||
@ -896,7 +891,7 @@ func (p *Proxy) serveMainPage(ctx *fasthttp.RequestCtx, statusCode int, err erro
|
||||
}
|
||||
if p.Key == nil {
|
||||
p := HTMLMainPageFormParam{config.Config.UrlParameter}
|
||||
err := HTML_MAIN_PAGE_FORM.Execute(ctx, p)
|
||||
err := htmlMainPageForm.Execute(ctx, p)
|
||||
if err != nil {
|
||||
if config.Config.Debug {
|
||||
fmt.Println("failed to inject main page form", err)
|
||||
@ -905,18 +900,16 @@ func (p *Proxy) serveMainPage(ctx *fasthttp.RequestCtx, statusCode int, err erro
|
||||
} else {
|
||||
ctx.Write([]byte(`<h3>Warning! This instance does not support direct URL opening.</h3>`))
|
||||
}
|
||||
ctx.Write([]byte(YUKARI_HTML_PAGE_END))
|
||||
ctx.Write([]byte(htmlPageStop))
|
||||
}
|
||||
|
||||
func main() {
|
||||
var configFile string
|
||||
var proxy string
|
||||
var proxyEnv bool
|
||||
var socks5 string
|
||||
var version bool
|
||||
|
||||
flag.StringVar(&configFile, "f", "", "Configuration file")
|
||||
flag.BoolVar(&proxyEnv, "proxyenv", false, "Use a HTTP proxy as set in the environment (HTTP_PROXY, HTTPS_PROXY and NO_PROXY). Overrides: -proxy, -socks5, IPv6")
|
||||
flag.StringVar(&proxy, "proxy", "", "Use the specified HTTP proxy (ie: '[user:pass@]hostname:port'). Overrides: -socks5, IPv6")
|
||||
flag.StringVar(&socks5, "socks5", "", "Use a SOCKS5 proxy (ie: 'hostname:port'). Overrides: IPv6.")
|
||||
flag.BoolVar(&version, "version", false, "Show version")
|
||||
@ -930,32 +923,36 @@ func main() {
|
||||
config.Config.FollowRedirect = false
|
||||
config.Config.UrlParameter = "yukariurl"
|
||||
config.Config.HashParameter = "yukarihash"
|
||||
config.Config.MaxConnsPerHost = 4
|
||||
config.Config.MaxConnsPerHost = 5
|
||||
config.Config.ProxyEnv = false
|
||||
|
||||
if version {
|
||||
yukari.FullVersion()
|
||||
return
|
||||
}
|
||||
|
||||
if proxyEnv && os.Getenv("HTTP_PROXY") == "" && os.Getenv("HTTPS_PROXY") == "" {
|
||||
if config.Config.ProxyEnv && os.Getenv("HTTP_PROXY") == "" && os.Getenv("HTTPS_PROXY") == "" {
|
||||
log.Fatal("Error -proxyenv is used but no environment variables named 'HTTP_PROXY' and/or 'HTTPS_PROXY' could be found.")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if proxyEnv {
|
||||
CLIENT.Dial = fasthttpproxy.FasthttpProxyHTTPDialer()
|
||||
if config.Config.ProxyEnv {
|
||||
config.Config.IPV6 = false
|
||||
Gap.Dial = fasthttpproxy.FasthttpProxyHTTPDialer()
|
||||
log.Println("Using environment defined proxy(ies).")
|
||||
} else if proxy != "" {
|
||||
CLIENT.Dial = fasthttpproxy.FasthttpHTTPDialer(proxy)
|
||||
config.Config.IPV6 = false
|
||||
Gap.Dial = fasthttpproxy.FasthttpHTTPDialer(proxy)
|
||||
log.Println("Using custom HTTP proxy.")
|
||||
} else if socks5 != "" {
|
||||
CLIENT.Dial = fasthttpproxy.FasthttpSocksDialer(socks5)
|
||||
config.Config.IPV6 = false
|
||||
Gap.Dial = fasthttpproxy.FasthttpSocksDialer(socks5)
|
||||
log.Println("Using Socks5 proxy.")
|
||||
} else if config.Config.IPV6 {
|
||||
CLIENT.Dial = fasthttp.DialDualStack
|
||||
Gap.Dial = fasthttp.DialDualStack
|
||||
log.Println("Using dual stack (IPv4/IPv6) direct connections.")
|
||||
} else {
|
||||
CLIENT.Dial = fasthttp.Dial
|
||||
Gap.Dial = fasthttp.Dial
|
||||
log.Println("Using IPv4 only direct connections.")
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ var Config struct {
|
||||
MaxConnsPerHost uint
|
||||
UrlParameter string
|
||||
HashParameter string
|
||||
ProxyEnv bool
|
||||
}
|
||||
|
||||
func readConfig(file string) error {
|
||||
@ -30,6 +31,6 @@ func readConfig(file string) error {
|
||||
Config.MaxConnsPerHost, _ = cfg.Section("yukari").Key("max_conns_per_host").Uint()
|
||||
Config.UrlParameter = cfg.Section("yukari").Key("urlparam").String()
|
||||
Config.HashParameter = cfg.Section("yukari").Key("hashparam").String()
|
||||
|
||||
Config.ProxyEnv, _ = cfg.Section("yukari").Key("proxyenv").Bool()
|
||||
return nil
|
||||
}
|
||||
|
40
yukari.ini.5
Normal file
40
yukari.ini.5
Normal file
@ -0,0 +1,40 @@
|
||||
.Dd $Mdocdate$
|
||||
.Dt YUKARI.INI 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm yukari.ini
|
||||
.Nd INI-style configuration file for
|
||||
.Xr yukari 1
|
||||
.Sh OPTIONS
|
||||
.Bl -tag -width Ds
|
||||
.It debug (bool)
|
||||
Enable/disable proxy and redirection logs (default true)
|
||||
.It listen (string)
|
||||
Listen address (default "127.0.0.1:3000")
|
||||
.It key (string)
|
||||
HMAC url validation key (base64 encoded) - leave blank to disable validation
|
||||
.It ipv6 (bool)
|
||||
Enable IPv6 support for queries
|
||||
(can be overrided by the proxy options, default true)
|
||||
.It timeout (uint)
|
||||
Request timeout (default 5)
|
||||
.It followredirect (bool)
|
||||
Follow HTTP GET redirect (default false)
|
||||
.It max_conns_per_host (uint)
|
||||
How much connections are allowed per Host/IP (default 4)
|
||||
.It urlparam (string)
|
||||
User-defined requesting string URL parameter name
|
||||
(ie: '/?url=...' or '/?u=...') (default "yukariurl")
|
||||
.It hashparam (string)
|
||||
User-defined requesting string HASH parameter name
|
||||
(ie: '/?hash=...' or '/?h=...') (default "yukarihash")
|
||||
.It proxyenv (string)
|
||||
Use a HTTP proxy as set in the environment
|
||||
(
|
||||
.Ev HTTP_PROXY ,
|
||||
.Ev HTTPS_PROXY ,
|
||||
.Ev NO_PROXY
|
||||
) (overrides ipv6, default false)
|
||||
.El
|
||||
.Sh AUTHORS
|
||||
.An Izuru Yakumo Aq Mt yakumo.izuru@chaotic.ninja
|
Loading…
x
Reference in New Issue
Block a user