package main
import (
"errors"
"log"
"net"
"os"
"strings"
"github.com/taruti/sftpd"
"github.com/taruti/sshutil"
"golang.org/x/crypto/ssh"
)
func main() {
RunServerLowLevel(":2022", ReadOnlyDirFs{})
}
type ReadOnlyDirFs struct {
sftpd.EmptyFS
}
func rfsMangle(path string) (string, error) {
if strings.Contains(path, "..") {
return "<invalid>", errors.New("Invalid path")
}
if len(path) > 0 && path[0] == '/' {
path = path[1:]
}
path = "./" + path
return path, nil
}
func (fs ReadOnlyDirFs) Stat(name string, isLstat bool) (*sftpd.Attr, error) {
p, e := rfsMangle(name)
if e != nil {
return nil, e
}
var fi os.FileInfo
if isLstat {
fi, e = os.Lstat(p)
} else {
fi, e = os.Stat(p)
}
if e != nil {
return nil, e
}
var a sftpd.Attr
a.FillFrom(fi)
return &a, nil
}
// RunServerLowLevel is an example how to use the low level API
func RunServerLowLevel(hostport string, fs sftpd.FileSystem) {
e := runServer(hostport, fs)
if e != nil {
log.Println("running server errored:", e)
}
}
func runServer(hostport string, fs sftpd.FileSystem) error {
config := &ssh.ServerConfig{
PasswordCallback: sshutil.CreatePasswordCheck("root", []byte("123456")),
}
// Add the sshutil.RSA2048 and sshutil.Save flags if needed for the server in question...
hkey, e := sshutil.KeyLoader{Flags: sshutil.Create | sshutil.RSA2048}.Load()
// hkey, e := sshutil.KeyLoader{Flags: sshutil.Create}.Load()
if e != nil {
return e
}
config.AddHostKey(hkey)
listener, e := net.Listen("tcp", hostport)
if e != nil {
return e
}
log.Printf("Listening on %s user %s pass %s\n", hostport, "root", "123456")
for {
conn, e := listener.Accept()
if e != nil {
return e
}
go HandleConn(conn, config, fs)
}
}
func HandleConn(conn net.Conn, config *ssh.ServerConfig, fs sftpd.FileSystem) {
defer conn.Close()
e := handleConn(conn, config, fs)
if e != nil {
log.Println("sftpd connection errored:", e)
}
}
func handleConn(conn net.Conn, config *ssh.ServerConfig, fs sftpd.FileSystem) error {
sc, chans, reqs, e := ssh.NewServerConn(conn, config)
if e != nil {
return e
}
defer sc.Close()
// The incoming Request channel must be serviced.
go PrintDiscardRequests(reqs)
// Service the incoming Channel channel.
for newChannel := range chans {
if newChannel.ChannelType() != "session" {
newChannel.Reject(ssh.UnknownChannelType, "unknown channel type")
continue
}
channel, requests, err := newChannel.Accept()
if err != nil {
return err
}
go func(in <-chan *ssh.Request) {
for req := range in {
ok := false
switch {
case sftpd.IsSftpRequest(req):
ok = true
go func() {
e := sftpd.ServeChannel(channel, fs)
if e != nil {
log.Println("sftpd servechannel failed:", e)
}
}()
}
req.Reply(ok, nil)
}
}(requests)
}
return nil
}
func PrintDiscardRequests(in <-chan *ssh.Request) {
for req := range in {
log.Println("Discarding ssh request", req.Type, *req)
if req.WantReply {
req.Reply(false, nil)
}
}
}
when i debug your code, i found op code is 200(ssh_FXP_EXTENDED), but in switch loop it did not handled.