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:
yakumo.izuru 2024-03-21 22:39:20 +00:00
parent 3720388827
commit d1d65a0b21
6 changed files with 96 additions and 76 deletions

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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.")
}

View File

@ -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
View 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