downloader: fix internal range check
This commit is contained in:
parent
3c54c1f840
commit
2a370a8776
|
@ -9,8 +9,6 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -46,10 +44,6 @@ var instance = &Downloader{
|
||||||
serverCache: make(map[string][]string),
|
serverCache: make(map[string][]string),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regex to match the end of an IPv4/IPv6 address. This allows the port to be removed
|
|
||||||
// so that we are just working with the raw IP address in question.
|
|
||||||
var ipMatchRegex = regexp.MustCompile(`(:\d+)$`)
|
|
||||||
|
|
||||||
// Internal IP ranges that should be blocked if the resource requested resolves within.
|
// Internal IP ranges that should be blocked if the resource requested resolves within.
|
||||||
var internalRanges = []*net.IPNet{
|
var internalRanges = []*net.IPNet{
|
||||||
mustParseCIDR("127.0.0.1/8"),
|
mustParseCIDR("127.0.0.1/8"),
|
||||||
|
@ -62,9 +56,11 @@ var internalRanges = []*net.IPNet{
|
||||||
mustParseCIDR("fc00::/7"),
|
mustParseCIDR("fc00::/7"),
|
||||||
}
|
}
|
||||||
|
|
||||||
const ErrInternalResolution = errors.Sentinel("downloader: destination resolves to internal network location")
|
const (
|
||||||
const ErrInvalidIPAddress = errors.Sentinel("downloader: invalid IP address")
|
ErrInternalResolution = errors.Sentinel("downloader: destination resolves to internal network location")
|
||||||
const ErrDownloadFailed = errors.Sentinel("downloader: download request failed")
|
ErrInvalidIPAddress = errors.Sentinel("downloader: invalid IP address")
|
||||||
|
ErrDownloadFailed = errors.Sentinel("downloader: download request failed")
|
||||||
|
)
|
||||||
|
|
||||||
type Counter struct {
|
type Counter struct {
|
||||||
total int
|
total int
|
||||||
|
@ -79,8 +75,8 @@ func (c *Counter) Write(p []byte) (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type DownloadRequest struct {
|
type DownloadRequest struct {
|
||||||
URL *url.URL
|
|
||||||
Directory string
|
Directory string
|
||||||
|
URL *url.URL
|
||||||
}
|
}
|
||||||
|
|
||||||
type Download struct {
|
type Download struct {
|
||||||
|
@ -92,7 +88,7 @@ type Download struct {
|
||||||
cancelFunc *context.CancelFunc
|
cancelFunc *context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
// Starts a new tracked download which allows for cancellation later on by calling
|
// New starts a new tracked download which allows for cancellation later on by calling
|
||||||
// the Downloader.Cancel function.
|
// the Downloader.Cancel function.
|
||||||
func New(s *server.Server, r DownloadRequest) *Download {
|
func New(s *server.Server, r DownloadRequest) *Download {
|
||||||
dl := Download{
|
dl := Download{
|
||||||
|
@ -104,14 +100,14 @@ func New(s *server.Server, r DownloadRequest) *Download {
|
||||||
return &dl
|
return &dl
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns all of the tracked downloads for a given server instance.
|
// ByServer returns all the tracked downloads for a given server instance.
|
||||||
func ByServer(sid string) []*Download {
|
func ByServer(sid string) []*Download {
|
||||||
instance.mu.Lock()
|
instance.mu.Lock()
|
||||||
defer instance.mu.Unlock()
|
defer instance.mu.Unlock()
|
||||||
var downloads []*Download
|
var downloads []*Download
|
||||||
if v, ok := instance.serverCache[sid]; ok {
|
if v, ok := instance.serverCache[sid]; ok {
|
||||||
for _, id := range v {
|
for _, id := range v {
|
||||||
if dl, dlok := instance.downloadCache[id]; dlok {
|
if dl, ok := instance.downloadCache[id]; ok {
|
||||||
downloads = append(downloads, dl)
|
downloads = append(downloads, dl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,7 +115,7 @@ func ByServer(sid string) []*Download {
|
||||||
return downloads
|
return downloads
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a single Download matching a given identifier. If no download is found
|
// ByID returns a single Download matching a given identifier. If no download is found
|
||||||
// the second argument in the response will be false.
|
// the second argument in the response will be false.
|
||||||
func ByID(dlid string) *Download {
|
func ByID(dlid string) *Download {
|
||||||
return instance.find(dlid)
|
return instance.find(dlid)
|
||||||
|
@ -136,7 +132,7 @@ func (dl Download) MarshalJSON() ([]byte, error) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Executes a given download for the server and begins writing the file to the disk. Once
|
// Execute executes a given download for the server and begins writing the file to the disk. Once
|
||||||
// completed the download will be removed from the cache.
|
// completed the download will be removed from the cache.
|
||||||
func (dl *Download) Execute() error {
|
func (dl *Download) Execute() error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Hour*12)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Hour*12)
|
||||||
|
@ -187,7 +183,7 @@ func (dl *Download) Execute() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cancels a running download and frees up the associated resources. If a file is being
|
// Cancel cancels a running download and frees up the associated resources. If a file is being
|
||||||
// written a partial file will remain present on the disk.
|
// written a partial file will remain present on the disk.
|
||||||
func (dl *Download) Cancel() {
|
func (dl *Download) Cancel() {
|
||||||
if dl.cancelFunc != nil {
|
if dl.cancelFunc != nil {
|
||||||
|
@ -196,12 +192,12 @@ func (dl *Download) Cancel() {
|
||||||
instance.remove(dl.Identifier)
|
instance.remove(dl.Identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if the given download belongs to the provided server.
|
// BelongsTo checks if the given download belongs to the provided server.
|
||||||
func (dl *Download) BelongsTo(s *server.Server) bool {
|
func (dl *Download) BelongsTo(s *server.Server) bool {
|
||||||
return dl.server.ID() == s.ID()
|
return dl.server.ID() == s.ID()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the current progress of the download as a float value between 0 and 1 where
|
// Progress returns the current progress of the download as a float value between 0 and 1 where
|
||||||
// 1 indicates that the download is completed.
|
// 1 indicates that the download is completed.
|
||||||
func (dl *Download) Progress() float64 {
|
func (dl *Download) Progress() float64 {
|
||||||
dl.mu.RLock()
|
dl.mu.RLock()
|
||||||
|
@ -234,15 +230,19 @@ func (dl *Download) isExternalNetwork(ctx context.Context) error {
|
||||||
|
|
||||||
// This cluster-fuck of math and integer shit converts an integer IP into a proper IPv4.
|
// This cluster-fuck of math and integer shit converts an integer IP into a proper IPv4.
|
||||||
// For example: 16843009 would become 1.1.1.1
|
// For example: 16843009 would become 1.1.1.1
|
||||||
if i, err := strconv.ParseInt(host, 10, 64); err == nil {
|
//if i, err := strconv.ParseInt(host, 10, 64); err == nil {
|
||||||
host = strconv.FormatInt((i>>24)&0xFF, 10) + "." + strconv.FormatInt((i>>16)&0xFF, 10) + "." + strconv.FormatInt((i>>8)&0xFF, 10) + "." + strconv.FormatInt(i&0xFF, 10)
|
// host = strconv.FormatInt((i>>24)&0xFF, 10) + "." + strconv.FormatInt((i>>16)&0xFF, 10) + "." + strconv.FormatInt((i>>8)&0xFF, 10) + "." + strconv.FormatInt(i&0xFF, 10)
|
||||||
}
|
//}
|
||||||
|
|
||||||
if !ipMatchRegex.MatchString(host) {
|
if _, _, err := net.SplitHostPort(host); err != nil {
|
||||||
if dl.req.URL.Scheme == "https" {
|
if !strings.Contains(err.Error(), "missing port in address") {
|
||||||
host = host + ":443"
|
return errors.WithStack(err)
|
||||||
} else {
|
}
|
||||||
host = host + ":80"
|
switch dl.req.URL.Scheme {
|
||||||
|
case "http":
|
||||||
|
host += ":80"
|
||||||
|
case "https":
|
||||||
|
host += ":443"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,7 +252,11 @@ func (dl *Download) isExternalNetwork(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
_ = c.Close()
|
_ = c.Close()
|
||||||
|
|
||||||
ip := net.ParseIP(ipMatchRegex.ReplaceAllString(c.RemoteAddr().String(), ""))
|
ipStr, _, err := net.SplitHostPort(c.RemoteAddr().String())
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
ip := net.ParseIP(ipStr)
|
||||||
if ip == nil {
|
if ip == nil {
|
||||||
return errors.WithStack(ErrInvalidIPAddress)
|
return errors.WithStack(ErrInvalidIPAddress)
|
||||||
}
|
}
|
||||||
|
@ -267,7 +271,7 @@ func (dl *Download) isExternalNetwork(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Defines a global downloader struct that keeps track of all currently processing downloads
|
// Downloader represents a global downloader that keeps track of all currently processing downloads
|
||||||
// for the machine.
|
// for the machine.
|
||||||
type Downloader struct {
|
type Downloader struct {
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
|
@ -275,7 +279,7 @@ type Downloader struct {
|
||||||
serverCache map[string][]string
|
serverCache map[string][]string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tracks a download in the internal cache for this instance.
|
// track tracks a download in the internal cache for this instance.
|
||||||
func (d *Downloader) track(dl *Download) {
|
func (d *Downloader) track(dl *Download) {
|
||||||
d.mu.Lock()
|
d.mu.Lock()
|
||||||
defer d.mu.Unlock()
|
defer d.mu.Unlock()
|
||||||
|
@ -289,7 +293,7 @@ func (d *Downloader) track(dl *Download) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finds a given download entry using the provided ID and returns it.
|
// find finds a given download entry using the provided ID and returns it.
|
||||||
func (d *Downloader) find(dlid string) *Download {
|
func (d *Downloader) find(dlid string) *Download {
|
||||||
d.mu.RLock()
|
d.mu.RLock()
|
||||||
defer d.mu.RUnlock()
|
defer d.mu.RUnlock()
|
||||||
|
@ -299,7 +303,7 @@ func (d *Downloader) find(dlid string) *Download {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the given download reference from the cache storing them. This also updates
|
// remove removes the given download reference from the cache storing them. This also updates
|
||||||
// the slice of active downloads for a given server to not include this download.
|
// the slice of active downloads for a given server to not include this download.
|
||||||
func (d *Downloader) remove(dlID string) {
|
func (d *Downloader) remove(dlID string) {
|
||||||
d.mu.Lock()
|
d.mu.Lock()
|
||||||
|
|
|
@ -254,8 +254,8 @@ func getServerPullingFiles(c *gin.Context) {
|
||||||
func postServerPullRemoteFile(c *gin.Context) {
|
func postServerPullRemoteFile(c *gin.Context) {
|
||||||
s := ExtractServer(c)
|
s := ExtractServer(c)
|
||||||
var data struct {
|
var data struct {
|
||||||
|
RootPath string `binding:"required,omitempty" json:"root"`
|
||||||
URL string `binding:"required" json:"url"`
|
URL string `binding:"required" json:"url"`
|
||||||
Directory string `binding:"required,omitempty" json:"directory"`
|
|
||||||
}
|
}
|
||||||
if err := c.BindJSON(&data); err != nil {
|
if err := c.BindJSON(&data); err != nil {
|
||||||
return
|
return
|
||||||
|
@ -286,11 +286,11 @@ func postServerPullRemoteFile(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
dl := downloader.New(s, downloader.DownloadRequest{
|
dl := downloader.New(s, downloader.DownloadRequest{
|
||||||
|
Directory: data.RootPath,
|
||||||
URL: u,
|
URL: u,
|
||||||
Directory: data.Directory,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Execute this pull in a seperate thread since it may take a long time to complete.
|
// Execute this pull in a separate thread since it may take a long time to complete.
|
||||||
go func() {
|
go func() {
|
||||||
s.Log().WithField("download_id", dl.Identifier).WithField("url", u.String()).Info("starting pull of remote file to disk")
|
s.Log().WithField("download_id", dl.Identifier).WithField("url", u.String()).Info("starting pull of remote file to disk")
|
||||||
if err := dl.Execute(); err != nil {
|
if err := dl.Execute(); err != nil {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user