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:
vysion 2021-09-13 21:59:45 -07:00
parent 5c7ce1f0ed
commit f00fe2b185
13 changed files with 102 additions and 64 deletions

View File

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

25
constants/constants.go Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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