Added constants.go
Changed configuration int64 types to int Fixed some constant scopes Better zero width checking to avoid unnecessary work
This commit is contained in:
parent
5c7ce1f0ed
commit
f00fe2b185
|
@ -14,24 +14,24 @@ type Configuration struct {
|
|||
|
||||
type storageConfig struct {
|
||||
Directory string
|
||||
MaxSize int64
|
||||
IDLength int64
|
||||
CollisionCheckAttempts int64
|
||||
MaxSize int
|
||||
IDLength int
|
||||
CollisionCheckAttempts int
|
||||
}
|
||||
|
||||
type rateLimitConfig struct {
|
||||
ResetAfter int64
|
||||
ResetAfter int
|
||||
Path struct {
|
||||
Upload int64
|
||||
Global int64
|
||||
Upload int
|
||||
Global int
|
||||
}
|
||||
Bandwidth rateLimitBandwidthConfig
|
||||
}
|
||||
|
||||
type rateLimitBandwidthConfig struct {
|
||||
ResetAfter int64
|
||||
Download int64
|
||||
Upload int64
|
||||
ResetAfter int
|
||||
Download int
|
||||
Upload int
|
||||
}
|
||||
|
||||
type filterConfig struct {
|
||||
|
@ -46,12 +46,12 @@ type securityConfig struct {
|
|||
}
|
||||
|
||||
type serverConfig struct {
|
||||
Port int64
|
||||
Concurrency int64
|
||||
Port int
|
||||
Concurrency int
|
||||
}
|
||||
|
||||
type redisConfig struct {
|
||||
URI string
|
||||
Password string
|
||||
DB int64
|
||||
DB int
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package constants
|
||||
|
||||
const (
|
||||
// Version is the current version of the server.
|
||||
Version = "1.3.3"
|
||||
|
||||
// ExtensionLengthLimit means that file extensions cannot have more characters than the number specified.
|
||||
// file.png has an extension of 4.
|
||||
ExtensionLengthLimit = 12
|
||||
)
|
||||
|
||||
// PathType is an integer representation of what path is currently being handled.
|
||||
// Used mainly by constants.LimitPath.
|
||||
type PathType int
|
||||
|
||||
const (
|
||||
// LimitUploadPath represents /upload.
|
||||
LimitUploadPath PathType = iota
|
||||
|
||||
// LimitGeneralPath represents /general.
|
||||
LimitGeneralPath
|
||||
)
|
||||
|
||||
// sus imposter
|
||||
var PathLengthLimitBytes int
|
|
@ -10,8 +10,3 @@ var Configuration *api.Configuration
|
|||
|
||||
// RedisClient holds the Redis client used to communicate with Redis databases.
|
||||
var RedisClient *redis.Client
|
||||
|
||||
const (
|
||||
// Version is the current version of the server.
|
||||
Version = "1.3.2"
|
||||
)
|
||||
|
|
9
init.go
9
init.go
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/vysiondev/tytanium/constants"
|
||||
"github.com/vysiondev/tytanium/global"
|
||||
"log"
|
||||
"os"
|
||||
|
@ -16,7 +17,7 @@ const (
|
|||
)
|
||||
|
||||
func init() {
|
||||
log.Print("* Tytanium " + global.Version + "\n\n")
|
||||
log.Print("* Tytanium " + constants.Version + "\n\n")
|
||||
initConfiguration()
|
||||
checkStorage()
|
||||
initRedis()
|
||||
|
@ -62,6 +63,12 @@ func initConfiguration() {
|
|||
}
|
||||
}
|
||||
|
||||
// - ID length * 4 bytes,
|
||||
// - extension length limit * 4 bytes,
|
||||
// - 1 byte for the / character,
|
||||
// - 4 bytes for the . character
|
||||
constants.PathLengthLimitBytes = (global.Configuration.Storage.IDLength * 4) + (constants.ExtensionLengthLimit * 4) + 5
|
||||
|
||||
log.Println("Loaded configuration")
|
||||
}
|
||||
|
||||
|
|
2
main.go
2
main.go
|
@ -21,7 +21,7 @@ func main() {
|
|||
Handler: middleware.HandleCORS(middleware.LimitPath(middleware.HandleHTTPRequest)),
|
||||
HeaderReceived: nil,
|
||||
ContinueHandler: nil,
|
||||
Concurrency: int(global.Configuration.Server.Concurrency),
|
||||
Concurrency: global.Configuration.Server.Concurrency,
|
||||
DisableKeepalive: false,
|
||||
ReadTimeout: 3 * time.Second,
|
||||
TCPKeepalive: false,
|
||||
|
|
|
@ -3,6 +3,7 @@ package middleware
|
|||
import (
|
||||
"fmt"
|
||||
"github.com/valyala/fasthttp"
|
||||
"github.com/vysiondev/tytanium/constants"
|
||||
"github.com/vysiondev/tytanium/global"
|
||||
"github.com/vysiondev/tytanium/response"
|
||||
"github.com/vysiondev/tytanium/routes"
|
||||
|
@ -12,18 +13,6 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// PathType is an integer representation of what path is currently being handled.
|
||||
// Used mainly by LimitPath.
|
||||
type PathType int
|
||||
|
||||
const (
|
||||
// LimitUploadPath represents /upload.
|
||||
LimitUploadPath PathType = iota
|
||||
|
||||
// LimitGeneralPath represents /general.
|
||||
LimitGeneralPath
|
||||
)
|
||||
|
||||
// LimitPath generally handles all paths.
|
||||
// IPs will be stored like 0_192.168.1.1 for path 0, 1_192.168.1.1 for path 1, and so on.
|
||||
// Bandwidth checking for uploading is set as BW_UP_192.168.1.1, for another example.
|
||||
|
@ -34,12 +23,12 @@ func LimitPath(h fasthttp.RequestHandler) fasthttp.RequestHandler {
|
|||
h(ctx)
|
||||
} else {
|
||||
p := string(ctx.Request.URI().Path())
|
||||
pathType := LimitGeneralPath
|
||||
pathType := constants.LimitGeneralPath
|
||||
reqLimit := global.Configuration.RateLimit.Path.Global
|
||||
|
||||
switch strings.ToLower(p) {
|
||||
case "/upload":
|
||||
pathType = LimitUploadPath
|
||||
pathType = constants.LimitUploadPath
|
||||
reqLimit = global.Configuration.RateLimit.Path.Upload
|
||||
}
|
||||
if reqLimit <= 0 {
|
||||
|
@ -47,7 +36,7 @@ func LimitPath(h fasthttp.RequestHandler) fasthttp.RequestHandler {
|
|||
} else {
|
||||
rlString := ""
|
||||
// Check the global rate limit
|
||||
isGlobalRateLimitOk, err := security.Try(ctx, global.RedisClient, fmt.Sprintf("G_%s", ip), global.Configuration.RateLimit.Path.Global, global.Configuration.RateLimit.ResetAfter, 1)
|
||||
isGlobalRateLimitOk, err := security.Try(ctx, global.RedisClient, fmt.Sprintf("G_%s", ip), int64(global.Configuration.RateLimit.Path.Global), int64(global.Configuration.RateLimit.ResetAfter), 1)
|
||||
if err != nil {
|
||||
response.SendTextResponse(ctx, "Failed to call Try() to get information on global rate limit. "+err.Error(), fasthttp.StatusInternalServerError)
|
||||
return
|
||||
|
@ -56,9 +45,9 @@ func LimitPath(h fasthttp.RequestHandler) fasthttp.RequestHandler {
|
|||
rlString = "Global"
|
||||
}
|
||||
|
||||
if pathType != LimitGeneralPath {
|
||||
if pathType != constants.LimitGeneralPath {
|
||||
// Check the route exclusive rate limit
|
||||
isPathOk, err := security.Try(ctx, global.RedisClient, fmt.Sprintf("%d_%s", pathType, ip), reqLimit, global.Configuration.RateLimit.ResetAfter, 1)
|
||||
isPathOk, err := security.Try(ctx, global.RedisClient, fmt.Sprintf("%d_%s", pathType, ip), int64(reqLimit), int64(global.Configuration.RateLimit.ResetAfter), 1)
|
||||
if err != nil {
|
||||
response.SendTextResponse(ctx, "Failed to call Try() to get information on path-specific rate limit. "+err.Error(), fasthttp.StatusInternalServerError)
|
||||
return
|
||||
|
|
|
@ -8,13 +8,13 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
PlainTextContentType = "text/plain; charset=utf8"
|
||||
JsonContentType = "application/json"
|
||||
plainTextContentType = "text/plain; charset=utf8"
|
||||
jsonContentType = "application/json"
|
||||
)
|
||||
|
||||
// SendTextResponse sends a plaintext response to the client along with an HTTP status code.
|
||||
func SendTextResponse(ctx *fasthttp.RequestCtx, msg string, code int) {
|
||||
ctx.Response.Header.SetContentType(PlainTextContentType)
|
||||
ctx.Response.Header.SetContentType(plainTextContentType)
|
||||
if code == fasthttp.StatusInternalServerError {
|
||||
log.Printf(fmt.Sprintf("Unhandled error!, %s", msg))
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ func SendTextResponse(ctx *fasthttp.RequestCtx, msg string, code int) {
|
|||
|
||||
// SendJSONResponse sends a JSON encoded response to the client along with an HTTP status code of 200 OK.
|
||||
func SendJSONResponse(ctx *fasthttp.RequestCtx, json interface{}) {
|
||||
ctx.SetContentType(JsonContentType)
|
||||
ctx.SetContentType(jsonContentType)
|
||||
e := json2.NewEncoder(ctx.Response.BodyWriter()).Encode(json)
|
||||
if e != nil {
|
||||
log.Printf(fmt.Sprintf("JSON failed to send! %v", e))
|
||||
|
|
|
@ -4,12 +4,12 @@ import (
|
|||
"fmt"
|
||||
"github.com/gabriel-vasile/mimetype"
|
||||
"github.com/valyala/fasthttp"
|
||||
"github.com/vysiondev/tytanium/constants"
|
||||
"github.com/vysiondev/tytanium/global"
|
||||
"github.com/vysiondev/tytanium/response"
|
||||
"github.com/vysiondev/tytanium/security"
|
||||
"github.com/vysiondev/tytanium/utils"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
|
@ -17,7 +17,10 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
const rawParam = "raw"
|
||||
const (
|
||||
rawParam = "raw"
|
||||
ZeroWidthCharacterFirstByte = 243
|
||||
)
|
||||
|
||||
// discordHTML represents what is sent back to any client which User-Agent contains the regex contained in
|
||||
// discordBotRegex.
|
||||
|
@ -40,15 +43,26 @@ var (
|
|||
// ServeFile will serve the / endpoint. It gets the "id" variable from mux and tries to find the file's information in the database.
|
||||
// If an ID is either not provided or not found, the function hands the request off to ServeNotFound.
|
||||
func ServeFile(ctx *fasthttp.RequestCtx) {
|
||||
p := string(ctx.Request.URI().Path())
|
||||
if len(p) == 0 {
|
||||
ServeNotFound(ctx)
|
||||
pBytes := ctx.Request.URI().Path()
|
||||
|
||||
if len(pBytes) > constants.PathLengthLimitBytes {
|
||||
response.SendTextResponse(ctx, "Path is too long.", fasthttp.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if len(pBytes) <= 1 {
|
||||
response.SendTextResponse(ctx, "Path is too short.", fasthttp.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
p := string(pBytes[1:])
|
||||
// Convert entire path to normal string if a zero-width character is detected at the beginning.
|
||||
// %2F = /, %F3%A0 = part of zero width character
|
||||
if strings.HasPrefix(url.QueryEscape(p), "%2F%F3%A0") {
|
||||
p = utils.ZWSToString(p)
|
||||
if pBytes[1] == ZeroWidthCharacterFirstByte {
|
||||
p = utils.StringToZeroWidthCharacters(p)
|
||||
if len(p) == 0 {
|
||||
response.SendTextResponse(ctx, "Malformed zero-width URL path.", fasthttp.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
filePath := path.Join(global.Configuration.Storage.Directory, p)
|
||||
|
@ -80,7 +94,7 @@ func ServeFile(ctx *fasthttp.RequestCtx) {
|
|||
}()
|
||||
|
||||
if global.Configuration.RateLimit.Bandwidth.Download > 0 && global.Configuration.RateLimit.Bandwidth.ResetAfter > 0 {
|
||||
isBandwidthLimitNotReached, err := security.Try(ctx, global.RedisClient, fmt.Sprintf("BW_DN_%s", utils.GetIP(ctx)), global.Configuration.RateLimit.Bandwidth.Download, global.Configuration.RateLimit.Bandwidth.ResetAfter, fileInfo.Size())
|
||||
isBandwidthLimitNotReached, err := security.Try(ctx, global.RedisClient, fmt.Sprintf("BW_DN_%s", utils.GetIP(ctx)), int64(global.Configuration.RateLimit.Bandwidth.Download), int64(global.Configuration.RateLimit.Bandwidth.ResetAfter), fileInfo.Size())
|
||||
if err != nil {
|
||||
response.SendTextResponse(ctx, fmt.Sprintf("Bandwidth limit couldn't be checked. %v", err), fasthttp.StatusInternalServerError)
|
||||
return
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/valyala/fasthttp"
|
||||
"github.com/vysiondev/tytanium/constants"
|
||||
"github.com/vysiondev/tytanium/global"
|
||||
"github.com/vysiondev/tytanium/response"
|
||||
"runtime"
|
||||
|
@ -29,7 +30,7 @@ type StatsFromSizeChecker struct {
|
|||
// ServeStats serves stats. StatsFromSizeChecker are populated into redis by https://github.com/vysiondev/size-checker.
|
||||
func ServeStats(ctx *fasthttp.RequestCtx) {
|
||||
var stats GeneralStats
|
||||
stats.ServerVersion = global.Version
|
||||
stats.ServerVersion = constants.Version
|
||||
|
||||
totalSize, err := getStatValueFromRedis(ctx, global.RedisClient, "sc_total_size")
|
||||
if err != nil {
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"github.com/gabriel-vasile/mimetype"
|
||||
"github.com/valyala/fasthttp"
|
||||
"github.com/vysiondev/tytanium/constants"
|
||||
"github.com/vysiondev/tytanium/global"
|
||||
"github.com/vysiondev/tytanium/response"
|
||||
"github.com/vysiondev/tytanium/security"
|
||||
|
@ -40,7 +41,7 @@ func ServeUpload(ctx *fasthttp.RequestCtx) {
|
|||
f := mp.File[fileHandler][0]
|
||||
|
||||
if global.Configuration.RateLimit.Bandwidth.Upload > 0 && global.Configuration.RateLimit.Bandwidth.ResetAfter > 0 {
|
||||
isUploadBandwidthLimitNotReached, err := security.Try(ctx, global.RedisClient, fmt.Sprintf("BW_UP_%s", utils.GetIP(ctx)), global.Configuration.RateLimit.Bandwidth.Upload, global.Configuration.RateLimit.Bandwidth.ResetAfter, f.Size)
|
||||
isUploadBandwidthLimitNotReached, err := security.Try(ctx, global.RedisClient, fmt.Sprintf("BW_UP_%s", utils.GetIP(ctx)), int64(global.Configuration.RateLimit.Bandwidth.Upload), int64(global.Configuration.RateLimit.Bandwidth.ResetAfter), f.Size)
|
||||
if err != nil {
|
||||
response.SendTextResponse(ctx, fmt.Sprintf("Bandwidth limit couldn't be checked. %v", err), fasthttp.StatusInternalServerError)
|
||||
return
|
||||
|
@ -51,6 +52,13 @@ func ServeUpload(ctx *fasthttp.RequestCtx) {
|
|||
}
|
||||
}
|
||||
|
||||
ext := path.Ext(f.Filename)
|
||||
|
||||
if len(ext) > constants.ExtensionLengthLimit {
|
||||
response.SendTextResponse(ctx, "The file extension is too long.", fasthttp.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
openedFile, e := f.Open()
|
||||
if e != nil {
|
||||
response.SendTextResponse(ctx, fmt.Sprintf("File failed to open. %v", e), fasthttp.StatusInternalServerError)
|
||||
|
@ -87,11 +95,12 @@ func ServeUpload(ctx *fasthttp.RequestCtx) {
|
|||
randomStringChan := make(chan string, 1)
|
||||
go func() {
|
||||
wg.Add(1)
|
||||
utils.RandBytes(int(global.Configuration.Storage.IDLength), randomStringChan, func() { wg.Done() })
|
||||
utils.RandBytes(global.Configuration.Storage.IDLength, randomStringChan, func() { wg.Done() })
|
||||
}()
|
||||
wg.Wait()
|
||||
fileId := <-randomStringChan
|
||||
fileName = fileId + path.Ext(f.Filename)
|
||||
|
||||
fileName = fileId + ext
|
||||
|
||||
i, e := os.Stat(path.Join(global.Configuration.Storage.Directory, fileName))
|
||||
if e != nil {
|
||||
|
@ -103,7 +112,7 @@ func ServeUpload(ctx *fasthttp.RequestCtx) {
|
|||
break
|
||||
}
|
||||
attempts++
|
||||
if attempts >= int(global.Configuration.Storage.CollisionCheckAttempts) {
|
||||
if attempts >= global.Configuration.Storage.CollisionCheckAttempts {
|
||||
response.SendTextResponse(ctx, "Tried too many times to find a valid file ID to use. Consider increasing the ID length.", fasthttp.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
@ -130,7 +139,7 @@ func ServeUpload(ctx *fasthttp.RequestCtx) {
|
|||
}
|
||||
|
||||
if global.Configuration.ForceZeroWidth || string(ctx.QueryArgs().Peek("zerowidth")) == "1" {
|
||||
fileName = utils.StringToZWS(fileName)
|
||||
fileName = utils.ZeroWidthCharactersToString(fileName)
|
||||
}
|
||||
|
||||
var u string
|
||||
|
|
|
@ -4,13 +4,13 @@ import "github.com/valyala/fasthttp"
|
|||
|
||||
const (
|
||||
// CloudflareForwardedIP is the client's original IP given by CloudFlare in the request header.
|
||||
CloudflareForwardedIP = "CF-Connecting-IP"
|
||||
cloudflareForwardedIP = "CF-Connecting-IP"
|
||||
)
|
||||
|
||||
// GetIP gets the forwarded IP from Cloudflare if it's available,
|
||||
// or gets the remote IP as given by fasthttp.RequestCtx as a fallback.
|
||||
func GetIP(ctx *fasthttp.RequestCtx) string {
|
||||
forwardedIP := ctx.Request.Header.Peek(CloudflareForwardedIP)
|
||||
forwardedIP := ctx.Request.Header.Peek(cloudflareForwardedIP)
|
||||
if len(forwardedIP) != 0 {
|
||||
return string(forwardedIP)
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ import (
|
|||
"unsafe"
|
||||
)
|
||||
|
||||
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
const (
|
||||
letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
letterIdxBits = 6 // 6 bits to represent a letter index
|
||||
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
|
||||
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
|
||||
|
|
10
utils/zws.go
10
utils/zws.go
|
@ -1,7 +1,6 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -33,7 +32,7 @@ func GetCharacterIndex(s string) int {
|
|||
return strings.Index(characterIndex, s)
|
||||
}
|
||||
|
||||
func StringToZWS(baseStr string) string {
|
||||
func ZeroWidthCharactersToString(baseStr string) string {
|
||||
|
||||
var completedStr string
|
||||
|
||||
|
@ -50,13 +49,12 @@ func StringToZWS(baseStr string) string {
|
|||
}
|
||||
|
||||
// what else am i supposed to call it dumbass
|
||||
func ZWSToString(encodedStr string) string {
|
||||
func StringToZeroWidthCharacters(encodedStr string) string {
|
||||
|
||||
rL := []rune(encodedStr)
|
||||
fmt.Println(len(rL))
|
||||
var finalStr string
|
||||
|
||||
for ind, r := range rL {
|
||||
for _, r := range rL {
|
||||
match := false
|
||||
for i, v := range characterReference {
|
||||
if []rune(v)[0] == r {
|
||||
|
@ -66,7 +64,7 @@ func ZWSToString(encodedStr string) string {
|
|||
}
|
||||
}
|
||||
if !match {
|
||||
finalStr += string(rL[ind])
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue