hardfiles

- Unnamed repository; edit this file 'description' to name the repository.
git clone git://git.acid.vegas/-c.git
Log | Files | Refs | Archive | README | LICENSE

commit 7bb32e7c170134869e848fabfd71be9c8edc0228
Author: delorean <jackdelmar@protonmail.com>
Date: Sat, 30 Sep 2023 18:06:22 -0500

initial

Diffstat:
AREADME.md | 0
Aconfig.toml | 7+++++++
Ago.mod | 20++++++++++++++++++++
Ago.sum | 29+++++++++++++++++++++++++++++
Amain.go | 206+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Awww/fist.ico | 0
Awww/index.html | 155+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

7 files changed, 417 insertions(+), 0 deletions(-)

diff --git a/README.md b/README.md
diff --git a/config.toml b/config.toml
@@ -0,0 +1,6 @@
+webroot = "www"
+lport = "5000"
+vhost = "hardfiles.org"
+dbfile = "dbfile.db"
+filelen = 6
+folder = "files"
+\ No newline at end of file
diff --git a/go.mod b/go.mod
@@ -0,0 +1,20 @@
+module hardfiles
+
+go 1.21.0
+
+require (
+	github.com/boltdb/bolt v1.3.1
+	github.com/gabriel-vasile/mimetype v1.4.2
+	github.com/gorilla/mux v1.8.0
+	github.com/landlock-lsm/go-landlock v0.0.0-20230607164353-b03374193cb2
+	github.com/rs/zerolog v1.31.0
+)
+
+require (
+	github.com/BurntSushi/toml v1.3.2 // indirect
+	github.com/mattn/go-colorable v0.1.13 // indirect
+	github.com/mattn/go-isatty v0.0.19 // indirect
+	golang.org/x/net v0.8.0 // indirect
+	golang.org/x/sys v0.12.0 // indirect
+	kernel.org/pub/linux/libs/security/libcap/psx v1.2.66 // indirect
+)
diff --git a/go.sum b/go.sum
@@ -0,0 +1,29 @@
+github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
+github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
+github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
+github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
+github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
+github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
+github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
+github.com/landlock-lsm/go-landlock v0.0.0-20230607164353-b03374193cb2 h1:urvYVFXXrgiZWCYCQ6LWEE98QdU4Mvd5zwXuTllWMTg=
+github.com/landlock-lsm/go-landlock v0.0.0-20230607164353-b03374193cb2/go.mod h1:D25+lEYNcoxH7SgM/VOvWNrF3ZNAfRdydDuVbQBe6yE=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
+github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
+github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
+github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
+golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
+golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
+golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+kernel.org/pub/linux/libs/security/libcap/psx v1.2.66 h1:ikIhPzfkSSAEwBOU+2DWhoF+xnGUhvlMTfQjBVhvzQY=
+kernel.org/pub/linux/libs/security/libcap/psx v1.2.66/go.mod h1:+l6Ee2F59XiJ2I6WR5ObpC1utCQJZ/VLsEbQCD8RG24=
diff --git a/main.go b/main.go
@@ -0,0 +1,206 @@
+package main
+
+import (
+	"io"
+	"math/rand"
+	"net/http"
+	"os"
+	"strconv"
+	"time"
+
+	"github.com/BurntSushi/toml"
+	"github.com/boltdb/bolt"
+	"github.com/gabriel-vasile/mimetype"
+	"github.com/gorilla/mux"
+	"github.com/landlock-lsm/go-landlock/landlock"
+	"github.com/rs/zerolog"
+	"github.com/rs/zerolog/log"
+)
+
+var (
+	db   *bolt.DB
+	conf Config
+)
+
+type Config struct {
+	Webroot    string `toml:"webroot"`
+	LPort      string `toml:"lport"`
+	VHost      string `toml:"vhost"`
+	DBFile     string `toml:"dbfile"`
+	FileLen    int    `toml:"filelen"`
+	FileFolder string `toml:"folder"`
+}
+
+func LoadConf() {
+	if _, err := toml.DecodeFile("config.toml", &conf); err != nil {
+		log.Fatal().Err(err).Msg("unable to parse config.toml")
+	}
+}
+
+func NameGen() string {
+	const chars = "abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ123456789"
+	ll := len(chars)
+	b := make([]byte, conf.FileLen)
+	rand.Read(b) // generates len(b) random bytes
+	for i := int64(0); i < int64(conf.FileLen); i++ {
+		b[i] = chars[int(b[i])%ll]
+	}
+	return string(b)
+}
+
+func CheckFile(name string) bool { // false if doesn't exist, true if exists
+	tfd, err := os.Open(conf.FileFolder + "/" + name)
+	if err != nil {
+		return false
+	}
+	tfd.Close()
+	return true
+}
+
+func UploadHandler(w http.ResponseWriter, r *http.Request) {
+	// expiry sanitize
+	twentyfour := int64(86400)
+
+	file, _, err := r.FormFile("file")
+	if err != nil {
+		w.WriteHeader(http.StatusBadRequest)
+		return
+	}
+	defer file.Close()
+
+	mtype, err := mimetype.DetectReader(file)
+	if err != nil {
+		w.Write([]byte("error detecting the mime type of your file\n"))
+		return
+	}
+	file.Seek(0, 0)
+
+	// generate + check name
+	var name string
+	for {
+		id := NameGen()
+		name = id + mtype.Extension()
+		if !CheckFile(name) {
+			break
+		}
+	}
+
+	err = db.Update(func(tx *bolt.Tx) error {
+		b := tx.Bucket([]byte("expiry"))
+		err := b.Put([]byte(name), []byte(strconv.FormatInt(time.Now().Unix()+twentyfour, 10)))
+		return err
+	})
+	if err != nil {
+		log.Error().Err(err).Msg("Failed to put expiry")
+	}
+
+	log.Info().Int64("expiry", twentyfour).Msg("Writing new file")
+
+	f, err := os.OpenFile(conf.FileFolder+"/"+name, os.O_WRONLY|os.O_CREATE, 0644)
+	if err != nil {
+		log.Error().Err(err).Msg("Error opening a file for write")
+		w.WriteHeader(http.StatusInternalServerError) // change to json
+		return
+	}
+	defer f.Close()
+
+	io.Copy(f, file)
+
+	w.Write([]byte("https://" + conf.VHost + "/uploads/" + name))
+}
+
+func Cull() {
+	for {
+		removed := 0
+		db.Update(func(tx *bolt.Tx) error {
+			b := tx.Bucket([]byte("expiry"))
+			c := b.Cursor()
+			for k, v := c.First(); k != nil; k, v = c.Next() {
+				eol, err := strconv.ParseInt(string(v), 10, 64)
+				if err != nil {
+					log.Error().Err(err).Bytes("k", k).Bytes("v", v).Msg("expiration time could not be parsed")
+					continue
+				}
+				if time.Now().After(time.Unix(eol, 0)) {
+					os.Remove(conf.FileFolder + "/" + string(k))
+					removed += 1
+					c.Delete()
+				}
+			}
+			return nil
+		})
+		if removed >= 1 {
+			log.Info().Int("amount", removed).Msg("expired")
+		}
+		time.Sleep(5 * time.Second)
+	}
+}
+
+func main() {
+	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
+	LoadConf()
+
+	err := landlock.V2.BestEffort().RestrictPaths(
+		landlock.RWDirs("./"+conf.FileFolder),
+		landlock.RWFiles(conf.DBFile),
+		landlock.RWFiles(conf.Webroot+"/index.html"),
+	)
+
+	if err != nil {
+		log.Warn().Err(err).Msg("could not landlock")
+	}
+
+	_, err = os.Open("/etc/passwd")
+	if err == nil {
+		log.Warn().Msg("landlock failed, could open /etc/passwd")
+	} else {
+		log.Info().Err(err).Msg("Landlocked")
+	}
+
+	db, err = bolt.Open(conf.DBFile, 0600, nil)
+	if err != nil {
+		log.Fatal().Err(err).Msg("unable to open database file")
+	}
+	db.Update(func(tx *bolt.Tx) error {
+		_, err := tx.CreateBucketIfNotExists([]byte("expiry"))
+		if err != nil {
+			log.Fatal().Err(err).Msg("error creating expiry bucket")
+			return err
+		}
+		return nil
+	})
+
+	r := mux.NewRouter()
+	r.HandleFunc("/", UploadHandler).Methods("POST")
+	r.HandleFunc("/uploads/{name}", func(w http.ResponseWriter, r *http.Request) { // upload hits
+		vars := mux.Vars(r)
+		if !CheckFile(vars["name"]) {
+			w.WriteHeader(http.StatusNotFound)
+		} else {
+			http.ServeFile(w, r, conf.FileFolder+"/"+vars["name"])
+		}
+	}).Methods("GET")
+	r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		http.ServeFile(w, r, conf.Webroot+"/index.html")
+	}).Methods("GET")
+	http.Handle("/", r)
+
+	go Cull()
+
+	serv := &http.Server{
+		Addr:     ":" + conf.LPort,
+		Handler:  r,
+		ErrorLog: nil,
+		//ReadTimeout:  20 * time.Second,
+		//WriteTimeout: 20 * time.Second,
+		IdleTimeout: 20 * time.Second,
+	}
+
+	log.Info().Err(err).Msg("listening...")
+
+	if err := serv.ListenAndServe(); err != nil {
+		log.Fatal().Err(err).Msg("error starting server")
+	}
+
+	db.Close()
+}
diff --git a/www/fist.ico b/www/fist.ico
Binary files differ.
diff --git a/www/index.html b/www/index.html
@@ -0,0 +1,155 @@
+<!DOCTYPE html>
+<html>
+    <head>
+	<script type="text/javascript" src="//code.jquery.com/jquery-1.10.2.min.js"></script>
+        <meta name="viewport" content="width=device-width, initial-scale=1.0">
+        <link rel="icon" href="fist.ico">
+        <link rel="preconnect" href="https://fonts.googleapis.com">
+        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+        <link href="https://fonts.googleapis.com/css2?family=Black+Ops+One&display=swap" rel="stylesheet"> 
+        <title>HARDFILES</title>
+        <style>
+            body {
+            font-family: sans-serif;
+            background-image: url('https://media.tenor.com/fYnd0R6F-0UAAAAC/gun-revolver.gif');
+            background-size: cover;
+            }
+            @media (prefers-color-scheme: dark) {
+            body {
+            background: black;
+            background-image: url('https://media.tenor.com/fYnd0R6F-0UAAAAC/gun-revolver.gif');
+            background-size: cover;
+            background-position: center;
+            color: white;
+            }
+            }
+            .container {
+            padding-right: 15px;
+            padding-left: 15px;
+            margin-right: auto;
+            margin-left: auto;
+            display: flex;
+            flex-direction: column;
+            align-items: center;
+            justify-content: center;
+            height: 100vh;
+            }
+            @media (min-width: 768px) {
+            .container {
+            width: 750px;
+            }
+            .hf {
+                font-size: 3rem;
+            }
+            }
+            @media (min-width: 992px) {
+            .container {
+            width: 970px;
+            }
+
+            }
+            @media (min-width: 1200px) {
+            .container {
+            width: 1170px;
+            }
+            .hf {
+                font-size: 5rem;
+            }
+            }
+            input,select {
+            margin-bottom: 1em;
+            }
+            .file-upload{display:block;text-align:center;font-family: Helvetica, Arial, sans-serif;font-size: 12px; width: 300px;}
+            .file-upload .file-select{display:block;border: 2px solid #dce4ec;color: black;cursor:pointer;height:40px;line-height:40px;text-align:left;background:#FFFFFF;overflow:hidden;position:relative;}
+            .file-upload .file-select .file-select-button{background:#dce4ec;padding:0 10px;display:inline-block;height:40px;line-height:40px;}
+            .file-upload .file-select .file-select-name{line-height:40px;display:inline-block;padding:0 10px;}
+            .file-upload .file-select:hover{border-color:red;transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;}
+            .file-upload .file-select:hover .file-select-button{background:red;color:#FFFFFF;transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;}
+            .file-upload.active .file-select{border-color:red;transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;}
+            .file-upload.active .file-select .file-select-button{background:red;color:#FFFFFF;transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;}
+            .file-upload .file-select input[type=file]{z-index:100;cursor:pointer;position:absolute;height:100%;width:100%;top:0;left:0;opacity:0;filter:alpha(opacity=0);}
+            .file-upload .file-select.file-select-disabled{opacity:0.65;}
+            .file-upload .file-select.file-select-disabled:hover{cursor:default;display:block;border: 2px solid #dce4ec;color: red;cursor:pointer;height:40px;line-height:40px;margin-top:5px;text-align:left;background:#FFFFFF;overflow:hidden;position:relative;}
+            .file-upload .file-select.file-select-disabled:hover .file-select-button{background:#dce4ec;color:#666666;padding:0 10px;display:inline-block;height:40px;line-height:40px;}
+            .file-upload .file-select.file-select-disabled:hover .file-select-name{line-height:40px;display:inline-block;padding:0 10px;}
+            .subform {
+            display: flex;
+            flex-direction: column;
+            align-items: center;
+            }
+            button {
+            display: inline-block;
+            border: 0;
+            outline: 0;
+            margin: 0;
+            padding: 15px;
+            height: 44px;
+            color: #fff;
+            font: 19px/15px 'Oswald', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial, "Lucida Grande", sans-serif;
+            font-weight: bold;
+            text-transform: uppercase;
+            cursor: pointer;
+            -webkit-border-radius: 3px;
+            -moz-border-radius: 3px;
+            border-radius: 3px;
+            -webkit-box-sizing: border-box;     /* Safari/Chrome, other WebKit */
+            -moz-box-sizing: border-box;     /* Firefox, other Gecko */
+            box-sizing: border-box;     /* Opera/IE 8+ */
+            -webkit-font-smoothing:antialiased;
+            -webkit-text-size-adjust:none;
+            box-shadow: 0 1px 2px rgba(0,0,0,.15);
+            }
+            button:hover {
+            background: #c93c1d;
+            -webkit-transition:all .25s ease-in-out;
+            -moz-transition:all .25s ease-in-out;
+            -o-transition:all .25s ease-in-out;
+            transition:all .25s ease-in-out; 
+            }
+            button:active {
+            background: #ae3318;
+            }
+            button.light {
+            background: #fff;
+            color: #555759;
+            }
+            button.light:hover {
+            background: red;
+            color: #fff;
+            }
+        </style>
+    </head>
+    <body>
+        <div class="container">
+            <!-- <img src="fist.png" width="280px" alt="supernets"> -->
+            <h1 class="hf" style=" color: #660000; font-weight: 800; font-family: 'Black Ops One', cursive; margin-bottom: 0 !important; margin-top: 5rem !important;">HARDFILES</h1>
+            <h2 style="font-size: 1em; color: #ffffff; font-weight: 200;">curl -F file=@example.png https://hardfiles.org/</h1>
+            <form method="POST" class="subform" enctype="multipart/form-data">
+                <div class="file-upload" style="display: flex; justify-content: center; width: 25rem;">
+                    <div class="file-select" style="width: 100%;">
+                        <div class="file-select-button" id="fileName">Browse</div>
+                        <div class="file-select-name" id="noFile" style="cursor: pointer;">No file chosen...</div>
+                        <input type="file" name="file" id="chooseFile">
+                    </div>
+                    <button class="light" type="submit" style="margin-left: 0.7rem;">up</button>
+                </div>
+            </form>
+            <p style="color: white; font-weight: 300;">⚠️ Uploads are erased after 24 hours</p>
+        </div>
+    </body>
+</html>
+
+<script>
+$('#chooseFile').bind('change', function () {
+  var filename = $("#chooseFile").val();
+  if (/^\s*$/.test(filename)) {
+    $(".file-upload").removeClass('active');
+    $("#noFile").text("No file chosen...");
+  }
+  else {
+    $(".file-upload").addClass('active');
+    $("#noFile").text(filename.replace("C:\\fakepath\\", "")); 
+  }
+});
+</script>
+