mirror of
https://github.com/rclone/rclone.git
synced 2024-11-25 09:41:44 +08:00
vendor: add github.com/goftp/server
This commit is contained in:
parent
a25875170b
commit
a14f0d46d7
1
go.mod
1
go.mod
|
@ -17,6 +17,7 @@ require (
|
|||
github.com/djherbis/times v1.0.1
|
||||
github.com/dropbox/dropbox-sdk-go-unofficial v4.1.0+incompatible
|
||||
github.com/go-ini/ini v1.37.0 // indirect
|
||||
github.com/goftp/server v0.0.0-20180914132916-1fd52c8552f1
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7 // indirect
|
||||
github.com/golang/protobuf v1.1.0 // indirect
|
||||
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 // indirect
|
||||
|
|
2
go.sum
2
go.sum
|
@ -32,6 +32,8 @@ github.com/dropbox/dropbox-sdk-go-unofficial v4.1.0+incompatible h1:ZFvUIiBbGhDY
|
|||
github.com/dropbox/dropbox-sdk-go-unofficial v4.1.0+incompatible/go.mod h1:lr+LhMM3F6Y3lW1T9j2U5l7QeuWm87N9+PPXo3yH4qY=
|
||||
github.com/go-ini/ini v1.37.0 h1:/FpMfveJbc7ExTTDgT5nL9Vw+aZdst/c2dOxC931U+M=
|
||||
github.com/go-ini/ini v1.37.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/goftp/server v0.0.0-20180914132916-1fd52c8552f1 h1:WjgeEHEDLGx56ndxS6FYi6qFjZGajSVHPuEPdpJ60cI=
|
||||
github.com/goftp/server v0.0.0-20180914132916-1fd52c8552f1/go.mod h1:k/SS6VWkxY7dHPhoMQ8IdRu8L4lQtmGbhyXGg+vCnXE=
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7 h1:2hRPrmiwPrp3fQX967rNJIhQPtiGXdlQWAxKbKw3VHA=
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||
github.com/golang/protobuf v1.1.0 h1:0iH4Ffd/meGoXqF2lSAhZHt8X+cPgkfn/cb6Cce5Vpc=
|
||||
|
|
2
vendor/github.com/goftp/server/.gitignore
generated
vendored
Normal file
2
vendor/github.com/goftp/server/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
testdata
|
||||
coverage.txt
|
20
vendor/github.com/goftp/server/LICENSE
generated
vendored
Normal file
20
vendor/github.com/goftp/server/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
Copyright (c) 2018 Goftp Authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
92
vendor/github.com/goftp/server/README.md
generated
vendored
Normal file
92
vendor/github.com/goftp/server/README.md
generated
vendored
Normal file
|
@ -0,0 +1,92 @@
|
|||
# server
|
||||
|
||||
[![CircleCI](https://circleci.com/gh/goftp/server.svg?style=shield)](https://circleci.com/gh/goftp/server)
|
||||
[![](https://goreportcard.com/badge/github.com/goftp/server)](https://goreportcard.com/report/github.com/goftp/server)
|
||||
[![codecov](https://codecov.io/gh/goftp/server/branch/master/graph/badge.svg)](https://codecov.io/gh/goftp/server)
|
||||
|
||||
A FTP server framework forked from [github.com/yob/graval](http://github.com/yob/graval) and changed a lot.
|
||||
|
||||
Full documentation for the package is available on [godoc](http://godoc.org/github.com/goftp/server)
|
||||
|
||||
## Version
|
||||
|
||||
v0.2.3
|
||||
|
||||
## Installation
|
||||
|
||||
go get github.com/goftp/server
|
||||
|
||||
## Usage
|
||||
|
||||
To boot a FTP server you will need to provide a driver that speaks to
|
||||
your persistence layer - the required driver contract is in [the
|
||||
documentation](http://godoc.org/github.com/goftp/server).
|
||||
|
||||
Look at the [file driver](https://github.com/goftp/file-driver) to see
|
||||
an example of how to build a backend.
|
||||
|
||||
There is a [sample ftp server](/exampleftpd) as a demo. You can build it with this
|
||||
command:
|
||||
|
||||
go install github.com/goftp/server/exampleftpd
|
||||
|
||||
Then run it if you have add $GOPATH to your $PATH:
|
||||
|
||||
exampleftpd -root /tmp
|
||||
|
||||
And finally, connect to the server with any FTP client and the following
|
||||
details:
|
||||
|
||||
host: 127.0.0.1
|
||||
port: 2121
|
||||
username: admin
|
||||
password: 123456
|
||||
|
||||
This uses the file driver mentioned above to serve files.
|
||||
|
||||
## Contributors
|
||||
|
||||
see [https://github.com/goftp/server/graphs/contributors](https://github.com/goftp/server/graphs/contributors)
|
||||
|
||||
## Warning
|
||||
|
||||
FTP is an incredibly insecure protocol. Be careful about forcing users to authenticate
|
||||
with an username or password that are important.
|
||||
|
||||
## License
|
||||
|
||||
This library is distributed under the terms of the MIT License. See the included file for
|
||||
more detail.
|
||||
|
||||
## Contributing
|
||||
|
||||
All suggestions and patches welcome, preferably via a git repository I can pull from.
|
||||
If this library proves useful to you, please let me know.
|
||||
|
||||
## Further Reading
|
||||
|
||||
There are a range of RFCs that together specify the FTP protocol. In chronological
|
||||
order, the more useful ones are:
|
||||
|
||||
* [http://tools.ietf.org/rfc/rfc959.txt](http://tools.ietf.org/rfc/rfc959.txt)
|
||||
* [http://tools.ietf.org/rfc/rfc1123.txt](http://tools.ietf.org/rfc/rfc1123.txt)
|
||||
* [http://tools.ietf.org/rfc/rfc2228.txt](http://tools.ietf.org/rfc/rfc2228.txt)
|
||||
* [http://tools.ietf.org/rfc/rfc2389.txt](http://tools.ietf.org/rfc/rfc2389.txt)
|
||||
* [http://tools.ietf.org/rfc/rfc2428.txt](http://tools.ietf.org/rfc/rfc2428.txt)
|
||||
* [http://tools.ietf.org/rfc/rfc3659.txt](http://tools.ietf.org/rfc/rfc3659.txt)
|
||||
* [http://tools.ietf.org/rfc/rfc4217.txt](http://tools.ietf.org/rfc/rfc4217.txt)
|
||||
|
||||
For an english summary that's somewhat more legible than the RFCs, and provides
|
||||
some commentary on what features are actually useful or relevant 24 years after
|
||||
RFC959 was published:
|
||||
|
||||
* [http://cr.yp.to/ftp.html](http://cr.yp.to/ftp.html)
|
||||
|
||||
For a history lesson, check out Appendix III of RCF959. It lists the preceding
|
||||
(obsolete) RFC documents that relate to file transfers, including the ye old
|
||||
RFC114 from 1971, "A File Transfer Protocol"
|
||||
|
||||
This library is heavily based on [em-ftpd](https://github.com/yob/em-ftpd), an FTPd
|
||||
framework with similar design goals within the ruby and EventMachine ecosystems. It
|
||||
worked well enough, but you know, callbacks and event loops make me something
|
||||
something.
|
28
vendor/github.com/goftp/server/auth.go
generated
vendored
Normal file
28
vendor/github.com/goftp/server/auth.go
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
// Copyright 2018 The goftp Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
|
||||
// Auth is an interface to auth your ftp user login.
|
||||
type Auth interface {
|
||||
CheckPasswd(string, string) (bool, error)
|
||||
}
|
||||
|
||||
var (
|
||||
_ Auth = &SimpleAuth{}
|
||||
)
|
||||
|
||||
// SimpleAuth implements Auth interface to provide a memory user login auth
|
||||
type SimpleAuth struct {
|
||||
Name string
|
||||
Password string
|
||||
}
|
||||
|
||||
// CheckPasswd will check user's password
|
||||
func (a *SimpleAuth) CheckPasswd(name, pass string) (bool, error) {
|
||||
if name != a.Name || pass != a.Password {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
14
vendor/github.com/goftp/server/circle.yml
generated
vendored
Normal file
14
vendor/github.com/goftp/server/circle.yml
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
dependencies:
|
||||
override:
|
||||
- mkdir -p ~/.go_workspace/src/github.com/goftp
|
||||
- ln -s ${HOME}/${CIRCLE_PROJECT_REPONAME} ${HOME}/.go_workspace/src/github.com/goftp/${CIRCLE_PROJECT_REPONAME}
|
||||
# './...' is a relative pattern which means all subdirectories
|
||||
- go get -t -d -v ./...
|
||||
- go build -v
|
||||
|
||||
test:
|
||||
override:
|
||||
# './...' is a relative pattern which means all subdirectories
|
||||
- go test -v -race -coverprofile=coverage.txt -covermode=atomic
|
||||
post:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
1138
vendor/github.com/goftp/server/cmd.go
generated
vendored
Normal file
1138
vendor/github.com/goftp/server/cmd.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
252
vendor/github.com/goftp/server/conn.go
generated
vendored
Normal file
252
vendor/github.com/goftp/server/conn.go
generated
vendored
Normal file
|
@ -0,0 +1,252 @@
|
|||
// Copyright 2018 The goftp Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
mrand "math/rand"
|
||||
"net"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultWelcomeMessage = "Welcome to the Go FTP Server"
|
||||
)
|
||||
|
||||
type Conn struct {
|
||||
conn net.Conn
|
||||
controlReader *bufio.Reader
|
||||
controlWriter *bufio.Writer
|
||||
dataConn DataSocket
|
||||
driver Driver
|
||||
auth Auth
|
||||
logger Logger
|
||||
server *Server
|
||||
tlsConfig *tls.Config
|
||||
sessionID string
|
||||
namePrefix string
|
||||
reqUser string
|
||||
user string
|
||||
renameFrom string
|
||||
lastFilePos int64
|
||||
appendData bool
|
||||
closed bool
|
||||
tls bool
|
||||
}
|
||||
|
||||
func (conn *Conn) LoginUser() string {
|
||||
return conn.user
|
||||
}
|
||||
|
||||
func (conn *Conn) IsLogin() bool {
|
||||
return len(conn.user) > 0
|
||||
}
|
||||
|
||||
func (conn *Conn) PublicIp() string {
|
||||
return conn.server.PublicIp
|
||||
}
|
||||
|
||||
func (conn *Conn) passiveListenIP() string {
|
||||
if len(conn.PublicIp()) > 0 {
|
||||
return conn.PublicIp()
|
||||
}
|
||||
return conn.conn.LocalAddr().String()
|
||||
}
|
||||
|
||||
func (conn *Conn) PassivePort() int {
|
||||
if len(conn.server.PassivePorts) > 0 {
|
||||
portRange := strings.Split(conn.server.PassivePorts, "-")
|
||||
|
||||
if len(portRange) != 2 {
|
||||
log.Println("empty port")
|
||||
return 0
|
||||
}
|
||||
|
||||
minPort, _ := strconv.Atoi(strings.TrimSpace(portRange[0]))
|
||||
maxPort, _ := strconv.Atoi(strings.TrimSpace(portRange[1]))
|
||||
|
||||
return minPort + mrand.Intn(maxPort-minPort)
|
||||
}
|
||||
// let system automatically chose one port
|
||||
return 0
|
||||
}
|
||||
|
||||
// returns a random 20 char string that can be used as a unique session ID
|
||||
func newSessionID() string {
|
||||
hash := sha256.New()
|
||||
_, err := io.CopyN(hash, rand.Reader, 50)
|
||||
if err != nil {
|
||||
return "????????????????????"
|
||||
}
|
||||
md := hash.Sum(nil)
|
||||
mdStr := hex.EncodeToString(md)
|
||||
return mdStr[0:20]
|
||||
}
|
||||
|
||||
// Serve starts an endless loop that reads FTP commands from the client and
|
||||
// responds appropriately. terminated is a channel that will receive a true
|
||||
// message when the connection closes. This loop will be running inside a
|
||||
// goroutine, so use this channel to be notified when the connection can be
|
||||
// cleaned up.
|
||||
func (conn *Conn) Serve() {
|
||||
conn.logger.Print(conn.sessionID, "Connection Established")
|
||||
// send welcome
|
||||
conn.writeMessage(220, conn.server.WelcomeMessage)
|
||||
// read commands
|
||||
for {
|
||||
line, err := conn.controlReader.ReadString('\n')
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
conn.logger.Print(conn.sessionID, fmt.Sprint("read error:", err))
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
conn.receiveLine(line)
|
||||
// QUIT command closes connection, break to avoid error on reading from
|
||||
// closed socket
|
||||
if conn.closed == true {
|
||||
break
|
||||
}
|
||||
}
|
||||
conn.Close()
|
||||
conn.logger.Print(conn.sessionID, "Connection Terminated")
|
||||
}
|
||||
|
||||
// Close will manually close this connection, even if the client isn't ready.
|
||||
func (conn *Conn) Close() {
|
||||
conn.conn.Close()
|
||||
conn.closed = true
|
||||
if conn.dataConn != nil {
|
||||
conn.dataConn.Close()
|
||||
conn.dataConn = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (conn *Conn) upgradeToTLS() error {
|
||||
conn.logger.Print(conn.sessionID, "Upgrading connectiion to TLS")
|
||||
tlsConn := tls.Server(conn.conn, conn.tlsConfig)
|
||||
err := tlsConn.Handshake()
|
||||
if err == nil {
|
||||
conn.conn = tlsConn
|
||||
conn.controlReader = bufio.NewReader(tlsConn)
|
||||
conn.controlWriter = bufio.NewWriter(tlsConn)
|
||||
conn.tls = true
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// receiveLine accepts a single line FTP command and co-ordinates an
|
||||
// appropriate response.
|
||||
func (conn *Conn) receiveLine(line string) {
|
||||
command, param := conn.parseLine(line)
|
||||
conn.logger.PrintCommand(conn.sessionID, command, param)
|
||||
cmdObj := commands[strings.ToUpper(command)]
|
||||
if cmdObj == nil {
|
||||
conn.writeMessage(500, "Command not found")
|
||||
return
|
||||
}
|
||||
if cmdObj.RequireParam() && param == "" {
|
||||
conn.writeMessage(553, "action aborted, required param missing")
|
||||
} else if cmdObj.RequireAuth() && conn.user == "" {
|
||||
conn.writeMessage(530, "not logged in")
|
||||
} else {
|
||||
cmdObj.Execute(conn, param)
|
||||
}
|
||||
}
|
||||
|
||||
func (conn *Conn) parseLine(line string) (string, string) {
|
||||
params := strings.SplitN(strings.Trim(line, "\r\n"), " ", 2)
|
||||
if len(params) == 1 {
|
||||
return params[0], ""
|
||||
}
|
||||
return params[0], strings.TrimSpace(params[1])
|
||||
}
|
||||
|
||||
// writeMessage will send a standard FTP response back to the client.
|
||||
func (conn *Conn) writeMessage(code int, message string) (wrote int, err error) {
|
||||
conn.logger.PrintResponse(conn.sessionID, code, message)
|
||||
line := fmt.Sprintf("%d %s\r\n", code, message)
|
||||
wrote, err = conn.controlWriter.WriteString(line)
|
||||
conn.controlWriter.Flush()
|
||||
return
|
||||
}
|
||||
|
||||
// writeMessage will send a standard FTP response back to the client.
|
||||
func (conn *Conn) writeMessageMultiline(code int, message string) (wrote int, err error) {
|
||||
conn.logger.PrintResponse(conn.sessionID, code, message)
|
||||
line := fmt.Sprintf("%d-%s\r\n%d END\r\n", code, message, code)
|
||||
wrote, err = conn.controlWriter.WriteString(line)
|
||||
conn.controlWriter.Flush()
|
||||
return
|
||||
}
|
||||
|
||||
// buildPath takes a client supplied path or filename and generates a safe
|
||||
// absolute path within their account sandbox.
|
||||
//
|
||||
// buildpath("/")
|
||||
// => "/"
|
||||
// buildpath("one.txt")
|
||||
// => "/one.txt"
|
||||
// buildpath("/files/two.txt")
|
||||
// => "/files/two.txt"
|
||||
// buildpath("files/two.txt")
|
||||
// => "/files/two.txt"
|
||||
// buildpath("/../../../../etc/passwd")
|
||||
// => "/etc/passwd"
|
||||
//
|
||||
// The driver implementation is responsible for deciding how to treat this path.
|
||||
// Obviously they MUST NOT just read the path off disk. The probably want to
|
||||
// prefix the path with something to scope the users access to a sandbox.
|
||||
func (conn *Conn) buildPath(filename string) (fullPath string) {
|
||||
if len(filename) > 0 && filename[0:1] == "/" {
|
||||
fullPath = filepath.Clean(filename)
|
||||
} else if len(filename) > 0 && filename != "-a" {
|
||||
fullPath = filepath.Clean(conn.namePrefix + "/" + filename)
|
||||
} else {
|
||||
fullPath = filepath.Clean(conn.namePrefix)
|
||||
}
|
||||
fullPath = strings.Replace(fullPath, "//", "/", -1)
|
||||
fullPath = strings.Replace(fullPath, string(filepath.Separator), "/", -1)
|
||||
return
|
||||
}
|
||||
|
||||
// sendOutofbandData will send a string to the client via the currently open
|
||||
// data socket. Assumes the socket is open and ready to be used.
|
||||
func (conn *Conn) sendOutofbandData(data []byte) {
|
||||
bytes := len(data)
|
||||
if conn.dataConn != nil {
|
||||
conn.dataConn.Write(data)
|
||||
conn.dataConn.Close()
|
||||
conn.dataConn = nil
|
||||
}
|
||||
message := "Closing data connection, sent " + strconv.Itoa(bytes) + " bytes"
|
||||
conn.writeMessage(226, message)
|
||||
}
|
||||
|
||||
func (conn *Conn) sendOutofBandDataWriter(data io.ReadCloser) error {
|
||||
conn.lastFilePos = 0
|
||||
bytes, err := io.Copy(conn.dataConn, data)
|
||||
if err != nil {
|
||||
conn.dataConn.Close()
|
||||
conn.dataConn = nil
|
||||
return err
|
||||
}
|
||||
message := "Closing data connection, sent " + strconv.Itoa(int(bytes)) + " bytes"
|
||||
conn.writeMessage(226, message)
|
||||
conn.dataConn.Close()
|
||||
conn.dataConn = nil
|
||||
|
||||
return nil
|
||||
}
|
14
vendor/github.com/goftp/server/doc.go
generated
vendored
Normal file
14
vendor/github.com/goftp/server/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
// Copyright 2018 The goftp Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
http://tools.ietf.org/html/rfc959
|
||||
|
||||
http://www.faqs.org/rfcs/rfc2389.html
|
||||
http://www.faqs.org/rfcs/rfc959.html
|
||||
|
||||
http://tools.ietf.org/html/rfc2428
|
||||
*/
|
||||
|
||||
package server
|
61
vendor/github.com/goftp/server/driver.go
generated
vendored
Normal file
61
vendor/github.com/goftp/server/driver.go
generated
vendored
Normal file
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2018 The goftp Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
|
||||
import "io"
|
||||
|
||||
// DriverFactory is a driver factory to create driver. For each client that connects to the server, a new FTPDriver is required.
|
||||
// Create an implementation if this interface and provide it to FTPServer.
|
||||
type DriverFactory interface {
|
||||
NewDriver() (Driver, error)
|
||||
}
|
||||
|
||||
// Driver is an interface that you will create an implementation that speaks to your
|
||||
// chosen persistence layer. graval will create a new instance of your
|
||||
// driver for each client that connects and delegate to it as required.
|
||||
type Driver interface {
|
||||
// Init init
|
||||
Init(*Conn)
|
||||
|
||||
// params - a file path
|
||||
// returns - a time indicating when the requested path was last modified
|
||||
// - an error if the file doesn't exist or the user lacks
|
||||
// permissions
|
||||
Stat(string) (FileInfo, error)
|
||||
|
||||
// params - path
|
||||
// returns - true if the current user is permitted to change to the
|
||||
// requested path
|
||||
ChangeDir(string) error
|
||||
|
||||
// params - path, function on file or subdir found
|
||||
// returns - error
|
||||
// path
|
||||
ListDir(string, func(FileInfo) error) error
|
||||
|
||||
// params - path
|
||||
// returns - nil if the directory was deleted or any error encountered
|
||||
DeleteDir(string) error
|
||||
|
||||
// params - path
|
||||
// returns - nil if the file was deleted or any error encountered
|
||||
DeleteFile(string) error
|
||||
|
||||
// params - from_path, to_path
|
||||
// returns - nil if the file was renamed or any error encountered
|
||||
Rename(string, string) error
|
||||
|
||||
// params - path
|
||||
// returns - nil if the new directory was created or any error encountered
|
||||
MakeDir(string) error
|
||||
|
||||
// params - path
|
||||
// returns - a string containing the file data to send to the client
|
||||
GetFile(string, int64) (int64, io.ReadCloser, error)
|
||||
|
||||
// params - destination path, an io.Reader containing the file data
|
||||
// returns - the number of bytes writen and the first error encountered while writing, if any.
|
||||
PutFile(string, io.Reader, bool) (int64, error)
|
||||
}
|
14
vendor/github.com/goftp/server/fileinfo.go
generated
vendored
Normal file
14
vendor/github.com/goftp/server/fileinfo.go
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
// Copyright 2018 The goftp Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
|
||||
import "os"
|
||||
|
||||
type FileInfo interface {
|
||||
os.FileInfo
|
||||
|
||||
Owner() string
|
||||
Group() string
|
||||
}
|
49
vendor/github.com/goftp/server/listformatter.go
generated
vendored
Normal file
49
vendor/github.com/goftp/server/listformatter.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
// Copyright 2018 The goftp Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type listFormatter []FileInfo
|
||||
|
||||
// Short returns a string that lists the collection of files by name only,
|
||||
// one per line
|
||||
func (formatter listFormatter) Short() []byte {
|
||||
var buf bytes.Buffer
|
||||
for _, file := range formatter {
|
||||
fmt.Fprintf(&buf, "%s\r\n", file.Name())
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// Detailed returns a string that lists the collection of files with extra
|
||||
// detail, one per line
|
||||
func (formatter listFormatter) Detailed() []byte {
|
||||
var buf bytes.Buffer
|
||||
for _, file := range formatter {
|
||||
fmt.Fprintf(&buf, file.Mode().String())
|
||||
fmt.Fprintf(&buf, " 1 %s %s ", file.Owner(), file.Group())
|
||||
fmt.Fprintf(&buf, lpad(strconv.FormatInt(file.Size(), 10), 12))
|
||||
fmt.Fprintf(&buf, file.ModTime().Format(" Jan _2 15:04 "))
|
||||
fmt.Fprintf(&buf, "%s\r\n", file.Name())
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func lpad(input string, length int) (result string) {
|
||||
if len(input) < length {
|
||||
result = strings.Repeat(" ", length-len(input)) + input
|
||||
} else if len(input) == length {
|
||||
result = input
|
||||
} else {
|
||||
result = input[0:length]
|
||||
}
|
||||
return
|
||||
}
|
48
vendor/github.com/goftp/server/logger.go
generated
vendored
Normal file
48
vendor/github.com/goftp/server/logger.go
generated
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
// Copyright 2018 The goftp Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
type Logger interface {
|
||||
Print(sessionId string, message interface{})
|
||||
Printf(sessionId string, format string, v ...interface{})
|
||||
PrintCommand(sessionId string, command string, params string)
|
||||
PrintResponse(sessionId string, code int, message string)
|
||||
}
|
||||
|
||||
// Use an instance of this to log in a standard format
|
||||
type StdLogger struct{}
|
||||
|
||||
func (logger *StdLogger) Print(sessionId string, message interface{}) {
|
||||
log.Printf("%s %s", sessionId, message)
|
||||
}
|
||||
|
||||
func (logger *StdLogger) Printf(sessionId string, format string, v ...interface{}) {
|
||||
logger.Print(sessionId, fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
func (logger *StdLogger) PrintCommand(sessionId string, command string, params string) {
|
||||
if command == "PASS" {
|
||||
log.Printf("%s > PASS ****", sessionId)
|
||||
} else {
|
||||
log.Printf("%s > %s %s", sessionId, command, params)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *StdLogger) PrintResponse(sessionId string, code int, message string) {
|
||||
log.Printf("%s < %d %s", sessionId, code, message)
|
||||
}
|
||||
|
||||
// Silent logger, produces no output
|
||||
type DiscardLogger struct{}
|
||||
|
||||
func (logger *DiscardLogger) Print(sessionId string, message interface{}) {}
|
||||
func (logger *DiscardLogger) Printf(sessionId string, format string, v ...interface{}) {}
|
||||
func (logger *DiscardLogger) PrintCommand(sessionId string, command string, params string) {}
|
||||
func (logger *DiscardLogger) PrintResponse(sessionId string, code int, message string) {}
|
52
vendor/github.com/goftp/server/perm.go
generated
vendored
Normal file
52
vendor/github.com/goftp/server/perm.go
generated
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
// Copyright 2018 The goftp Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
|
||||
import "os"
|
||||
|
||||
type Perm interface {
|
||||
GetOwner(string) (string, error)
|
||||
GetGroup(string) (string, error)
|
||||
GetMode(string) (os.FileMode, error)
|
||||
|
||||
ChOwner(string, string) error
|
||||
ChGroup(string, string) error
|
||||
ChMode(string, os.FileMode) error
|
||||
}
|
||||
|
||||
type SimplePerm struct {
|
||||
owner, group string
|
||||
}
|
||||
|
||||
func NewSimplePerm(owner, group string) *SimplePerm {
|
||||
return &SimplePerm{
|
||||
owner: owner,
|
||||
group: group,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SimplePerm) GetOwner(string) (string, error) {
|
||||
return s.owner, nil
|
||||
}
|
||||
|
||||
func (s *SimplePerm) GetGroup(string) (string, error) {
|
||||
return s.group, nil
|
||||
}
|
||||
|
||||
func (s *SimplePerm) GetMode(string) (os.FileMode, error) {
|
||||
return os.ModePerm, nil
|
||||
}
|
||||
|
||||
func (s *SimplePerm) ChOwner(string, string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SimplePerm) ChGroup(string, string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SimplePerm) ChMode(string, os.FileMode) error {
|
||||
return nil
|
||||
}
|
278
vendor/github.com/goftp/server/server.go
generated
vendored
Normal file
278
vendor/github.com/goftp/server/server.go
generated
vendored
Normal file
|
@ -0,0 +1,278 @@
|
|||
// Copyright 2018 The goftp Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Version returns the library version
|
||||
func Version() string {
|
||||
return "0.3.0"
|
||||
}
|
||||
|
||||
// ServerOpts contains parameters for server.NewServer()
|
||||
type ServerOpts struct {
|
||||
// The factory that will be used to create a new FTPDriver instance for
|
||||
// each client connection. This is a mandatory option.
|
||||
Factory DriverFactory
|
||||
|
||||
Auth Auth
|
||||
|
||||
// Server Name, Default is Go Ftp Server
|
||||
Name string
|
||||
|
||||
// The hostname that the FTP server should listen on. Optional, defaults to
|
||||
// "::", which means all hostnames on ipv4 and ipv6.
|
||||
Hostname string
|
||||
|
||||
// Public IP of the server
|
||||
PublicIp string
|
||||
|
||||
// Passive ports
|
||||
PassivePorts string
|
||||
|
||||
// The port that the FTP should listen on. Optional, defaults to 3000. In
|
||||
// a production environment you will probably want to change this to 21.
|
||||
Port int
|
||||
|
||||
// use tls, default is false
|
||||
TLS bool
|
||||
|
||||
// if tls used, cert file is required
|
||||
CertFile string
|
||||
|
||||
// if tls used, key file is required
|
||||
KeyFile string
|
||||
|
||||
// If ture TLS is used in RFC4217 mode
|
||||
ExplicitFTPS bool
|
||||
|
||||
WelcomeMessage string
|
||||
|
||||
// A logger implementation, if nil the StdLogger is used
|
||||
Logger Logger
|
||||
}
|
||||
|
||||
// Server is the root of your FTP application. You should instantiate one
|
||||
// of these and call ListenAndServe() to start accepting client connections.
|
||||
//
|
||||
// Always use the NewServer() method to create a new Server.
|
||||
type Server struct {
|
||||
*ServerOpts
|
||||
listenTo string
|
||||
logger Logger
|
||||
listener net.Listener
|
||||
tlsConfig *tls.Config
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
feats string
|
||||
}
|
||||
|
||||
// ErrServerClosed is returned by ListenAndServe() or Serve() when a shutdown
|
||||
// was requested.
|
||||
var ErrServerClosed = errors.New("ftp: Server closed")
|
||||
|
||||
// serverOptsWithDefaults copies an ServerOpts struct into a new struct,
|
||||
// then adds any default values that are missing and returns the new data.
|
||||
func serverOptsWithDefaults(opts *ServerOpts) *ServerOpts {
|
||||
var newOpts ServerOpts
|
||||
if opts == nil {
|
||||
opts = &ServerOpts{}
|
||||
}
|
||||
if opts.Hostname == "" {
|
||||
newOpts.Hostname = "::"
|
||||
} else {
|
||||
newOpts.Hostname = opts.Hostname
|
||||
}
|
||||
if opts.Port == 0 {
|
||||
newOpts.Port = 3000
|
||||
} else {
|
||||
newOpts.Port = opts.Port
|
||||
}
|
||||
newOpts.Factory = opts.Factory
|
||||
if opts.Name == "" {
|
||||
newOpts.Name = "Go FTP Server"
|
||||
} else {
|
||||
newOpts.Name = opts.Name
|
||||
}
|
||||
|
||||
if opts.WelcomeMessage == "" {
|
||||
newOpts.WelcomeMessage = defaultWelcomeMessage
|
||||
} else {
|
||||
newOpts.WelcomeMessage = opts.WelcomeMessage
|
||||
}
|
||||
|
||||
if opts.Auth != nil {
|
||||
newOpts.Auth = opts.Auth
|
||||
}
|
||||
|
||||
newOpts.Logger = &StdLogger{}
|
||||
if opts.Logger != nil {
|
||||
newOpts.Logger = opts.Logger
|
||||
}
|
||||
|
||||
newOpts.TLS = opts.TLS
|
||||
newOpts.KeyFile = opts.KeyFile
|
||||
newOpts.CertFile = opts.CertFile
|
||||
newOpts.ExplicitFTPS = opts.ExplicitFTPS
|
||||
|
||||
newOpts.PublicIp = opts.PublicIp
|
||||
newOpts.PassivePorts = opts.PassivePorts
|
||||
|
||||
return &newOpts
|
||||
}
|
||||
|
||||
// NewServer initialises a new FTP server. Configuration options are provided
|
||||
// via an instance of ServerOpts. Calling this function in your code will
|
||||
// probably look something like this:
|
||||
//
|
||||
// factory := &MyDriverFactory{}
|
||||
// server := server.NewServer(&server.ServerOpts{ Factory: factory })
|
||||
//
|
||||
// or:
|
||||
//
|
||||
// factory := &MyDriverFactory{}
|
||||
// opts := &server.ServerOpts{
|
||||
// Factory: factory,
|
||||
// Port: 2000,
|
||||
// Hostname: "127.0.0.1",
|
||||
// }
|
||||
// server := server.NewServer(opts)
|
||||
//
|
||||
func NewServer(opts *ServerOpts) *Server {
|
||||
opts = serverOptsWithDefaults(opts)
|
||||
s := new(Server)
|
||||
s.ServerOpts = opts
|
||||
s.listenTo = net.JoinHostPort(opts.Hostname, strconv.Itoa(opts.Port))
|
||||
s.logger = opts.Logger
|
||||
return s
|
||||
}
|
||||
|
||||
// NewConn constructs a new object that will handle the FTP protocol over
|
||||
// an active net.TCPConn. The TCP connection should already be open before
|
||||
// it is handed to this functions. driver is an instance of FTPDriver that
|
||||
// will handle all auth and persistence details.
|
||||
func (server *Server) newConn(tcpConn net.Conn, driver Driver) *Conn {
|
||||
c := new(Conn)
|
||||
c.namePrefix = "/"
|
||||
c.conn = tcpConn
|
||||
c.controlReader = bufio.NewReader(tcpConn)
|
||||
c.controlWriter = bufio.NewWriter(tcpConn)
|
||||
c.driver = driver
|
||||
c.auth = server.Auth
|
||||
c.server = server
|
||||
c.sessionID = newSessionID()
|
||||
c.logger = server.logger
|
||||
c.tlsConfig = server.tlsConfig
|
||||
|
||||
driver.Init(c)
|
||||
return c
|
||||
}
|
||||
|
||||
func simpleTLSConfig(certFile, keyFile string) (*tls.Config, error) {
|
||||
config := &tls.Config{}
|
||||
if config.NextProtos == nil {
|
||||
config.NextProtos = []string{"ftp"}
|
||||
}
|
||||
|
||||
var err error
|
||||
config.Certificates = make([]tls.Certificate, 1)
|
||||
config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// ListenAndServe asks a new Server to begin accepting client connections. It
|
||||
// accepts no arguments - all configuration is provided via the NewServer
|
||||
// function.
|
||||
//
|
||||
// If the server fails to start for any reason, an error will be returned. Common
|
||||
// errors are trying to bind to a privileged port or something else is already
|
||||
// listening on the same port.
|
||||
//
|
||||
func (server *Server) ListenAndServe() error {
|
||||
var listener net.Listener
|
||||
var err error
|
||||
var curFeats = featCmds
|
||||
|
||||
if server.ServerOpts.TLS {
|
||||
server.tlsConfig, err = simpleTLSConfig(server.CertFile, server.KeyFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
curFeats += " AUTH TLS\n PBSZ\n PROT\n"
|
||||
|
||||
if server.ServerOpts.ExplicitFTPS {
|
||||
listener, err = net.Listen("tcp", server.listenTo)
|
||||
} else {
|
||||
listener, err = tls.Listen("tcp", server.listenTo, server.tlsConfig)
|
||||
}
|
||||
} else {
|
||||
listener, err = net.Listen("tcp", server.listenTo)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
server.feats = fmt.Sprintf(feats, curFeats)
|
||||
|
||||
sessionID := ""
|
||||
server.logger.Printf(sessionID, "%s listening on %d", server.Name, server.Port)
|
||||
|
||||
return server.Serve(listener)
|
||||
}
|
||||
|
||||
// Serve accepts connections on a given net.Listener and handles each
|
||||
// request in a new goroutine.
|
||||
//
|
||||
func (server *Server) Serve(l net.Listener) error {
|
||||
server.listener = l
|
||||
server.ctx, server.cancel = context.WithCancel(context.Background())
|
||||
sessionID := ""
|
||||
for {
|
||||
tcpConn, err := server.listener.Accept()
|
||||
if err != nil {
|
||||
select {
|
||||
case <-server.ctx.Done():
|
||||
return ErrServerClosed
|
||||
default:
|
||||
}
|
||||
server.logger.Printf(sessionID, "listening error: %v", err)
|
||||
if ne, ok := err.(net.Error); ok && ne.Temporary() {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
driver, err := server.Factory.NewDriver()
|
||||
if err != nil {
|
||||
server.logger.Printf(sessionID, "Error creating driver, aborting client connection: %v", err)
|
||||
tcpConn.Close()
|
||||
} else {
|
||||
ftpConn := server.newConn(tcpConn, driver)
|
||||
go ftpConn.Serve()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shutdown will gracefully stop a server. Already connected clients will retain their connections
|
||||
func (server *Server) Shutdown() error {
|
||||
if server.cancel != nil {
|
||||
server.cancel()
|
||||
}
|
||||
if server.listener != nil {
|
||||
return server.listener.Close()
|
||||
}
|
||||
// server wasnt even started
|
||||
return nil
|
||||
}
|
245
vendor/github.com/goftp/server/socket.go
generated
vendored
Normal file
245
vendor/github.com/goftp/server/socket.go
generated
vendored
Normal file
|
@ -0,0 +1,245 @@
|
|||
// Copyright 2018 The goftp Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// DataSocket describes a data socket is used to send non-control data between the client and
|
||||
// server.
|
||||
type DataSocket interface {
|
||||
Host() string
|
||||
|
||||
Port() int
|
||||
|
||||
// the standard io.Reader interface
|
||||
Read(p []byte) (n int, err error)
|
||||
|
||||
// the standard io.ReaderFrom interface
|
||||
ReadFrom(r io.Reader) (int64, error)
|
||||
|
||||
// the standard io.Writer interface
|
||||
Write(p []byte) (n int, err error)
|
||||
|
||||
// the standard io.Closer interface
|
||||
Close() error
|
||||
}
|
||||
|
||||
type ftpActiveSocket struct {
|
||||
conn *net.TCPConn
|
||||
host string
|
||||
port int
|
||||
logger Logger
|
||||
}
|
||||
|
||||
func newActiveSocket(remote string, port int, logger Logger, sessionID string) (DataSocket, error) {
|
||||
connectTo := net.JoinHostPort(remote, strconv.Itoa(port))
|
||||
|
||||
logger.Print(sessionID, "Opening active data connection to "+connectTo)
|
||||
|
||||
raddr, err := net.ResolveTCPAddr("tcp", connectTo)
|
||||
|
||||
if err != nil {
|
||||
logger.Print(sessionID, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tcpConn, err := net.DialTCP("tcp", nil, raddr)
|
||||
|
||||
if err != nil {
|
||||
logger.Print(sessionID, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
socket := new(ftpActiveSocket)
|
||||
socket.conn = tcpConn
|
||||
socket.host = remote
|
||||
socket.port = port
|
||||
socket.logger = logger
|
||||
|
||||
return socket, nil
|
||||
}
|
||||
|
||||
func (socket *ftpActiveSocket) Host() string {
|
||||
return socket.host
|
||||
}
|
||||
|
||||
func (socket *ftpActiveSocket) Port() int {
|
||||
return socket.port
|
||||
}
|
||||
|
||||
func (socket *ftpActiveSocket) Read(p []byte) (n int, err error) {
|
||||
return socket.conn.Read(p)
|
||||
}
|
||||
|
||||
func (socket *ftpActiveSocket) ReadFrom(r io.Reader) (int64, error) {
|
||||
return socket.conn.ReadFrom(r)
|
||||
}
|
||||
|
||||
func (socket *ftpActiveSocket) Write(p []byte) (n int, err error) {
|
||||
return socket.conn.Write(p)
|
||||
}
|
||||
|
||||
func (socket *ftpActiveSocket) Close() error {
|
||||
return socket.conn.Close()
|
||||
}
|
||||
|
||||
type ftpPassiveSocket struct {
|
||||
conn net.Conn
|
||||
port int
|
||||
host string
|
||||
ingress chan []byte
|
||||
egress chan []byte
|
||||
logger Logger
|
||||
lock sync.Mutex // protects conn and err
|
||||
err error
|
||||
tlsConfig *tls.Config
|
||||
}
|
||||
|
||||
// Detect if an error is "bind: address already in use"
|
||||
//
|
||||
// Originally from https://stackoverflow.com/a/52152912/164234
|
||||
func isErrorAddressAlreadyInUse(err error) bool {
|
||||
errOpError, ok := err.(*net.OpError)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
errSyscallError, ok := errOpError.Err.(*os.SyscallError)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
errErrno, ok := errSyscallError.Err.(syscall.Errno)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if errErrno == syscall.EADDRINUSE {
|
||||
return true
|
||||
}
|
||||
const WSAEADDRINUSE = 10048
|
||||
if runtime.GOOS == "windows" && errErrno == WSAEADDRINUSE {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func newPassiveSocket(host string, port func() int, logger Logger, sessionID string, tlsConfig *tls.Config) (DataSocket, error) {
|
||||
socket := new(ftpPassiveSocket)
|
||||
socket.ingress = make(chan []byte)
|
||||
socket.egress = make(chan []byte)
|
||||
socket.logger = logger
|
||||
socket.host = host
|
||||
socket.tlsConfig = tlsConfig
|
||||
const retries = 10
|
||||
var err error
|
||||
for i := 1; i <= retries; i++ {
|
||||
socket.port = port()
|
||||
err = socket.GoListenAndServe(sessionID)
|
||||
if err != nil && socket.port != 0 && isErrorAddressAlreadyInUse(err) {
|
||||
// choose a different port on error already in use
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
return socket, err
|
||||
}
|
||||
|
||||
func (socket *ftpPassiveSocket) Host() string {
|
||||
return socket.host
|
||||
}
|
||||
|
||||
func (socket *ftpPassiveSocket) Port() int {
|
||||
return socket.port
|
||||
}
|
||||
|
||||
func (socket *ftpPassiveSocket) Read(p []byte) (n int, err error) {
|
||||
socket.lock.Lock()
|
||||
defer socket.lock.Unlock()
|
||||
if socket.err != nil {
|
||||
return 0, socket.err
|
||||
}
|
||||
return socket.conn.Read(p)
|
||||
}
|
||||
|
||||
func (socket *ftpPassiveSocket) ReadFrom(r io.Reader) (int64, error) {
|
||||
socket.lock.Lock()
|
||||
defer socket.lock.Unlock()
|
||||
if socket.err != nil {
|
||||
return 0, socket.err
|
||||
}
|
||||
|
||||
// For normal TCPConn, this will use sendfile syscall; if not,
|
||||
// it will just downgrade to normal read/write procedure
|
||||
return io.Copy(socket.conn, r)
|
||||
}
|
||||
|
||||
func (socket *ftpPassiveSocket) Write(p []byte) (n int, err error) {
|
||||
socket.lock.Lock()
|
||||
defer socket.lock.Unlock()
|
||||
if socket.err != nil {
|
||||
return 0, socket.err
|
||||
}
|
||||
return socket.conn.Write(p)
|
||||
}
|
||||
|
||||
func (socket *ftpPassiveSocket) Close() error {
|
||||
socket.lock.Lock()
|
||||
defer socket.lock.Unlock()
|
||||
if socket.conn != nil {
|
||||
return socket.conn.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (socket *ftpPassiveSocket) GoListenAndServe(sessionID string) (err error) {
|
||||
laddr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort("", strconv.Itoa(socket.port)))
|
||||
if err != nil {
|
||||
socket.logger.Print(sessionID, err)
|
||||
return
|
||||
}
|
||||
|
||||
var listener net.Listener
|
||||
listener, err = net.ListenTCP("tcp", laddr)
|
||||
if err != nil {
|
||||
socket.logger.Print(sessionID, err)
|
||||
return
|
||||
}
|
||||
|
||||
add := listener.Addr()
|
||||
parts := strings.Split(add.String(), ":")
|
||||
port, err := strconv.Atoi(parts[len(parts)-1])
|
||||
if err != nil {
|
||||
socket.logger.Print(sessionID, err)
|
||||
return
|
||||
}
|
||||
|
||||
socket.port = port
|
||||
if socket.tlsConfig != nil {
|
||||
listener = tls.NewListener(listener, socket.tlsConfig)
|
||||
}
|
||||
|
||||
socket.lock.Lock()
|
||||
go func() {
|
||||
defer socket.lock.Unlock()
|
||||
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
socket.err = err
|
||||
return
|
||||
}
|
||||
socket.err = nil
|
||||
socket.conn = conn
|
||||
_ = listener.Close()
|
||||
}()
|
||||
return nil
|
||||
}
|
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
|
@ -74,6 +74,8 @@ github.com/dropbox/dropbox-sdk-go-unofficial/dropbox/team_policies
|
|||
github.com/dropbox/dropbox-sdk-go-unofficial/dropbox/users_common
|
||||
# github.com/go-ini/ini v1.37.0
|
||||
github.com/go-ini/ini
|
||||
# github.com/goftp/server v0.0.0-20180914132916-1fd52c8552f1
|
||||
github.com/goftp/server
|
||||
# github.com/golang/protobuf v1.1.0
|
||||
github.com/golang/protobuf/proto
|
||||
# github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135
|
||||
|
|
Loading…
Reference in New Issue
Block a user