261 lines
6.9 KiB
Go
261 lines
6.9 KiB
Go
package core
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gorilla/mux"
|
|
"golang.org/x/crypto/acme"
|
|
|
|
"github.com/kgretzky/pwndrop/api"
|
|
"github.com/kgretzky/pwndrop/log"
|
|
"github.com/kgretzky/pwndrop/storage"
|
|
)
|
|
|
|
const (
|
|
API_PATH = "api/v1"
|
|
)
|
|
|
|
type Server struct {
|
|
srv *http.Server
|
|
listenTLS net.Listener
|
|
listen net.Listener
|
|
wdav *WebDav
|
|
http *Http
|
|
cdb *CertDb
|
|
ns *Nameserver
|
|
r *mux.Router
|
|
}
|
|
|
|
func NewServer(host string, port_plain int, port_tls int, enable_letsencrypt bool, enable_dns bool, ch_exit *chan bool) (*Server, error) {
|
|
var err error
|
|
s := &Server{}
|
|
|
|
hostname := fmt.Sprintf("%s:%d", host, port_plain)
|
|
hostname_tls := fmt.Sprintf("%s:%d", host, port_tls)
|
|
|
|
s.cdb, err = NewCertDb(Cfg.GetDataDir())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cert, err := LoadTLSCertificate(filepath.Join(Cfg.GetDataDir(), "public.crt"), filepath.Join(Cfg.GetDataDir(), "private.key"))
|
|
if err != nil {
|
|
log.Warning("certificate: %s", err)
|
|
cert, err = GenerateTLSCertificate(host)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
log.Info("generated self-signed certificate")
|
|
} else {
|
|
log.Info("using TLS certificate from data directory")
|
|
enable_letsencrypt = false
|
|
}
|
|
|
|
tls_cfg := &tls.Config{}
|
|
tls_cfg.Certificates = append(tls_cfg.Certificates, *cert)
|
|
if enable_letsencrypt {
|
|
log.Info("autocert: enabled")
|
|
tls_cfg.GetCertificate = s.cdb.AutocertMgr.GetCertificate
|
|
tls_cfg.NextProtos = []string{
|
|
"h2", "http/1.1", // enable HTTP/2
|
|
acme.ALPNProto, // enable tls-alpn ACME challenges
|
|
}
|
|
} else {
|
|
log.Info("autocert: disabled")
|
|
}
|
|
|
|
s.wdav, err = NewWebDav(s)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
s.http, err = NewHttp(s)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
s.setupRouter()
|
|
|
|
s.srv = &http.Server{
|
|
Handler: http.Handler(s),
|
|
Addr: hostname,
|
|
WriteTimeout: 15 * time.Second,
|
|
ReadTimeout: 15 * time.Second,
|
|
TLSConfig: tls_cfg,
|
|
}
|
|
|
|
s.listenTLS, err = tls.Listen("tcp", hostname_tls, tls_cfg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
s.listen, err = net.Listen("tcp", hostname)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
log.Info("starting HTTP/WebDAV server at %s", hostname)
|
|
log.Info("starting HTTPS server at %s", hostname_tls)
|
|
|
|
if enable_dns {
|
|
s.ns, err = NewNameserver(ch_exit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
go func() {
|
|
err := s.srv.Serve(s.listen)
|
|
if err != nil {
|
|
log.Fatal("failed to start HTTP/WebDAV server at %s", hostname)
|
|
*ch_exit <- false
|
|
}
|
|
}()
|
|
|
|
go func() {
|
|
err := s.srv.Serve(s.listenTLS)
|
|
if err != nil {
|
|
log.Fatal("failed to start HTTPS server at %s", hostname_tls)
|
|
*ch_exit <- false
|
|
}
|
|
}()
|
|
|
|
return s, nil
|
|
}
|
|
|
|
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
log.Debug("%s %s", r.Method, r.URL.Path)
|
|
|
|
if !s.isWebDavRequest(r) {
|
|
|
|
cookie_name := Cfg.GetCookieName()
|
|
cookie_token := Cfg.GetCookieToken()
|
|
|
|
if r.URL.Path == Cfg.GetSecretPath() {
|
|
ck := &http.Cookie{
|
|
Domain: "",
|
|
Path: "/",
|
|
Expires: time.Now().AddDate(0, 3, 0),
|
|
HttpOnly: true,
|
|
Name: cookie_name,
|
|
Value: cookie_token,
|
|
}
|
|
http.SetCookie(w, ck)
|
|
http.Redirect(w, r, "/", http.StatusFound)
|
|
return
|
|
}
|
|
|
|
if !s.FileExists(r.URL.Path) {
|
|
if ck, err := r.Cookie(cookie_name); err == nil {
|
|
if ck.Value == cookie_token {
|
|
s.r.ServeHTTP(w, r)
|
|
return
|
|
}
|
|
}
|
|
|
|
if len(Cfg.GetRedirectUrl()) > 0 {
|
|
http.Redirect(w, r, Cfg.GetRedirectUrl(), http.StatusFound)
|
|
return
|
|
}
|
|
}
|
|
|
|
// http
|
|
s.http.ServeHTTP(w, r)
|
|
} else {
|
|
// webdav
|
|
s.wdav.Handler().ServeHTTP(w, r)
|
|
}
|
|
}
|
|
|
|
func (s *Server) isWebDavRequest(r *http.Request) bool {
|
|
ua := r.Header.Get("user-agent")
|
|
if strings.Index(ua, "WebDAV") >= 0 || strings.Index(ua, "DavClnt") >= 0 {
|
|
return true
|
|
}
|
|
if r.Header.Get("translate") == "f" {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (s *Server) setupRouter() {
|
|
admin_path := "/"
|
|
s.r = mux.NewRouter()
|
|
sr := s.r.PathPrefix(admin_path + API_PATH).Subrouter()
|
|
sr.HandleFunc("/auth", api.AuthOptionsHandler).Methods("OPTIONS")
|
|
sr.HandleFunc("/auth", api.AuthCheckHandler).Methods("GET")
|
|
sr.HandleFunc("/server_info", api.ServerInfoOptionsHandler).Methods("OPTIONS")
|
|
sr.HandleFunc("/server_info", api.ServerInfoGetHandler).Methods("GET")
|
|
sr.HandleFunc("/version", api.VersionOptionsHandler).Methods("OPTIONS")
|
|
sr.HandleFunc("/version", api.VersionGetHandler).Methods("GET")
|
|
sr.HandleFunc("/login", api.AuthOptionsHandler).Methods("OPTIONS")
|
|
sr.HandleFunc("/login", api.LoginUserHandler).Methods("POST")
|
|
sr.HandleFunc("/logout", api.LogoutUserHandler).Methods("GET")
|
|
sr.HandleFunc("/clear_secret", api.ClearSecretSessionHandler).Methods("GET")
|
|
sr.HandleFunc("/create_account", api.AuthOptionsHandler).Methods("OPTIONS")
|
|
sr.HandleFunc("/create_account", api.CreateUserHandler).Methods("POST")
|
|
sr.HandleFunc("/config", api.ConfigOptionsHandler).Methods("OPTIONS")
|
|
sr.HandleFunc("/config", api.ConfigGetHandler).Methods("GET")
|
|
sr.HandleFunc("/config", api.ConfigUpdateHandler).Methods("POST")
|
|
sr.HandleFunc("/files", api.FileOptionsHandler).Methods("OPTIONS")
|
|
sr.HandleFunc("/files", api.FileListHandler).Methods("GET")
|
|
sr.HandleFunc("/files", api.FileCreateHandler).Methods("POST")
|
|
sr.HandleFunc("/files/{id}", api.FileDeleteHandler).Methods("DELETE")
|
|
sr.HandleFunc("/files/{id}", api.FileUpdateHandler).Methods("PUT")
|
|
sr.HandleFunc("/files/{id}/sub", api.SubFileCreateHandler).Methods("POST")
|
|
sr.HandleFunc("/files/{id}/sub/{sub_id}", api.SubFileDeleteHandler).Methods("DELETE")
|
|
sr.HandleFunc("/files/{id}/enable", api.FileEnableHandler).Methods("GET")
|
|
sr.HandleFunc("/files/{id}/disable", api.FileDisableHandler).Methods("GET")
|
|
sr.HandleFunc("/files/{id}/pause", api.FilePauseHandler).Methods("GET")
|
|
sr.HandleFunc("/files/{id}/unpause", api.FileUnpauseHandler).Methods("GET")
|
|
s.r.PathPrefix(fmt.Sprintf("%s", admin_path)).Handler(http.StripPrefix(fmt.Sprintf("%s", admin_path), http.FileServer(http.Dir(Cfg.GetAdminDir()))))
|
|
}
|
|
|
|
func (s *Server) handleNotFound(w http.ResponseWriter, r *http.Request) {
|
|
log.Debug("%s %s", r.Method, r.URL.Path)
|
|
}
|
|
|
|
func (s *Server) GetFile(url string) (*storage.DbFile, int, error) {
|
|
is_redirect := false
|
|
f, err := storage.FileGetByUrl(url)
|
|
if err != nil {
|
|
f, err = storage.FileGetByRedirectUrl(url)
|
|
if err != nil {
|
|
return nil, 404, err
|
|
}
|
|
is_redirect = true
|
|
}
|
|
if !f.IsEnabled {
|
|
return nil, 404, fmt.Errorf("file is disabled")
|
|
}
|
|
if f.IsPaused {
|
|
if f.RedirectPath != "" && is_redirect {
|
|
return nil, 404, fmt.Errorf("can't access facade via redirect while paused")
|
|
} else if f.RefSubFile > 0 {
|
|
sf, err := storage.SubFileGet(f.RefSubFile)
|
|
if err != nil {
|
|
return nil, 404, fmt.Errorf("facade file not found")
|
|
}
|
|
f.Filename = sf.Filename
|
|
f.FileSize = sf.FileSize
|
|
} else {
|
|
return nil, 404, fmt.Errorf("facade file not set")
|
|
}
|
|
}
|
|
return f, 200, nil
|
|
}
|
|
|
|
func (s *Server) FileExists(url string) bool {
|
|
_, err := storage.FileGetByUrl(url)
|
|
if err != nil {
|
|
_, err = storage.FileGetByRedirectUrl(url)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|