Spies = make([]string, 0)
SpiesM sync.RWMutex
- Restricted = make(map[string][]string)
+ Restricted = make(map[string][]string)
RestrictedM sync.RWMutex
)
"fmt"
"log"
- "go.cypherpunks.ru/ucspi"
+ "go.cypherpunks.su/ucspi/v2"
ttls "go.stargrave.org/tofuproxy/tls"
)
"net"
"net/http"
- "go.cypherpunks.ru/ucspi"
+ "go.cypherpunks.su/ucspi/v2"
"go.stargrave.org/tofuproxy"
"go.stargrave.org/tofuproxy/fifos"
"go.stargrave.org/tofuproxy/rounds"
@multitable {XXXXX} {XXXX-XX-XX} {XXXX KiB} {meta4 tar pgp ssh}
@headitem Version @tab Date @tab Size @tab Tarball
+@item 0.6.0 @tab 2024-04-21 @tab 651 KiB @tab
+ @url{download/tofuproxy-0.6.0.tar.zst.meta4, meta4}
+ @url{download/tofuproxy-0.6.0.tar.zst, tar}
+ @url{download/tofuproxy-0.6.0.tar.zst.asc, pgp}
+ @url{download/tofuproxy-0.6.0.tar.zst.sig, ssh}
+
@item 0.5.0 @tab 2024-04-18 @tab 651 KiB @tab
@url{download/tofuproxy-0.5.0.tar.zst.meta4, meta4}
@url{download/tofuproxy-0.5.0.tar.zst, tar}
@item
Full TLS connection termination between Web-servers and
@command{tofuproxy} itself. TLS 1.3, session resumption, GOST
-cryptography (if built with @url{http://www.gostls13.cypherpunks.ru/,
+cryptography (if built with @url{http://www.gostls13.cypherpunks.su/,
gostls13}) support. Connection between @command{tofuproxy} and browser
itself uses ephemeral on-the-fly generated certificates with proper
domain name.
@file{.netrc}.
@item
-Permanent HTTP redirects are replaces with non-refreshing HTML page with
+Permanent HTTP redirects are replaced with non-refreshing HTML page with
the link, to make you explicitly allow that step. Temporary redirects
-are followed if it is neither @url{https://newsboat.org/, Newsboat}
-nor @url{https://www.feeder.stargrave.org/, go.stargrave.org/feeder}
-user-agent, not image paths.
+are followed if it is neither @url{https://www.feeder.stargrave.org/, go.stargrave.org/feeder}
+user-agent, nor image paths.
@item
JPEG XL, AVIF and WebP images are transparently transcoded to PNG,
Its fingerprint: @code{SHA256:NIDt9iZUizwivY3GoxmGvbQTH7mz/dmV7ZFOXeYfa2o}.
@example
-$ ssh-keygen -Y verify -f PUBKEY-SSH.pub -I tofuproxy@@cypherpunks.ru -n file \
+$ ssh-keygen -Y verify -f PUBKEY-SSH.pub -I tofuproxy@@stargrave.org -n file \
-s tofuproxy-@value{VERSION}.tar.zst.sig <tofuproxy-@value{VERSION}.tar.zst
@end example
@item I am tired that everyone provides very limited certificates trust
management capabilities, like either certificate or SPKI
@url{https://en.wikipedia.org/wiki/Certificate_pinning, pinning} with
-@url{https://en.wikipedia.org/wiki/Trust_on_first_use, TOFU}. Even my
-beloved @url{https://en.wikipedia.org/wiki/Xombrero, Xombrero} browser
-still pins only the whole certificate, but its public key would be much
-more sufficient and convenient to work with.
+@url{https://en.wikipedia.org/wiki/Trust_on_first_use, TOFU}.
@item I am tired that many clients provides very few information about
certificates and connections at all.
not friendly with TLS connections, obviously. Or use yet another
browser-specific plugin.
-@item Xombrero sometimes has problems with HTTP-based authorization.
-
@item Hardly anyone does
@url{https://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities, DANE}
checks.
Why the hell people just do not send PostScript documents instead!?
@item And wonderful @url{http://jpegxl.info/, JPEG XL} image format is
-not supported by most browsers. Even pretty old
-@url{https://developers.google.com/speed/webp, WebP}, that has highest
-compression ratio for lossless screenshots, is not supported everywhere,
-especially on old browsers.
-@url{https://aomediacodec.github.io/av1-avif/, AVIF} could be useful too.
+not supported by all browsers. Even pretty old
+@url{https://developers.google.com/speed/webp, WebP}, is not supported
+everywhere, especially on old browsers.
@item None of web browsers has ability to view web archives
(@url{https://en.wikipedia.org/wiki/Web_ARChive, WARC}s). And most of
)
func readLinesFromFIFO(p string) []string {
- fd, err := os.OpenFile(p, os.O_RDONLY, os.FileMode(0666))
+ fd, err := os.OpenFile(p, os.O_RDONLY, os.FileMode(0o666))
if err != nil {
log.Fatalln(err)
}
func del(l *sync.RWMutex, deleter func(string), p string) {
for {
- fd, err := os.OpenFile(p, os.O_RDONLY, os.FileMode(0666))
+ fd, err := os.OpenFile(p, os.O_RDONLY, os.FileMode(0o666))
if err != nil {
log.Fatalln(err)
}
func list(l *sync.RWMutex, m map[string]string, p string) {
for {
- fd, err := os.OpenFile(p, os.O_WRONLY|os.O_APPEND, os.FileMode(0666))
+ fd, err := os.OpenFile(p, os.O_WRONLY|os.O_APPEND, os.FileMode(0o666))
if err != nil {
log.Fatalln(err)
}
func listHTTPAuth(p string) {
for {
- fd, err := os.OpenFile(p, os.O_WRONLY|os.O_APPEND, os.FileMode(0666))
+ fd, err := os.OpenFile(p, os.O_WRONLY|os.O_APPEND, os.FileMode(0o666))
if err != nil {
log.Fatalln(err)
}
func listTLSAuth(p string) {
for {
- fd, err := os.OpenFile(p, os.O_WRONLY|os.O_APPEND, os.FileMode(0666))
+ fd, err := os.OpenFile(p, os.O_WRONLY|os.O_APPEND, os.FileMode(0o666))
if err != nil {
log.Fatalln(err)
}
caches.TLSAuthCacheM.RLock()
+ var cert *x509.Certificate
for host, tlsCert := range caches.TLSAuthCache {
subj := "NONE"
if len(tlsCert.Certificate) != 0 {
- cert, err := x509.ParseCertificate(tlsCert.Certificate[0])
+ cert, err = x509.ParseCertificate(tlsCert.Certificate[0])
if err != nil {
log.Fatalln(err)
}
"os"
"time"
- "go.cypherpunks.ru/tai64n/v2"
+ "go.cypherpunks.su/tai64n/v3"
)
var (
func logger(c chan string, p string) {
tai := new(tai64n.TAI64N)
for {
- fd, err := os.OpenFile(p, os.O_WRONLY|os.O_APPEND, os.FileMode(0666))
+ fd, err := os.OpenFile(p, os.O_WRONLY|os.O_APPEND, os.FileMode(0o666))
if err != nil {
log.Fatalln(err)
}
func listRestricted(p string) {
for {
- fd, err := os.OpenFile(p, os.O_WRONLY|os.O_APPEND, os.FileMode(0666))
+ fd, err := os.OpenFile(p, os.O_WRONLY|os.O_APPEND, os.FileMode(0o666))
if err != nil {
log.Fatalln(err)
}
func listSpies(p string) {
for {
- fd, err := os.OpenFile(p, os.O_WRONLY|os.O_APPEND, os.FileMode(0666))
+ fd, err := os.OpenFile(p, os.O_WRONLY|os.O_APPEND, os.FileMode(0o666))
if err != nil {
log.Fatalln(err)
}
"log"
"strings"
- "go.cypherpunks.ru/ucspi"
+ "go.cypherpunks.su/ucspi/v2"
"go.stargrave.org/tofuproxy/caches"
)
func listWARCs(p string) {
for {
- fd, err := os.OpenFile(p, os.O_WRONLY|os.O_APPEND, os.FileMode(0666))
+ fd, err := os.OpenFile(p, os.O_WRONLY|os.O_APPEND, os.FileMode(0o666))
if err != nil {
log.Fatalln(err)
}
require (
github.com/dustin/go-humanize v1.0.1
github.com/miekg/dns v1.1.57
- go.cypherpunks.ru/netrc v0.1.0
- go.cypherpunks.ru/tai64n/v2 v2.0.1
- go.cypherpunks.ru/ucspi v0.1.0
+ go.cypherpunks.su/netrc/v2 v2.0.0
+ go.cypherpunks.su/tai64n/v3 v3.0.0
+ go.cypherpunks.su/ucspi/v2 v2.0.0
)
require (
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
-go.cypherpunks.ru/netrc v0.1.0 h1:dzaSh4lgpPXvLzUME/hTSegg+Uw1aPEmC79fxbPLFGk=
-go.cypherpunks.ru/netrc v0.1.0/go.mod h1:ZmQaL9ENEII++WQooelccr33XZcBhKd8VgtcICf4SIE=
-go.cypherpunks.ru/tai64n/v2 v2.0.1 h1:AnwUUgi0EixZcr6nzjUaOy2DFInFwbNRkBkgon4oJfU=
-go.cypherpunks.ru/tai64n/v2 v2.0.1/go.mod h1:Jlva0YVJBpaRFAJ4jfY4BXVPuav0GMyGXzYPSKCCCL0=
-go.cypherpunks.ru/ucspi v0.1.0 h1:1w0E1mSf5oo3ozSSRUZN8Mf4/Ba18S3q7c5CDeCuHZA=
-go.cypherpunks.ru/ucspi v0.1.0/go.mod h1:mrkWllR0o0B/BocrUAL4DhcKLPK6XycObSswHUL5m6s=
+go.cypherpunks.su/netrc/v2 v2.0.0 h1:IWJ0fPcQoccn8VbqzAQf1/4rj8zw0Bf6X5kAkzwEd48=
+go.cypherpunks.su/netrc/v2 v2.0.0/go.mod h1:F5Ea8ii3R9dNUE0ixE61OYIhvJQVLLdIJjrU/3qROjw=
+go.cypherpunks.su/tai64n/v3 v3.0.0 h1:JvQiZJwkswUttMNcPrYS4KUK9Zfl6xllD0N6ZnSZX/U=
+go.cypherpunks.su/tai64n/v3 v3.0.0/go.mod h1:zGDFuyiFKJk+iem8lyBaFeCm+MNMOn7RRWy456n1J78=
+go.cypherpunks.su/ucspi/v2 v2.0.0 h1:Ha/0xfi0nXL/4xdiXqKbHdbXWE/qKcnXux4hG7c/lwM=
+go.cypherpunks.su/ucspi/v2 v2.0.0/go.mod h1:GIIOgXFLLze7hG1sAmXmPNTopVrAaFi5m+GMUA72fXY=
+golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
+golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
"os/exec"
"strings"
- "go.cypherpunks.ru/netrc"
+ "go.cypherpunks.su/netrc/v2"
ttls "go.stargrave.org/tofuproxy/tls"
)
var b bytes.Buffer
userInit, passInit := netrc.Find(host)
fmt.Fprintf(&b, `
-tk_setPalette grey
wm title . "Unauthorized: %s"
label .luser -text "User"
!strings.Contains(req.Header.Get("Accept"), ContentTypeGemini) {
w.Header().Add("Content-Type", "text/html")
w.WriteHeader(http.StatusOK)
- raw, err := io.ReadAll(br)
+ var raw []byte
+ raw, err = io.ReadAll(br)
if err != nil {
log.Printf("%s: can not read response body: %+v\n", req.URL, err)
return false, err
}
func isFeeder(req *http.Request) bool {
- if strings.Contains(req.Header.Get("User-Agent"), "newsboat/") {
- return true
- }
- if strings.Contains(req.Header.Get("User-Agent"), "stargrave.org-feeder/") {
- return true
- }
- return false
+ return strings.Contains(req.Header.Get("User-Agent"), "stargrave.org-feeder/")
}
func RoundRedirectHTML(
"fmt"
"net"
- "go.cypherpunks.ru/ucspi"
+ "go.cypherpunks.su/ucspi/v2"
"go.stargrave.org/tofuproxy/fifos"
)
"strconv"
"strings"
- "go.cypherpunks.ru/ucspi"
+ "go.cypherpunks.su/ucspi/v2"
"go.stargrave.org/tofuproxy/caches"
"go.stargrave.org/tofuproxy/fifos"
)
}
var b bytes.Buffer
fmt.Fprintf(&b, `
-tk_setPalette grey
wm title . "TLS client authentication: %s"
set lb [listbox .lb]
tlsCerts = append(tlsCerts, nil)
for i, ent := range ents {
p := filepath.Join(CCerts, ent.Name())
- _, cert, err := ucspi.CertificateFromFile(p)
+ var cert *x509.Certificate
+ _, cert, err = ucspi.CertificateFromFile(p)
if err != nil {
log.Fatalln(err)
}
- prv, err := ucspi.PrivateKeyFromFile(p)
+ var prv any
+ prv, err = ucspi.PrivateKeyFromFile(p)
if err != nil {
log.Fatalln(err)
}
"strings"
"sync"
- "go.cypherpunks.ru/ucspi"
+ "go.cypherpunks.su/ucspi/v2"
"go.stargrave.org/tofuproxy/caches"
"go.stargrave.org/tofuproxy/fifos"
)
set certsTheir [certsDecode $certsTheir]
set certsOur [certsDecode $certsOur]
-tk_setPalette grey
wm title . $host
proc paginator {i delta t l certs} {
fifos.LogVarious <- fmt.Sprintf(
"%s %s\tHTTP authorization required", req.Method, req.URL.Host,
)
- user, pass, err := authDialog(host, resp.Header.Get("WWW-Authenticate"))
+ var user, pass string
+ user, pass, err = authDialog(host, resp.Header.Get("WWW-Authenticate"))
if err != nil {
caches.HTTPAuthCacheM.Unlock()
fifos.LogErr <- fmt.Sprintf("%s\t%s", req.URL.Host, err.Error())
}
}
- for _, round := range []Round{
- rounds.RoundDenyFonts,
- rounds.RoundTranscodeWebP,
- rounds.RoundTranscodeJXL,
- rounds.RoundTranscodeAVIF,
- rounds.RoundRedirectHTML,
- } {
- cont, err := round(host, resp, w, req)
- if err != nil {
- http.Error(w, err.Error(), http.StatusBadGateway)
- return
- }
- if !cont {
- return
+ {
+ var cont bool
+ for _, round := range []Round{
+ rounds.RoundDenyFonts,
+ rounds.RoundTranscodeWebP,
+ rounds.RoundTranscodeJXL,
+ rounds.RoundTranscodeAVIF,
+ rounds.RoundRedirectHTML,
+ } {
+ cont, err = round(host, resp, w, req)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusBadGateway)
+ return
+ }
+ if !cont {
+ return
+ }
}
}
)
type CompressedReader struct {
- cmd *exec.Cmd
- fd *os.File
- stdout io.ReadCloser
- offsets []Offset
-
+ stdout io.ReadCloser
+ cmd *exec.Cmd
+ fd *os.File
offW *os.File
+ offsets []Offset
offReader sync.WaitGroup
}
cmd.Stdin = io.MultiReader(bytes.NewReader(dict), fd)
}
if offsets == nil {
- offR, offW, err := os.Pipe()
+ var offR, offW *os.File
+ offR, offW, err = os.Pipe()
if err != nil {
fd.Close()
return nil, err
gr := GZIPReader{r: r}
go func() {
z.Multistream(false)
- var offset, offsetPrev int64
+ var offset, offsetPrev, written int64
for {
- written, err := io.Copy(w, z)
+ written, err = io.Copy(w, z)
if err != nil {
w.CloseWithError(err)
return
Path string
rrr RawRecordReader
br *bufio.Reader
- offset int64
prevRec *Record
offsets []Offset
+ offset int64
}
func NewReader(warcPath string) (*Reader, error) {
hdrLen := len(line)
hdr := NewHeader()
for {
- line, err := r.br.ReadString('\n')
+ var line string
+ line, err = r.br.ReadString('\n')
if err != nil {
return nil, nil, err
}
)
type Record struct {
- WARCPath string
- Offset int64
- Size int64
-
- Hdr Header
- HdrLen int
- HdrLines []string
-
+ WARCPath string
+ Hdr Header
+ HdrLines []string
Continuations []*Record
+ Offset int64
+ Size int64
+ HdrLen int
}
func (rec *Record) URI() string {
var uris map[string]*Record
var offsets []Offset
dec := gob.NewDecoder(fd)
- if err := dec.Decode(&uris); err != nil {
+ if err = dec.Decode(&uris); err != nil {
return err
}
- if err := dec.Decode(&offsets); err != nil {
+ if err = dec.Decode(&offsets); err != nil {
return err
}
WARCsM.Lock()
log.Println("loaded marshalled index:", warcPath+IndexExt)
return nil
}
- if err != nil && !errors.Is(err, fs.ErrNotExist) {
+ if !errors.Is(err, fs.ErrNotExist) {
return err
}
r, err := NewReader(warcPath)
fd, err := os.OpenFile(
p+tmpSuffix,
os.O_WRONLY|os.O_CREATE|os.O_EXCL,
- os.FileMode(0666),
+ os.FileMode(0o666),
)
if err != nil {
return err