Skip to content

Commit

Permalink
Introduced the FtpServer struct, moving a lot of static code into it
Browse files Browse the repository at this point in the history
  • Loading branch information
fclairamb committed Sep 25, 2016
1 parent caeb141 commit 36704cb
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 130 deletions.
5 changes: 3 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import (

var (
gracefulChild = flag.Bool("graceful", false, "listen on fd open 3 (internal use only)")
stressTest = flag.Bool("stressTest", false, "start a client making connections")
stressTest = flag.Bool("stressTest", false, "start a client making connections")
)

func main() {
flag.Parse()
if *stressTest {
go client.StressTest()
}
server := server.NewFtpServer(sample.NewSampleDriver())
go server.Monitor()
server.Start(sample.NewSampleDriver(), *gracefulChild)
server.ListenAndServe(*gracefulChild)
}
6 changes: 3 additions & 3 deletions server/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ func (p *ClientHandler) HandleUser() {

func (p *ClientHandler) HandlePass() {
// think about using https://developer.bitium.com
if driver.CheckUser(p.user, p.param, &p.userInfo) {
if p.daddy.driver.CheckUser(p.user, p.param, &p.userInfo) {
p.writeMessage(230, "Password ok, continue")
} else {
p.writeMessage(530, "Incorrect password, not logged in")
p.theConnection.Close()
delete(ConnectionMap, p.cid)
p.conn.Close()
delete(p.daddy.ConnectionMap, p.cid)
}
}
2 changes: 1 addition & 1 deletion server/directory.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func (p *ClientHandler) HandleList() {
func (p *ClientHandler) dirList() ([]byte, error) {
var buf bytes.Buffer

files, err := driver.GetFiles(&p.userInfo)
files, err := p.daddy.driver.GetFiles(&p.userInfo)
for _, file := range files {

if file["isDir"] != "" {
Expand Down
4 changes: 2 additions & 2 deletions server/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ func (p *ClientHandler) HandleType() {
func (p *ClientHandler) HandleQuit() {
//fmt.Println("Goodbye")
p.writeMessage(221, "Goodbye")
p.theConnection.Close()
delete(ConnectionMap, p.cid)
p.conn.Close()
delete(p.daddy.ConnectionMap, p.cid)
}

func (p *ClientHandler) HandleCwd() {
Expand Down
10 changes: 5 additions & 5 deletions server/monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,20 @@ func trimGuid(guid string) string {
return guid[0:6]
}

func handler(w http.ResponseWriter, r *http.Request) {
func (server *FtpServer) handler(w http.ResponseWriter, r *http.Request) {

fmt.Fprintf(w, "%d client(s), %d passive(s), Up for %s\n",
len(ConnectionMap), PassiveCount, countdown(UpSince))
len(server.ConnectionMap), server.PassiveCount, countdown(server.StartTime))

for k, v := range ConnectionMap {
for k, v := range server.ConnectionMap {
fmt.Fprintf(w, " %s %s, %s\n", trimGuid(k), countdown(v.connectedAt), v.user)
for pk, pv := range v.passives {
fmt.Fprintf(w, " %s %s, %d %s %s\n", trimGuid(pk), countdown(pv.listenAt), pv.port, pv.command, pv.param)
}
}
}

func Monitor() {
http.HandleFunc("/", handler)
func (server *FtpServer) Monitor() {
http.HandleFunc("/", server.handler)
http.ListenAndServe(":5010", nil)
}
28 changes: 15 additions & 13 deletions server/passive.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package server

import "fmt"
import "sync"
import "net"
import "strconv"
import "strings"
import "time"
import (
"time"
"sync"
"net"
"strings"
"strconv"
"fmt"
)

func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {
c := make(chan struct{})
Expand Down Expand Up @@ -36,14 +38,14 @@ type Passive struct {
waiter sync.WaitGroup
}

func (p *ClientHandler) closePassive(passive *Passive) {
func (c *ClientHandler) closePassive(passive *Passive) {
err := passive.connection.Close()
if err != nil {
passive.closeFailedAt = time.Now().Unix()
} else {
passive.closeSuccessAt = time.Now().Unix()
delete(p.passives, passive.cid)
PassiveCount--
delete(c.passives, passive.cid)
c.daddy.PassiveCount--
}
}

Expand All @@ -61,8 +63,8 @@ func getThatPassiveConnection(passiveListen *net.TCPListener, p *Passive) {
p.waiter.Done()
}

func NewPassive(passiveListen *net.TCPListener, cid string, now int64) *Passive {
PassiveCount++
func (c *ClientHandler) NewPassive(passiveListen *net.TCPListener, cid string, now int64) *Passive {
c.daddy.PassiveCount++
p := Passive{}
p.cid = cid
p.listenAt = now
Expand Down Expand Up @@ -97,7 +99,7 @@ func (p *ClientHandler) HandlePassive() {
}

cid := genClientID()
passive := NewPassive(passiveListen, cid, time.Now().Unix())
passive := p.NewPassive(passiveListen, cid, time.Now().Unix())
passive.command = p.command
passive.param = p.param
p.lastPassCid = cid
Expand All @@ -106,7 +108,7 @@ func (p *ClientHandler) HandlePassive() {
if p.command == "PASV" {
p1 := passive.port / 256
p2 := passive.port - (p1 * 256)
addr := p.theConnection.LocalAddr()
addr := p.conn.LocalAddr()
tokens := strings.Split(addr.String(), ":")
host := tokens[0]
quads := strings.Split(host, ".")
Expand Down
153 changes: 84 additions & 69 deletions server/server.go
Original file line number Diff line number Diff line change
@@ -1,83 +1,98 @@
package server

import "bufio"
import "fmt"
import "io"
import "net"
import "strings"
import "sync"
import (
"time"
"bufio"
"net"
"sync"
"fmt"
"strings"
"io"
)

var CommandMap map[string]func(*ClientHandler)
var ConnectionMap map[string]*ClientHandler
var PassiveCount int
var UpSince int64

// TODO: Put this in a server handler struct
var driver Driver
var commandsMap map[string]func(*ClientHandler)

func init() {
// This is shared between FtpServer instances as there's no point in making the FTP commands behave differently
// between them.

commandsMap = make(map[string]func(*ClientHandler))

commandsMap["USER"] = (*ClientHandler).HandleUser
commandsMap["PASS"] = (*ClientHandler).HandlePass
commandsMap["STOR"] = (*ClientHandler).HandleStore
commandsMap["APPE"] = (*ClientHandler).HandleStore
commandsMap["STAT"] = (*ClientHandler).HandleStat

commandsMap["SYST"] = (*ClientHandler).HandleSyst
commandsMap["PWD"] = (*ClientHandler).HandlePwd
commandsMap["TYPE"] = (*ClientHandler).HandleType
commandsMap["PASV"] = (*ClientHandler).HandlePassive
commandsMap["EPSV"] = (*ClientHandler).HandlePassive
commandsMap["NLST"] = (*ClientHandler).HandleList
commandsMap["LIST"] = (*ClientHandler).HandleList
commandsMap["QUIT"] = (*ClientHandler).HandleQuit
commandsMap["CWD"] = (*ClientHandler).HandleCwd
commandsMap["SIZE"] = (*ClientHandler).HandleSize
commandsMap["RETR"] = (*ClientHandler).HandleRetr
}

type FtpServer struct {
driver Driver // Driver to handle all the actual authentication and files access logic
Listener net.Listener // Listener used to receive files
ConnectionMap map[string]*ClientHandler // Connections map
PassiveCount int // Number of passive connections opened
StartTime int64 // Time when the server was started
}

type ClientHandler struct {
writer *bufio.Writer
reader *bufio.Reader
theConnection net.Conn
waiter sync.WaitGroup
user string
homeDir string
path string
ip string
command string
param string
total int64
buffer []byte
cid string
connectedAt int64
passives map[string]*Passive
lastPassCid string
userInfo map[string]string
daddy *FtpServer // Server on which the connection was performed
writer *bufio.Writer // Writer on the TCP connection
reader *bufio.Reader // Reader on the TCP connection
conn net.Conn // TCP connection
waiter sync.WaitGroup
user string
homeDir string
path string
ip string
command string
param string
total int64
buffer []byte
cid string
connectedAt int64
passives map[string]*Passive // Index of all the passive connections that are associated to this control connection
lastPassCid string
userInfo map[string]string
}

func init() {
UpSince = time.Now().Unix()

CommandMap = make(map[string]func(*ClientHandler))

CommandMap["USER"] = (*ClientHandler).HandleUser
CommandMap["PASS"] = (*ClientHandler).HandlePass
CommandMap["STOR"] = (*ClientHandler).HandleStore
CommandMap["APPE"] = (*ClientHandler).HandleStore
CommandMap["STAT"] = (*ClientHandler).HandleStat

CommandMap["SYST"] = (*ClientHandler).HandleSyst
CommandMap["PWD"] = (*ClientHandler).HandlePwd
CommandMap["TYPE"] = (*ClientHandler).HandleType
CommandMap["PASV"] = (*ClientHandler).HandlePassive
CommandMap["EPSV"] = (*ClientHandler).HandlePassive
CommandMap["NLST"] = (*ClientHandler).HandleList
CommandMap["LIST"] = (*ClientHandler).HandleList
CommandMap["QUIT"] = (*ClientHandler).HandleQuit
CommandMap["CWD"] = (*ClientHandler).HandleCwd
CommandMap["SIZE"] = (*ClientHandler).HandleSize
CommandMap["RETR"] = (*ClientHandler).HandleRetr

ConnectionMap = make(map[string]*ClientHandler)
func NewFtpServer(driver Driver) *FtpServer {
return &FtpServer{
driver: driver,
StartTime: time.Now().Unix(), // Might make sense to put it in Start method
ConnectionMap: make(map[string]*ClientHandler),
}
}

func NewParadise(connection net.Conn, cid string, now int64) *ClientHandler {
p := ClientHandler{}

p.writer = bufio.NewWriter(connection)
p.reader = bufio.NewReader(connection)
p.path = "/"
p.theConnection = connection
p.ip = connection.RemoteAddr().String()
p.cid = cid
p.connectedAt = now
p.passives = make(map[string]*Passive)
p.userInfo = make(map[string]string)
p.userInfo["path"] = "/"
return &p
func (server *FtpServer) NewClientHandler(connection net.Conn, cid string, now int64) *ClientHandler {

p := &ClientHandler{
daddy: server,
conn: connection,
writer: bufio.NewWriter(connection),
reader: bufio.NewReader(connection),
connectedAt: time.Now().UTC().UnixNano(),
path: "/",
ip: connection.RemoteAddr().String(), // TODO: Do we need this ?
passives: make(map[string]*Passive),
userInfo: make(map[string]string),
}

// Just respecting the existing logic here, this could be probably be dropped at some point
p.userInfo["path"] = p.path

return p
}

func (p *ClientHandler) lastPassive() *Passive {
Expand All @@ -96,7 +111,7 @@ func (p *ClientHandler) HandleCommands() {
for {
line, err := p.reader.ReadString('\n')
if err != nil {
delete(ConnectionMap, p.cid)
delete(p.daddy.ConnectionMap, p.cid)
//fmt.Println(p.id, " end ", len(ConnectionMap))
if err == io.EOF {
//continue
Expand All @@ -107,7 +122,7 @@ func (p *ClientHandler) HandleCommands() {
p.command = command
p.param = param

fn := CommandMap[command]
fn := commandsMap[command]
if fn == nil {
p.writeMessage(550, "not allowed")
} else {
Expand Down
Loading

0 comments on commit 36704cb

Please sign in to comment.