Removed all redundant code that has been moved to https://git.gammaspectra.live/S.O.N.G/MakyuuIchaival
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
1531c52ae9
commit
71131602fe
698
OrbitalBeat.go
698
OrbitalBeat.go
|
@ -2,31 +2,20 @@ package main
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"database/sql"
|
||||
"encoding/base32"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"flag"
|
||||
"fmt"
|
||||
"git.gammaspectra.live/S.O.N.G/MakyuuIchaival"
|
||||
"git.gammaspectra.live/S.O.N.G/MakyuuIchaival/contentmessage"
|
||||
"git.gammaspectra.live/S.O.N.G/MakyuuIchaival/httputils"
|
||||
"git.gammaspectra.live/S.O.N.G/MakyuuIchaival/tlsutils"
|
||||
"github.com/cloudflare/circl/sign/ed25519"
|
||||
"github.com/dgrr/http2"
|
||||
"github.com/ipfs/go-cid"
|
||||
_ "github.com/lib/pq"
|
||||
"github.com/lucas-clemente/quic-go/http3"
|
||||
"github.com/multiformats/go-multihash"
|
||||
"github.com/valyala/fasthttp"
|
||||
"log"
|
||||
"math"
|
||||
"math/big"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
|
@ -41,33 +30,18 @@ type ContentCacheEntry struct {
|
|||
AccessTime time.Time
|
||||
}
|
||||
|
||||
var base32Encoding = base32.NewEncoding("qpzry9x8gf2tvdw0s3jn54khce6mua7l").WithPadding(base32.NoPadding)
|
||||
|
||||
var dbHandle *sql.DB
|
||||
var sniAddress string
|
||||
var sha256Statement *sql.Stmt
|
||||
var md5Statement *sql.Stmt
|
||||
var fdlimit int
|
||||
var fsHandler fasthttp.RequestHandler
|
||||
|
||||
var objectCacheMutex sync.RWMutex
|
||||
var objectCache = make(map[string]*ContentCacheEntry)
|
||||
|
||||
var messageCacheLimit int
|
||||
var messageCacheMutex sync.RWMutex
|
||||
var messageCache = make(map[string]*ContentMessage)
|
||||
|
||||
var extraHeadersMutex sync.RWMutex
|
||||
var extraHeaders = make(map[string]string)
|
||||
|
||||
var trustedPublicKeys []ed25519.PublicKey
|
||||
|
||||
var debugOutput = false
|
||||
|
||||
func isSNIAllowed(sni string) bool {
|
||||
return len(sniAddress) == 0 || sni == sniAddress
|
||||
}
|
||||
|
||||
func getFirstValidContentEntry(entries *[]ContentEntry) *ContentEntry {
|
||||
for _, entry := range *entries {
|
||||
stat, err := os.Stat(entry.Path)
|
||||
|
@ -109,7 +83,7 @@ func guessMimeType(p string) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func handleQueryRequest(ctx *RequestContext, identifier cid.Cid, extraArguments []string) {
|
||||
func handleQueryRequest(ctx *httputils.RequestContext, identifier cid.Cid, extraArguments []string) {
|
||||
cTime := time.Now()
|
||||
var cacheEntry = tryGetCacheEntryForIdentifier(identifier)
|
||||
|
||||
|
@ -121,21 +95,21 @@ func handleQueryRequest(ctx *RequestContext, identifier cid.Cid, extraArguments
|
|||
if entry != nil {
|
||||
cacheEntry = getCacheEntryForContentEntry(entry, identifier)
|
||||
}
|
||||
addServerTimingMetricInformational(ctx, "ec", "Content Cache MISS")
|
||||
ctx.AddTimingInformational("ec", "Content Cache MISS")
|
||||
} else {
|
||||
addServerTimingMetricInformational(ctx, "ec", "Content Cache HIT")
|
||||
ctx.AddTimingInformational("ec", "Content Cache HIT")
|
||||
}
|
||||
pTime := cTime
|
||||
cTime = time.Now()
|
||||
addServerTimingMetric(ctx, "e", "Content Entry", cTime.Sub(pTime))
|
||||
ctx.AddTiming("e", "Content Entry", cTime.Sub(pTime))
|
||||
|
||||
if cacheEntry == nil {
|
||||
var origin string
|
||||
|
||||
if len(ctx.getRequestHeader("Referer")) > 0 {
|
||||
origin = ctx.getRequestHeader("Referer")
|
||||
} else if len(ctx.getRequestHeader("Origin")) > 0 {
|
||||
origin = ctx.getRequestHeader("Origin")
|
||||
if len(ctx.GetRequestHeader("Referer")) > 0 {
|
||||
origin = ctx.GetRequestHeader("Referer")
|
||||
} else if len(ctx.GetRequestHeader("Origin")) > 0 {
|
||||
origin = ctx.GetRequestHeader("Origin")
|
||||
}
|
||||
|
||||
//Try to redirect back to origin
|
||||
|
@ -148,64 +122,62 @@ func handleQueryRequest(ctx *RequestContext, identifier cid.Cid, extraArguments
|
|||
kind = "md5"
|
||||
}
|
||||
if kind != "" {
|
||||
ctx.doRedirect(fmt.Sprintf("%s/%s/%s/%s", origin, kind, hex.EncodeToString(mh.Digest), strings.Join(extraArguments, "/")), http.StatusFound)
|
||||
ctx.DoRedirect(fmt.Sprintf("%s/%s/%s/%s", origin, kind, hex.EncodeToString(mh.Digest), strings.Join(extraArguments, "/")), http.StatusFound)
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.setResponseCode(http.StatusNotFound)
|
||||
ctx.SetResponseCode(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
mh, _ := multihash.Decode(cacheEntry.Entry.Identifier.Hash())
|
||||
|
||||
ctx.setResponseHeader("Accept-Ranges", "bytes")
|
||||
ctx.setResponseHeader("X-Request-CID", identifier.String())
|
||||
ctx.setResponseHeader("ETag", fmt.Sprintf("\"%s\"", cacheEntry.Entry.Identifier.String()))
|
||||
ctx.SetResponseHeader("Accept-Ranges", "bytes")
|
||||
ctx.SetResponseHeader("X-Request-CID", identifier.String())
|
||||
ctx.SetResponseHeader("ETag", fmt.Sprintf("\"%s\"", cacheEntry.Entry.Identifier.String()))
|
||||
if mh.Code == multihash.SHA2_256 {
|
||||
ctx.setResponseHeader("Digest", fmt.Sprintf("sha-256=%s", base64.StdEncoding.EncodeToString(mh.Digest)))
|
||||
ctx.SetResponseHeader("Digest", fmt.Sprintf("sha-256=%s", base64.StdEncoding.EncodeToString(mh.Digest)))
|
||||
}
|
||||
ctx.setResponseHeader("Cache-Control", "public, max-age=2592000, immutable")
|
||||
ctx.SetResponseHeader("Cache-Control", "public, max-age=2592000, immutable")
|
||||
|
||||
filename := path.Base(cacheEntry.Entry.Path)
|
||||
|
||||
//TODO: setting to hide filename
|
||||
ctx.setResponseHeader("Content-Disposition", fmt.Sprintf("inline; filename*=utf-8''%s", url.PathEscape(filename)))
|
||||
ctx.SetResponseHeader("Content-Disposition", fmt.Sprintf("inline; filename*=utf-8''%s", url.PathEscape(filename)))
|
||||
|
||||
pTime = cTime
|
||||
cTime = time.Now()
|
||||
addServerTimingMetric(ctx, "s", "Content Serve", cTime.Sub(pTime))
|
||||
ctx.AddTiming("s", "Content Serve", cTime.Sub(pTime))
|
||||
|
||||
mime := guessMimeType(cacheEntry.Entry.Path)
|
||||
if len(mime) > 0 {
|
||||
ctx.setResponseHeader("Content-Type", mime)
|
||||
ctx.SetResponseHeader("Content-Type", mime)
|
||||
}
|
||||
|
||||
ctx.serveFile(cacheEntry.Entry.Path)
|
||||
ctx.ServeFile(cacheEntry.Entry.Path)
|
||||
}
|
||||
|
||||
func setOtherHeaders(ctx *RequestContext) {
|
||||
ctx.setResponseHeader("Server", "OrbitalBeat")
|
||||
ctx.setResponseHeader("Vary", "Content-Encoding")
|
||||
ctx.setResponseHeader("X-Content-Type-Options", "nosniff")
|
||||
func setOtherHeaders(ctx *httputils.RequestContext) {
|
||||
ctx.SetResponseHeader("Server", "OrbitalBeat")
|
||||
ctx.SetResponseHeader("Vary", "Content-Encoding")
|
||||
ctx.SetResponseHeader("X-Content-Type-Options", "nosniff")
|
||||
|
||||
extraHeadersMutex.RLock()
|
||||
defer extraHeadersMutex.RUnlock()
|
||||
for k, v := range extraHeaders {
|
||||
ctx.setResponseHeader(k, v)
|
||||
for k, v := range ctx.GetExtraHeaders() {
|
||||
ctx.SetResponseHeader(k, v)
|
||||
}
|
||||
}
|
||||
func setCORSHeaders(ctx *RequestContext) {
|
||||
ctx.setResponseHeader("Access-Control-Allow-Credentials", "true")
|
||||
ctx.setResponseHeader("Access-Control-Max-Age", "7200") //Firefox caps this to 86400, Chrome to 7200. Default is 5 seconds (!!!)
|
||||
ctx.setResponseHeader("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS")
|
||||
ctx.setResponseHeader("Access-Control-Allow-Headers", "DNT,ETag,Origin,Accept,Accept-Language,X-Requested-With,Range")
|
||||
ctx.setResponseHeader("Access-Control-Allow-Origin", "*")
|
||||
ctx.setResponseHeader("Access-Control-Expose-Headers", "*")
|
||||
func setCORSHeaders(ctx *httputils.RequestContext) {
|
||||
ctx.SetResponseHeader("Access-Control-Allow-Credentials", "true")
|
||||
ctx.SetResponseHeader("Access-Control-Max-Age", "7200") //Firefox caps this to 86400, Chrome to 7200. Default is 5 seconds (!!!)
|
||||
ctx.SetResponseHeader("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS")
|
||||
ctx.SetResponseHeader("Access-Control-Allow-Headers", "DNT,ETag,Origin,Accept,Accept-Language,X-Requested-With,Range")
|
||||
ctx.SetResponseHeader("Access-Control-Allow-Origin", "*")
|
||||
ctx.SetResponseHeader("Access-Control-Expose-Headers", "*")
|
||||
|
||||
//CORP, COEP, COOP
|
||||
ctx.setResponseHeader("Cross-Origin-Embedder-Policy", "require-corp")
|
||||
ctx.setResponseHeader("Cross-Origin-Resource-Policy", "cross-origin")
|
||||
ctx.setResponseHeader("Cross-Origin-Opener-Policy", "unsafe-none")
|
||||
ctx.SetResponseHeader("Cross-Origin-Embedder-Policy", "require-corp")
|
||||
ctx.SetResponseHeader("Cross-Origin-Resource-Policy", "cross-origin")
|
||||
ctx.SetResponseHeader("Cross-Origin-Opener-Policy", "unsafe-none")
|
||||
}
|
||||
|
||||
func getCacheEntryForContentEntry(entry *ContentEntry, originalIdentifier cid.Cid) *ContentCacheEntry {
|
||||
|
@ -269,228 +241,64 @@ func IsTrustedPublicKey(key ed25519.PublicKey) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func addServerTimingMetric(ctx *RequestContext, name string, desc string, d time.Duration) {
|
||||
if d < 0 {
|
||||
d = 0
|
||||
}
|
||||
|
||||
ctx.addResponseHeader("Server-Timing", fmt.Sprintf("%d_%s;desc=\"%s\";dur=%.6F", ctx.TimingEvents, name, desc, float64(d.Nanoseconds())/1e6))
|
||||
ctx.TimingEvents++
|
||||
}
|
||||
|
||||
func addServerTimingMetricInformational(ctx *RequestContext, name string, desc string) {
|
||||
ctx.addResponseHeader("Server-Timing", fmt.Sprintf("%d_%s;desc=\"%s\"", ctx.TimingEvents, name, desc))
|
||||
ctx.TimingEvents++
|
||||
}
|
||||
|
||||
type RequestContext struct {
|
||||
fasthttp *fasthttp.RequestCtx
|
||||
httpWriter *http.ResponseWriter
|
||||
httpRequest *http.Request
|
||||
connTime time.Time
|
||||
requestTime time.Time
|
||||
TimingEvents uint
|
||||
}
|
||||
|
||||
func NewRequestContextFromFastHttp(ctx *fasthttp.RequestCtx) RequestContext {
|
||||
return RequestContext{
|
||||
fasthttp: ctx,
|
||||
connTime: ctx.ConnTime(),
|
||||
requestTime: ctx.Time(),
|
||||
}
|
||||
}
|
||||
|
||||
func NewRequestContextFromHttp(w http.ResponseWriter, r *http.Request) RequestContext {
|
||||
return RequestContext{
|
||||
httpWriter: &w,
|
||||
httpRequest: r,
|
||||
connTime: time.Now(),
|
||||
requestTime: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RequestContext) getPath() string {
|
||||
if c.fasthttp != nil {
|
||||
return string(c.fasthttp.Path())
|
||||
} else if c.httpRequest != nil {
|
||||
return c.httpRequest.URL.Path
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *RequestContext) getConnectionTime() time.Time {
|
||||
return c.connTime
|
||||
}
|
||||
|
||||
func (c *RequestContext) getRequestTime() time.Time {
|
||||
return c.requestTime
|
||||
}
|
||||
|
||||
func (c *RequestContext) isFastHttp() bool {
|
||||
return c.fasthttp != nil
|
||||
}
|
||||
|
||||
func (c *RequestContext) getTLSServerName() string {
|
||||
if c.fasthttp != nil {
|
||||
return c.fasthttp.TLSConnectionState().ServerName
|
||||
} else if c.httpRequest != nil {
|
||||
return c.httpRequest.TLS.ServerName
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *RequestContext) getRequestHeader(name string) string {
|
||||
if c.fasthttp != nil {
|
||||
return string(c.fasthttp.Request.Header.Peek(name))
|
||||
} else if c.httpRequest != nil {
|
||||
return c.httpRequest.Header.Get(name)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *RequestContext) getResponseHeader(name string) string {
|
||||
if c.fasthttp != nil {
|
||||
return string(c.fasthttp.Response.Header.Peek(name))
|
||||
} else if c.httpWriter != nil {
|
||||
return (*c.httpWriter).Header().Get(name)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *RequestContext) addResponseHeader(name string, value string) {
|
||||
if c.fasthttp != nil {
|
||||
c.fasthttp.Response.Header.Add(name, value)
|
||||
} else if c.httpWriter != nil {
|
||||
(*c.httpWriter).Header().Add(name, value)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RequestContext) setResponseHeader(name string, value string) {
|
||||
if c.fasthttp != nil {
|
||||
c.fasthttp.Response.Header.Set(name, value)
|
||||
} else if c.httpWriter != nil {
|
||||
(*c.httpWriter).Header().Set(name, value)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RequestContext) serveFile(path string) {
|
||||
if c.fasthttp != nil {
|
||||
c.fasthttp.Request.URI().Reset()
|
||||
c.fasthttp.Request.URI().SetPath(path)
|
||||
fsHandler(c.fasthttp)
|
||||
} else if c.httpWriter != nil {
|
||||
http.ServeFile(*c.httpWriter, c.httpRequest, path)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RequestContext) setResponseCode(code int) {
|
||||
if c.fasthttp != nil {
|
||||
c.fasthttp.Response.SetStatusCode(code)
|
||||
} else if c.httpWriter != nil {
|
||||
(*c.httpWriter).WriteHeader(code)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RequestContext) doRedirect(location string, code int) {
|
||||
if c.fasthttp != nil {
|
||||
c.fasthttp.Redirect(location, code)
|
||||
} else if c.httpWriter != nil {
|
||||
http.Redirect(*c.httpWriter, c.httpRequest, location, code)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RequestContext) isGet() bool {
|
||||
if c.fasthttp != nil {
|
||||
return c.fasthttp.IsGet()
|
||||
} else if c.httpRequest != nil {
|
||||
return c.httpRequest.Method == "GET"
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *RequestContext) isOptions() bool {
|
||||
if c.fasthttp != nil {
|
||||
return c.fasthttp.IsOptions()
|
||||
} else if c.httpRequest != nil {
|
||||
return c.httpRequest.Method == "OPTIONS"
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *RequestContext) isHead() bool {
|
||||
if c.fasthttp != nil {
|
||||
return c.fasthttp.IsHead()
|
||||
} else if c.httpRequest != nil {
|
||||
return c.httpRequest.Method == "HEAD"
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func handle(ctx RequestContext) {
|
||||
if len(ctx.getRequestHeader("Host")) > 0 && ctx.getRequestHeader("Host") == ctx.getTLSServerName() { //Prevents rebinding / DNS stuff
|
||||
ctx.setResponseCode(http.StatusNotFound)
|
||||
ctx.setResponseCode(http.StatusNotFound)
|
||||
func handle(ctx *httputils.RequestContext) {
|
||||
if len(ctx.GetRequestHeader("Host")) > 0 && ctx.GetRequestHeader("Host") == ctx.GetTLSServerName() { //Prevents rebinding / DNS stuff
|
||||
ctx.SetResponseCode(http.StatusNotFound)
|
||||
ctx.SetResponseCode(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
cTime := time.Now()
|
||||
if ctx.isFastHttp() {
|
||||
addServerTimingMetric(&ctx, "c", "Connection", ctx.getRequestTime().Sub(ctx.getConnectionTime()))
|
||||
if ctx.IsFastHttp() {
|
||||
ctx.AddTiming("c", "Connection", ctx.GetRequestTime().Sub(ctx.GetConnectionTime()))
|
||||
}
|
||||
addServerTimingMetric(&ctx, "r", "Request Handler", cTime.Sub(ctx.getRequestTime()))
|
||||
ctx.AddTiming("r", "Request Handler", cTime.Sub(ctx.GetRequestTime()))
|
||||
|
||||
if ctx.isGet() || ctx.isHead() {
|
||||
if ctx.IsGet() || ctx.IsHead() {
|
||||
if debugOutput {
|
||||
log.Printf("Serve %s", ctx.getPath())
|
||||
log.Printf("Serve %s", ctx.GetPath())
|
||||
}
|
||||
setOtherHeaders(&ctx)
|
||||
setCORSHeaders(&ctx)
|
||||
pathElements := strings.Split(ctx.getPath(), "/")
|
||||
setOtherHeaders(ctx)
|
||||
setCORSHeaders(ctx)
|
||||
pathElements := strings.Split(ctx.GetPath(), "/")
|
||||
if len(pathElements) < 2 {
|
||||
ctx.setResponseCode(http.StatusBadRequest)
|
||||
ctx.SetResponseCode(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
messageBytes, err := base32Encoding.DecodeString(pathElements[1])
|
||||
messageBytes, err := MakyuuIchaival.Bech32Encoding.DecodeString(pathElements[1])
|
||||
if err != nil {
|
||||
ctx.setResponseCode(http.StatusBadRequest)
|
||||
ctx.SetResponseCode(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
message := DecodeContentMessage(messageBytes)
|
||||
message := contentmessage.DecodeContentMessage(messageBytes)
|
||||
if message == nil {
|
||||
ctx.setResponseCode(http.StatusBadRequest)
|
||||
ctx.SetResponseCode(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if !IsTrustedPublicKey(message.PublicKey) {
|
||||
ctx.setResponseCode(http.StatusForbidden)
|
||||
ctx.SetResponseCode(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
pTime := cTime
|
||||
cTime := time.Now()
|
||||
addServerTimingMetric(&ctx, "d", "Decode", cTime.Sub(pTime))
|
||||
ctx.AddTiming("d", "Decode", cTime.Sub(pTime))
|
||||
|
||||
result, cacheHit := message.verify()
|
||||
result, cacheHit := message.Verify()
|
||||
|
||||
pTime = cTime
|
||||
cTime = time.Now()
|
||||
if cacheHit {
|
||||
addServerTimingMetricInformational(&ctx, "vc", "Ed25519 Cache HIT")
|
||||
ctx.AddTimingInformational("vc", "Ed25519 Cache HIT")
|
||||
} else {
|
||||
addServerTimingMetricInformational(&ctx, "vc", "Ed25519 Cache MISS")
|
||||
ctx.AddTimingInformational("vc", "Ed25519 Cache MISS")
|
||||
}
|
||||
addServerTimingMetric(&ctx, "v", "Ed25519 Verify", cTime.Sub(pTime))
|
||||
ctx.AddTiming("v", "Ed25519 Verify", cTime.Sub(pTime))
|
||||
|
||||
if !result {
|
||||
ctx.setResponseCode(http.StatusForbidden)
|
||||
ctx.SetResponseCode(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -498,176 +306,16 @@ func handle(ctx RequestContext) {
|
|||
log.Printf("Serving CID %s", message.Identifier.String())
|
||||
}
|
||||
|
||||
handleQueryRequest(&ctx, message.Identifier, pathElements[2:])
|
||||
} else if ctx.isOptions() {
|
||||
setOtherHeaders(&ctx)
|
||||
setCORSHeaders(&ctx)
|
||||
ctx.setResponseCode(http.StatusNoContent)
|
||||
handleQueryRequest(ctx, message.Identifier, pathElements[2:])
|
||||
} else if ctx.IsOptions() {
|
||||
setOtherHeaders(ctx)
|
||||
setCORSHeaders(ctx)
|
||||
ctx.SetResponseCode(http.StatusNoContent)
|
||||
} else {
|
||||
ctx.setResponseCode(http.StatusNotImplemented)
|
||||
ctx.SetResponseCode(http.StatusNotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
// ContentMessage TODO: move this to a library
|
||||
type ContentMessage struct {
|
||||
Version uint64
|
||||
PublicKey ed25519.PublicKey
|
||||
IssueTime int64
|
||||
Identifier cid.Cid
|
||||
VerificationResult *bool
|
||||
Signature []byte
|
||||
}
|
||||
|
||||
func (s *ContentMessage) sign(privateKey ed25519.PrivateKey) {
|
||||
s.PublicKey = make([]byte, ed25519.PublicKeySize)
|
||||
copy(s.PublicKey, privateKey[32:])
|
||||
s.Signature = ed25519.Sign(privateKey, s.encodeMessage())
|
||||
s.VerificationResult = nil
|
||||
}
|
||||
|
||||
func (s *ContentMessage) verify() (bool, bool) {
|
||||
currentTime := time.Now()
|
||||
|
||||
issueTime := time.Unix(s.IssueTime, 0)
|
||||
validityStart := issueTime.Add(-time.Hour) //Only one hour before time
|
||||
validityEnd := issueTime.Add(time.Hour * 24) //Only 24 hours after time
|
||||
|
||||
if validityStart.After(currentTime) {
|
||||
return false, false
|
||||
}
|
||||
|
||||
if validityEnd.Before(currentTime) {
|
||||
return false, false
|
||||
}
|
||||
|
||||
messageCacheMutex.RLock()
|
||||
k := string(s.encode())
|
||||
cachedMessage, ok := messageCache[k]
|
||||
messageCacheMutex.RUnlock()
|
||||
if ok {
|
||||
return *cachedMessage.VerificationResult, true
|
||||
}
|
||||
|
||||
messageCacheMutex.Lock()
|
||||
defer messageCacheMutex.Unlock()
|
||||
|
||||
if s.VerificationResult == nil {
|
||||
makeBool := func(v bool) *bool { return &v }
|
||||
s.VerificationResult = makeBool(ed25519.Verify(s.PublicKey, s.encodeMessage(), s.Signature))
|
||||
}
|
||||
|
||||
if len(messageCache) >= messageCacheLimit {
|
||||
//Find oldest value, remove it
|
||||
var item *ContentMessage
|
||||
for _, e := range messageCache {
|
||||
if item == nil || e.IssueTime < item.IssueTime {
|
||||
item = e
|
||||
}
|
||||
}
|
||||
|
||||
if item != nil {
|
||||
delete(messageCache, string(item.encode()))
|
||||
}
|
||||
}
|
||||
|
||||
messageCache[k] = s
|
||||
|
||||
return *s.VerificationResult, false
|
||||
}
|
||||
|
||||
func (s *ContentMessage) encodeMessage() []byte {
|
||||
message := &bytes.Buffer{}
|
||||
|
||||
buf := make([]byte, binary.MaxVarintLen64)
|
||||
|
||||
n := binary.PutUvarint(buf, s.Version) //signature version
|
||||
_, _ = message.Write(buf[:n])
|
||||
|
||||
if s.Version == 0 || s.Version == 1 {
|
||||
_, _ = message.Write(s.PublicKey)
|
||||
n = binary.PutVarint(buf, s.IssueTime) //time
|
||||
_, _ = message.Write(buf[:n])
|
||||
|
||||
if s.Version == 1 {
|
||||
_, _ = message.Write(s.Identifier.Hash())
|
||||
} else {
|
||||
_, _ = s.Identifier.WriteBytes(message)
|
||||
}
|
||||
|
||||
return message.Bytes()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ContentMessage) encode() []byte {
|
||||
message := s.encodeMessage()
|
||||
|
||||
if message == nil || len(s.Signature) != ed25519.SignatureSize {
|
||||
return nil
|
||||
}
|
||||
|
||||
return append(message, s.Signature...)
|
||||
}
|
||||
|
||||
func (s ContentMessage) String() string {
|
||||
return fmt.Sprintf("%d %x %d %s %x", s.Version, s.PublicKey, s.IssueTime, s.Identifier.String(), s.Signature)
|
||||
}
|
||||
|
||||
func DecodeContentMessage(signatureBytes []byte) *ContentMessage {
|
||||
message := ContentMessage{}
|
||||
|
||||
buffer := bytes.NewBuffer(signatureBytes)
|
||||
|
||||
var err error
|
||||
|
||||
message.Version, err = binary.ReadUvarint(buffer)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if message.Version == 0 || message.Version == 1 {
|
||||
message.PublicKey = make([]byte, ed25519.PublicKeySize)
|
||||
_, err := buffer.Read(message.PublicKey)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
message.IssueTime, err = binary.ReadVarint(buffer)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if message.Version == 1 {
|
||||
read, mh, err := multihash.MHFromBytes(buffer.Bytes())
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
buffer.Next(read)
|
||||
message.Identifier = cid.NewCidV1(cid.Raw, mh)
|
||||
} else {
|
||||
_, message.Identifier, err = cid.CidFromReader(buffer)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
message.Signature = make([]byte, ed25519.SignatureSize)
|
||||
_, err = buffer.Read(message.Signature)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if buffer.Len() != 0 { //Unknown extra data
|
||||
return nil
|
||||
}
|
||||
|
||||
return &message
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type ContentEntry struct {
|
||||
Identifier cid.Cid
|
||||
Path string
|
||||
|
@ -716,53 +364,6 @@ func getEntriesForCID(identifier cid.Cid) []ContentEntry {
|
|||
return []ContentEntry{}
|
||||
}
|
||||
|
||||
func createSelfSignedCertificate() ([]byte, []byte) {
|
||||
serial, _ := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
|
||||
x509Template := x509.Certificate{
|
||||
SerialNumber: serial,
|
||||
Subject: pkix.Name{},
|
||||
NotBefore: time.Unix(0, 0).UTC(),
|
||||
NotAfter: time.Date(time.Now().UTC().Year()+10, 0, 0, 0, 0, 0, 0, time.UTC),
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
privateBogusKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
certBytes, err := x509.CreateCertificate(rand.Reader, &x509Template, &x509Template, privateBogusKey.Public(), privateBogusKey)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
keyBytes, err := x509.MarshalPKCS8PrivateKey(privateBogusKey)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return pem.EncodeToMemory(&pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: certBytes,
|
||||
}),
|
||||
pem.EncodeToMemory(&pem.Block{
|
||||
Type: "PRIVATE KEY",
|
||||
Bytes: keyBytes,
|
||||
})
|
||||
}
|
||||
|
||||
func processCertificate(c *tls.Certificate) {
|
||||
if c.Leaf == nil {
|
||||
leaf, err := x509.ParseCertificate(c.Certificate[0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.Leaf = leaf
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
//TODO: OCSP
|
||||
certificatePath := flag.String("certificate", "", "Path to SSL certificate file.")
|
||||
|
@ -789,7 +390,7 @@ func main() {
|
|||
flag.Parse()
|
||||
|
||||
fdlimit = *fdLimitOption
|
||||
messageCacheLimit = *signatureCacheLimitOption
|
||||
contentmessage.SetMessageCacheLimit(*signatureCacheLimitOption)
|
||||
|
||||
debugOutput = *debugOption
|
||||
|
||||
|
@ -797,7 +398,7 @@ func main() {
|
|||
|
||||
for _, k := range strings.Split(*trustedKeys, ",") {
|
||||
var publicKey ed25519.PublicKey
|
||||
publicKey, err = base32Encoding.DecodeString(strings.Trim(k, " "))
|
||||
publicKey, err = MakyuuIchaival.Bech32Encoding.DecodeString(strings.Trim(k, " "))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -830,159 +431,20 @@ func main() {
|
|||
|
||||
//TODO: create postgres tables
|
||||
|
||||
sniAddress = strings.ToLower(*sniAddressOption)
|
||||
|
||||
bogusCertificatePEM, bogusKeyPairPEM := createSelfSignedCertificate()
|
||||
|
||||
bogusCertificate, err := tls.X509KeyPair(bogusCertificatePEM, bogusKeyPairPEM)
|
||||
tlsConfiguration, err := tlsutils.NewTLSConfiguration(*certificatePath, *keypairPath, strings.ToLower(*sniAddressOption))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var serverCertificate tls.Certificate
|
||||
|
||||
if *certificatePath != "" && *keypairPath != "" {
|
||||
serverCertificate, err = tls.LoadX509KeyPair(*certificatePath, *keypairPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
serverCertificate = bogusCertificate
|
||||
}
|
||||
processCertificate(&bogusCertificate)
|
||||
processCertificate(&serverCertificate)
|
||||
|
||||
fs := fasthttp.FS{
|
||||
Root: "/",
|
||||
AcceptByteRange: true,
|
||||
Compress: false,
|
||||
CompressBrotli: false,
|
||||
CacheDuration: time.Minute * 15,
|
||||
PathRewrite: func(ctx *fasthttp.RequestCtx) []byte {
|
||||
return ctx.Request.URI().PathOriginal()
|
||||
},
|
||||
server := &httputils.Server{
|
||||
ListenAddress: *listenAddress,
|
||||
TLSConfig: tlsConfiguration,
|
||||
EnableHTTP2: *http2Option,
|
||||
EnableHTTP3: *http3Option,
|
||||
Handler: handle,
|
||||
Debug: debugOutput,
|
||||
}
|
||||
|
||||
fsHandler = fs.NewRequestHandler()
|
||||
server.Serve()
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
MaxVersion: 0, //max supported, currently TLS 1.3
|
||||
CurvePreferences: []tls.CurveID{
|
||||
tls.X25519,
|
||||
tls.CurveP256,
|
||||
tls.CurveP384,
|
||||
},
|
||||
CipherSuites: []uint16{
|
||||
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
},
|
||||
SessionTicketsDisabled: false,
|
||||
NextProtos: []string{
|
||||
"http/1.1",
|
||||
},
|
||||
GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
if isSNIAllowed(info.ServerName) {
|
||||
return &serverCertificate, nil
|
||||
}
|
||||
return &bogusCertificate, nil
|
||||
},
|
||||
}
|
||||
|
||||
if serverCertificate.Leaf.PublicKeyAlgorithm == x509.RSA || bogusCertificate.Leaf.PublicKeyAlgorithm == x509.RSA {
|
||||
tlsConfig.CipherSuites = append(tlsConfig.CipherSuites, []uint16{
|
||||
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
}...)
|
||||
}
|
||||
|
||||
if *http2Option {
|
||||
tlsConfig.NextProtos = []string{
|
||||
"h2",
|
||||
"http/1.1",
|
||||
}
|
||||
} else {
|
||||
extraHeaders["Connection"] = "close"
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
wg.Add(1)
|
||||
go func(wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
server := &fasthttp.Server{
|
||||
ReadTimeout: 5 * time.Second,
|
||||
IdleTimeout: 15 * time.Second,
|
||||
Handler: func(ctx *fasthttp.RequestCtx) {
|
||||
handle(NewRequestContextFromFastHttp(ctx))
|
||||
},
|
||||
NoDefaultServerHeader: true,
|
||||
NoDefaultDate: true,
|
||||
DisableKeepalive: !*http2Option,
|
||||
TCPKeepalive: *http2Option,
|
||||
TLSConfig: tlsConfig,
|
||||
}
|
||||
|
||||
if *http2Option {
|
||||
http2.ConfigureServer(server, http2.ServerConfig{
|
||||
Debug: false,
|
||||
})
|
||||
}
|
||||
|
||||
log.Printf("Serving TCP on %s", *listenAddress)
|
||||
ln, err := net.Listen("tcp", *listenAddress)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
defer ln.Close()
|
||||
err = server.Serve(tls.NewListener(ln, server.TLSConfig.Clone()))
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
}(&wg)
|
||||
|
||||
if *http3Option {
|
||||
|
||||
wg.Add(1)
|
||||
go func(wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
server := &http3.Server{
|
||||
Server: &http.Server{
|
||||
Addr: *listenAddress,
|
||||
TLSConfig: &tls.Config{
|
||||
GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
if isSNIAllowed(info.ServerName) {
|
||||
return &serverCertificate, nil
|
||||
}
|
||||
return &bogusCertificate, nil
|
||||
},
|
||||
},
|
||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if debugOutput {
|
||||
log.Print("Received HTTP/3 request")
|
||||
}
|
||||
handle(NewRequestContextFromHttp(w, r))
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
h := http.Header{}
|
||||
server.SetQuicHeaders(h)
|
||||
extraHeadersMutex.Lock()
|
||||
extraHeaders["Alt-Svc"] = h.Get("Alt-Svc")
|
||||
extraHeadersMutex.Unlock()
|
||||
|
||||
log.Printf("Serving UDP on %s", *listenAddress)
|
||||
err = server.ListenAndServe()
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
}(&wg)
|
||||
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
|
7
go.mod
7
go.mod
|
@ -3,22 +3,22 @@ module git.gammaspectra.live/S.O.N.G/OrbitalBeat
|
|||
go 1.17
|
||||
|
||||
require (
|
||||
git.gammaspectra.live/S.O.N.G/MakyuuIchaival v0.0.0-20220118183219-7bfcd667a183
|
||||
github.com/cloudflare/circl v1.1.0
|
||||
github.com/dgrr/http2 v0.3.3
|
||||
github.com/ipfs/go-cid v0.1.0
|
||||
github.com/lib/pq v1.10.4
|
||||
github.com/lucas-clemente/quic-go v0.25.0
|
||||
github.com/multiformats/go-multihash v0.1.0
|
||||
github.com/valyala/fasthttp v1.32.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.2 // indirect
|
||||
github.com/cheekybits/genny v1.0.0 // indirect
|
||||
github.com/dgrr/http2 v0.3.3 // indirect
|
||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/klauspost/compress v1.13.4 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
|
||||
github.com/lucas-clemente/quic-go v0.25.0 // indirect
|
||||
github.com/marten-seemann/qpack v0.2.1 // indirect
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.4 // indirect
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.0 // indirect
|
||||
|
@ -34,6 +34,7 @@ require (
|
|||
github.com/onsi/ginkgo v1.16.4 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.32.0 // indirect
|
||||
github.com/valyala/fastrand v1.0.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
|
||||
golang.org/x/mod v0.4.2 // indirect
|
||||
|
|
2
go.sum
2
go.sum
|
@ -7,6 +7,8 @@ dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBr
|
|||
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
|
||||
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
git.gammaspectra.live/S.O.N.G/MakyuuIchaival v0.0.0-20220118183219-7bfcd667a183 h1:wF+oxs88n5p2O5ixy/vJN4KYefYOgwVmx+8a0xB7TPc=
|
||||
git.gammaspectra.live/S.O.N.G/MakyuuIchaival v0.0.0-20220118183219-7bfcd667a183/go.mod h1:z6KcP5RPhMxDJaVU48sBhiYRCJ6ZJBbx1iIhkUrrhfY=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/andybalholm/brotli v1.0.2 h1:JKnhI/XQ75uFBTiuzXpzFrUriDPiZjlOSzh6wXogP0E=
|
||||
github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
||||
|
|
Loading…
Reference in a new issue