From 8b13dde599eac80acfc7c23d0dcba2611d8dccbe Mon Sep 17 00:00:00 2001 From: Zack Scholl Date: Wed, 10 Jul 2019 06:19:37 -0700 Subject: [PATCH] add utils --- go.mod | 2 +- main.go | 105 +++++++++++--- templates/view.html | 343 ++++++++++++++++++++++++++------------------ utils.go | 91 ++++++++++++ 4 files changed, 379 insertions(+), 162 deletions(-) create mode 100644 utils.go diff --git a/go.mod b/go.mod index b3647e8..22800d1 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/schollz/userve +module github.com/schollz/omniserve go 1.12 diff --git a/main.go b/main.go index 06b7a75..3cb3f2d 100644 --- a/main.go +++ b/main.go @@ -6,19 +6,21 @@ import ( "fmt" "io/ioutil" "net/http" + "path/filepath" "strings" "sync" "text/template" "time" "github.com/gorilla/websocket" + "github.com/h2non/filetype" log "github.com/schollz/logger" "github.com/vincent-petithory/dataurl" - "github.com/h2non/filetype" ) type server struct { - port string + publicURL string + port string // connections stored as map of domain -> connections conn map[string][]*Connection @@ -61,8 +63,9 @@ func (ws *WebsocketConn) Receive() (p Payload, err error) { func main() { var debug bool - var flagPort string + var flagPort, flagPublicURL string flag.StringVar(&flagPort, "port", "8001", "port") + flag.StringVar(&flagPublicURL, "url", "", "public url to use") flag.BoolVar(&debug, "debug", false, "debug mode") flag.Parse() @@ -72,11 +75,19 @@ func main() { log.SetLevel("info") } - s := new(server) - s.Lock() - s.port = flagPort - s.conn = make(map[string][]*Connection) - s.Unlock() + if flagPublicURL == "" { + flagPublicURL = "localhost:" + flagPort + } + if !strings.HasPrefix(flagPublicURL, "http") { + flagPublicURL = "http://" + flagPublicURL + } + + s := &server{ + port: flagPort, + conn: make(map[string][]*Connection), + publicURL: flagPublicURL, + } + s.serve() } @@ -97,7 +108,7 @@ func (s *server) handler(w http.ResponseWriter, r *http.Request) { } func (s *server) handle(w http.ResponseWriter, r *http.Request) (err error) { - + log.Debugf("URL: %s, Referer: %s", r.URL.Path, r.Referer()) // very special paths if r.URL.Path == "/robots.txt" { // special path @@ -117,14 +128,47 @@ Disallow: /`)) return err } type view struct { - Title string - HTML string + PublicURL string + Title string + HTML string } - return t.Execute(w, view{}) + return t.Execute(w, view{PublicURL: s.publicURL}) } else { log.Debugf("attempting to find %s", r.URL.Path) + + pathToFile := r.URL.Path[1:] + domain := strings.Split(r.URL.Path[1:], "/")[0] + // check to make sure it has domain prepended + piecesOfReferer := strings.Split(r.Referer(), "/") + if len(piecesOfReferer) > 4 { + domain = piecesOfReferer[3] + } + + // prefix the domain if it doesn't exist + if !strings.HasPrefix(pathToFile, domain) { + pathToFile = domain + "/" + pathToFile + http.Redirect(w, r, "/"+pathToFile, 302) + return + } + + // add index.html if it doesn't exist + if filepath.Ext(pathToFile) == "" { + if string(pathToFile[len(pathToFile)-1]) != "/" { + pathToFile += "/" + } + pathToFile += "index.html" + http.Redirect(w, r, "/"+pathToFile, 302) + return + } + + var ipAddress string + ipAddress, err = GetClientIPHelper(r) + if err != nil { + log.Debugf("could not determine ip: %s", err.Error()) + } + var data string - data, err = s.get(r.URL.Path[1:]) + data, err = s.get(pathToFile, ipAddress) if err != nil { return } @@ -134,10 +178,22 @@ Disallow: /`)) return } contentType := dataURL.MediaType.ContentType() - if contentType == "application/octet-stream" { - mimeType := filetype.GetType(r.URL.Path[1:]) + if contentType == "application/octet-stream" || contentType == "" { + pathToFileExt := filepath.Ext(pathToFile) + mimeType := filetype.GetType(pathToFileExt) contentType = mimeType.MIME.Value + if contentType == "" { + switch pathToFileExt { + case ".css": + contentType = "text/css" + case ".js": + contentType = "text/javascript" + case ".html": + contentType = "text/html" + } + } } + log.Debugf("%s content-type: '%s'", pathToFile, contentType) w.Header().Set("Content-Type", contentType) w.Write(dataURL.Data) return @@ -155,9 +211,10 @@ var wsupgrader = websocket.Upgrader{ type Payload struct { // message meta - Type string `json:"type"` - Success bool `json:"success"` - Message string `json:"message"` + Type string `json:"type"` + Success bool `json:"success"` + Message string `json:"message"` + IPAddress string `json:"ip"` } func (p Payload) String() string { @@ -219,7 +276,7 @@ func (s *server) handleWebsocket(w http.ResponseWriter, r *http.Request) (err er return } -func (s *server) get(filePath string) (payload string, err error) { +func (s *server) get(filePath, ipAddress string) (payload string, err error) { log.Debugf("requesting %s", filePath) domain := strings.Split(filePath, "/")[0] @@ -239,8 +296,9 @@ func (s *server) get(filePath string) (payload string, err error) { var p Payload p, err = func() (p Payload, err error) { err = conn.ws.Send(Payload{ - Type: "get", - Message: filePath, + Type: "get", + Message: filePath, + IPAddress: ipAddress, }) if err != nil { return @@ -253,7 +311,6 @@ func (s *server) get(filePath string) (payload string, err error) { s.DumpConnection(domain, conn.ID) continue } - log.Debugf("recv: %+v", p) if p.Type == "get" { payload = p.Message if !p.Success { @@ -261,6 +318,10 @@ func (s *server) get(filePath string) (payload string, err error) { } return } + if len(p.Message) > 10 { + p.Message = p.Message[:10] + "..." + } + log.Debugf("recv: %+v", p) err = fmt.Errorf("invalid response") break } diff --git a/templates/view.html b/templates/view.html index e5f8876..d10b2bc 100644 --- a/templates/view.html +++ b/templates/view.html @@ -9,168 +9,233 @@ - Share a file + omniserver +
+

omniserver

+

+

Serve your files from your browser at this domain: {{.PublicURL}}.

+
+ Click here for FAQ. +
-
Drop or click here to share a folder.
-

Max file size: 100 MB

+
Drop or folder or click here to share a file.
+

+
+
+
+ Files served +
+
+

Console:

+ +
+
- - diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..f08c174 --- /dev/null +++ b/utils.go @@ -0,0 +1,91 @@ +package main + +import ( + "errors" + "fmt" + "log" + "net" + "net/http" + "net/url" +) + +// GetClientIPHelper gets the client IP using a mixture of techniques. +// This is how it is with golang at the moment. +func GetClientIPHelper(req *http.Request) (ipResult string, errResult error) { + + // Try lots of ways :) Order is important. + // Try Request Headers (X-Forwarder). Client could be behind a Proxy + ip, err := getClientIPByHeaders(req) + if err == nil { + log.Printf("debug: Found IP using Request Headers sniffing. ip: %v", ip) + return ip, nil + } + + // Try by Request + ip, err = getClientIPByRequestRemoteAddr(req) + if err == nil { + log.Printf("debug: Found IP using Request sniffing. ip: %v", ip) + return ip, nil + } + + // Try Request Header ("Origin") + url, err := url.Parse(req.Header.Get("Origin")) + if err == nil { + host := url.Host + ip, _, err := net.SplitHostPort(host) + if err == nil { + log.Printf("debug: Found IP using Header (Origin) sniffing. ip: %v", ip) + return ip, nil + } + } + + err = errors.New("error: Could not find clients IP address") + return "", err +} + +// getClientIPByRequest tries to get directly from the Request. +// https://blog.golang.org/context/userip/userip.go +func getClientIPByRequestRemoteAddr(req *http.Request) (ip string, err error) { + + // Try via request + ip, port, err := net.SplitHostPort(req.RemoteAddr) + if err != nil { + log.Printf("debug: Getting req.RemoteAddr %v", err) + return "", err + } else { + log.Printf("debug: With req.RemoteAddr found IP:%v; Port: %v", ip, port) + } + + userIP := net.ParseIP(ip) + if userIP == nil { + message := fmt.Sprintf("debug: Parsing IP from Request.RemoteAddr got nothing.") + log.Printf(message) + return "", fmt.Errorf(message) + + } + log.Printf("debug: Found IP: %v", userIP) + return userIP.String(), nil + +} + +// getClientIPByHeaders tries to get directly from the Request Headers. +// This is only way when the client is behind a Proxy. +func getClientIPByHeaders(req *http.Request) (ip string, err error) { + + // Client could be behid a Proxy, so Try Request Headers (X-Forwarder) + ipSlice := []string{} + + ipSlice = append(ipSlice, req.Header.Get("X-Forwarded-For")) + ipSlice = append(ipSlice, req.Header.Get("x-forwarded-for")) + ipSlice = append(ipSlice, req.Header.Get("X-FORWARDED-FOR")) + + for _, v := range ipSlice { + log.Printf("debug: client request header check gives ip: %v", v) + if v != "" { + return v, nil + } + } + err = errors.New("error: Could not find clients IP address from the Request Headers") + return "", err + +}