2020-11-18 23:47:05 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/sha256"
|
|
|
|
"encoding/hex"
|
|
|
|
"encoding/json"
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"math/rand"
|
|
|
|
"net/http"
|
2020-11-19 00:38:53 +00:00
|
|
|
"os"
|
|
|
|
"strconv"
|
2020-11-18 23:47:05 +00:00
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2020-11-19 00:38:53 +00:00
|
|
|
var chunkSize = 2
|
2020-11-18 23:47:05 +00:00
|
|
|
var currentChunk string
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
rand.Seed(time.Now().UnixNano())
|
|
|
|
}
|
|
|
|
|
|
|
|
func newChunk() {
|
|
|
|
bid := make([]byte, 4)
|
|
|
|
rand.Read(bid)
|
|
|
|
currentChunk = hex.EncodeToString(bid)[:chunkSize]
|
|
|
|
}
|
|
|
|
|
|
|
|
type uploadChunk struct {
|
|
|
|
Proof string `json:"proof"`
|
|
|
|
Login string `json:"login"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkChunk(rndb string) bool {
|
|
|
|
rnd, err := hex.DecodeString(rndb)
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
sum := sha256.Sum256(append(rnd, currentChunk[0]))
|
|
|
|
return strings.HasPrefix(hex.EncodeToString(sum[:]), currentChunk[1:])
|
|
|
|
}
|
|
|
|
|
|
|
|
func ServeChunk(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if addr := r.Header.Get("X-Forwarded-For"); addr != "" {
|
|
|
|
r.RemoteAddr = addr
|
|
|
|
}
|
|
|
|
log.Printf("Handling %s request from %s: %s [%s]\n", r.Method, r.RemoteAddr, r.URL.Path, r.UserAgent())
|
|
|
|
|
|
|
|
w.Header().Set("Content-Type", "text/plain")
|
|
|
|
|
|
|
|
if r.Method == "POST" {
|
|
|
|
if r.ContentLength < 0 || r.ContentLength > 6553600 {
|
|
|
|
http.Error(w, fmt.Sprintf("{errmsg:\"Request too large or request size unknown\"}"), http.StatusRequestEntityTooLarge)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var body []byte
|
|
|
|
if r.ContentLength > 0 {
|
|
|
|
tmp := make([]byte, 1024)
|
|
|
|
for {
|
|
|
|
n, err := r.Body.Read(tmp)
|
|
|
|
for j := 0; j < n; j++ {
|
|
|
|
body = append(body, tmp[j])
|
|
|
|
}
|
|
|
|
if err != nil || n <= 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var uc uploadChunk
|
|
|
|
if err := json.Unmarshal(body, &uc); err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
w.Write([]byte(err.Error()))
|
|
|
|
} else if checkChunk(uc.Proof) {
|
|
|
|
newChunk()
|
|
|
|
log.Println("Good chunk from", uc.Login)
|
|
|
|
if _, err := DBExec("INSERT INTO chunks (time, username, chunk, proof) VALUES (?, ?, ?, ?)", time.Now(), uc.Login, currentChunk, uc.Proof); err != nil {
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
w.Write([]byte(err.Error()))
|
|
|
|
} else {
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
w.Write([]byte("true"))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
log.Println("BAD chunk from", uc.Login)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
w.Write([]byte(currentChunk))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-19 01:52:54 +00:00
|
|
|
type Score struct {
|
|
|
|
Login string `json:"login"`
|
|
|
|
NbChunk int `json:"chunks"`
|
|
|
|
Time time.Time `json:"time"`
|
|
|
|
}
|
|
|
|
|
2020-11-18 23:47:05 +00:00
|
|
|
func ServeScores(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if addr := r.Header.Get("X-Forwarded-For"); addr != "" {
|
|
|
|
r.RemoteAddr = addr
|
|
|
|
}
|
|
|
|
log.Printf("Handling %s request from %s: %s [%s]\n", r.Method, r.RemoteAddr, r.URL.Path, r.UserAgent())
|
|
|
|
|
2021-11-19 01:52:54 +00:00
|
|
|
isJson := false
|
|
|
|
if strings.HasSuffix(r.URL.Path, "/scores.json") {
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
isJson = true
|
|
|
|
} else {
|
|
|
|
w.Header().Set("Content-Type", "text/plain")
|
|
|
|
}
|
2020-11-18 23:47:05 +00:00
|
|
|
|
|
|
|
if rows, err := DBQuery("SELECT username, COUNT(id_chunk) AS nbchunk, MAX(time) AS time FROM chunks GROUP BY username ORDER BY nbchunk DESC"); err != nil {
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
w.Write([]byte(err.Error()))
|
|
|
|
} else {
|
|
|
|
defer rows.Close()
|
|
|
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
2021-11-19 01:52:54 +00:00
|
|
|
var scores []Score
|
|
|
|
|
2020-11-18 23:47:05 +00:00
|
|
|
for rows.Next() {
|
2021-11-19 01:52:54 +00:00
|
|
|
var score Score
|
2020-11-18 23:47:05 +00:00
|
|
|
|
2021-11-19 01:52:54 +00:00
|
|
|
if err := rows.Scan(&score.Login, &score.NbChunk, &score.Time); err == nil {
|
|
|
|
if isJson {
|
|
|
|
scores = append(scores, score)
|
|
|
|
} else {
|
|
|
|
w.Write([]byte(fmt.Sprintf("%q,%d,%q\n", score.Login, score.NbChunk, score.Time)))
|
|
|
|
}
|
2020-11-18 23:47:05 +00:00
|
|
|
}
|
|
|
|
}
|
2021-11-19 01:52:54 +00:00
|
|
|
|
|
|
|
if isJson {
|
|
|
|
res, _ := json.Marshal(scores)
|
|
|
|
w.Write(res)
|
|
|
|
}
|
2020-11-18 23:47:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
2020-11-19 00:38:53 +00:00
|
|
|
if v, exists := os.LookupEnv("CHUNKSIZE"); exists {
|
|
|
|
if cs, err := strconv.Atoi(v); err == nil {
|
|
|
|
chunkSize = cs
|
|
|
|
}
|
|
|
|
}
|
2020-11-18 23:47:05 +00:00
|
|
|
var bind = flag.String("bind", "0.0.0.0:8081", "Bind port/socket")
|
|
|
|
var dsn = flag.String("dsn", DSNGenerator(), "DSN to connect to the MySQL server")
|
2020-11-19 00:38:53 +00:00
|
|
|
flag.IntVar(&chunkSize, "chunkSize", chunkSize, "Taille du chunk à trouver")
|
2020-11-18 23:47:05 +00:00
|
|
|
flag.Parse()
|
|
|
|
newChunk()
|
|
|
|
|
|
|
|
// Database connection
|
|
|
|
log.Println("Opening database...")
|
|
|
|
if err := DBInit(*dsn); err != nil {
|
|
|
|
log.Fatal("Cannot open the database: ", err)
|
|
|
|
}
|
|
|
|
defer DBClose()
|
|
|
|
|
|
|
|
log.Println("Creating database...")
|
|
|
|
if err := DBCreate(); err != nil {
|
|
|
|
log.Fatal("Cannot create database: ", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Println("Registering handlers...")
|
|
|
|
http.HandleFunc("/scores", ServeScores)
|
2021-11-19 01:52:54 +00:00
|
|
|
http.HandleFunc("/scores.json", ServeScores)
|
2020-11-18 23:47:05 +00:00
|
|
|
http.HandleFunc("/chunk", ServeChunk)
|
|
|
|
|
|
|
|
log.Println(fmt.Sprintf("Ready, listening on %s", *bind))
|
|
|
|
if err := http.ListenAndServe(*bind, nil); err != nil {
|
|
|
|
log.Fatal("Unable to listen and serve: ", err)
|
|
|
|
}
|
|
|
|
}
|