smb2

package module
v1.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 19, 2024 License: BSD-2-Clause Imports: 36 Imported by: 0

README

smb2

Build Status Go Reference

Description

An SMB2/3 client implementation. This is a fork of the project github.com/hirochachacha/go-smb2. Any releases will be pre-1.0.0 for some time as features and bug fixes are implemented.

Installation

go get github.com/cyberb/go-smb2

Documentation

https://pkg.go.dev/github.com/cyberb/go-smb2

Examples

List share names
package main

import (
	"fmt"
	"net"

	"github.com/cyberb/go-smb2"
)

func main() {
	d := &smb2.Dialer{
		Initiator: &smb2.NTLMInitiator{
			User:     "USERNAME",
			Password: "PASSWORD",
		},
	}

	s, err := d.Dial(context.Background(), "SERVERNAME:445")
	if err != nil {
		panic(err)
	}
	defer s.Logoff()

	names, err := s.ListSharenames()
	if err != nil {
		panic(err)
	}

	for _, name := range names {
		fmt.Println(name)
	}
}
File manipulation
package main

import (
	"io"
	"net"

	"github.com/cyberb/go-smb2"
)

func main() {
	d := &smb2.Dialer{
		Initiator: &smb2.NTLMInitiator{
			User:     "USERNAME",
			Password: "PASSWORD",
		},
	}

	s, err := d.Dial(context.Background(), "SERVERNAME:445")
	if err != nil {
		panic(err)
	}
	defer s.Logoff()

	fs, err := s.Mount("SHARENAME")
	if err != nil {
		panic(err)
	}
	defer fs.Umount()

	f, err := fs.Create("hello.txt")
	if err != nil {
		panic(err)
	}
	defer fs.Remove("hello.txt")
	defer f.Close()

	_, err = f.Write([]byte("Hello world!"))
	if err != nil {
		panic(err)
	}

	_, err = f.Seek(0, io.SeekStart)
	if err != nil {
		panic(err)
	}

	bs, err := io.ReadAll(f)
	if err != nil {
		panic(err)
	}

	fmt.Println(string(bs))
}
Check error types
package main

import (
	"context"
	"fmt"
	"net"
	"os"

	"github.com/cyberb/go-smb2"
)

func main() {
	d := &smb2.Dialer{
		Initiator: &smb2.NTLMInitiator{
			User:     "USERNAME",
			Password: "PASSWORD",
		},
	}

	s, err := d.Dial(context.Background(), "SERVERNAME:445")
	if err != nil {
		panic(err)
	}
	defer s.Logoff()

	fs, err := s.Mount("SHARENAME")
	if err != nil {
		panic(err)
	}
	defer fs.Umount()

	_, err = fs.Open("notExist.txt")

	fmt.Println(os.IsNotExist(err)) // true
	fmt.Println(os.IsExist(err))    // false

	fs.WriteFile("hello2.txt", []byte("test"), 0444)
	err = fs.WriteFile("hello2.txt", []byte("test2"), 0444)
	fmt.Println(os.IsPermission(err)) // true

	ctx, cancel := context.WithTimeout(context.Background(), 0)
	defer cancel()

	_, err = fs.WithContext(ctx).Open("hello.txt")

	fmt.Println(os.IsTimeout(err)) // true
}
Glob and WalkDir through FS interface
package main

import (
	"fmt"
	"net"
	iofs "io/fs"

	"github.com/cyberb/go-smb2"
)

func main() {
	d := &smb2.Dialer{
		Initiator: &smb2.NTLMInitiator{
			User:     "USERNAME",
			Password: "PASSWORD",
		},
	}

	s, err := d.Dial(context.Background(), "SERVERNAME:445")
	if err != nil {
		panic(err)
	}
	defer s.Logoff()

	fs, err := s.Mount("SHARENAME")
	if err != nil {
		panic(err)
	}
	defer fs.Umount()

	matches, err := iofs.Glob(fs.DirFS("."), "*")
	if err != nil {
		panic(err)
	}
	for _, match := range matches {
		fmt.Println(match)
	}

	err = iofs.WalkDir(fs.DirFS("."), ".", func(path string, d iofs.DirEntry, err error) error {
		fmt.Println(path, d, err)

		return nil
	})
	if err != nil {
		panic(err)
	}
}
Readdir example via DFS
package main

import (
	"fmt"
	"net"
	iofs "io/fs"

	"github.com/cyberb/go-smb2"
)

func main() {
	conn, err := net.Dial("tcp", "SERVERNAME:445")
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	d := &smb2.Dialer{
		Initiator: &smb2.NTLMInitiator{
			User:     "USERNAME",
			Password: "PASSWORD",
		},
	}

	s, err := d.Dial(conn)
	if err != nil {
		panic(err)
	}
	defer s.Logoff()

	fs, err := s.Mount("SHARENAME")
	if err != nil {
		panic(err)
	}
	defer fs.Umount()

	ipc, err := s.Mount("$IPC")
	if err != nil {
		panic(err)
	}
	defer ipc.Umount()


	// Fetch the DFS list for the directory, and specify if its DFS link
	targetList, err := ipc.GetDFSTargetList(s, "SHARENAME", "DFSDIR", false)
	if err != nil {
		panic(err)
	}

	isLink := false

	targetList, err := ipc.GetDFSTargetList(session, sharename, dfsdir, false)
	if err != nil {
		t.Error("unexpected error: ", err)
	}

	for _, target := range targetList {

		address := target.TargetAddress
		actualTargetFolder := target.TargetFolder

		//In case of non dfs links, what we get in Target folder is the base address of dfs folder. We need to append
		//the directory name to reach the actual target
		if len(target.TargetFolder) > 0 && !isLink {
			actualTargetFolder = fmt.Sprintf("%s//%s", target.TargetFolder, dirname)
		} else if !isLink {
			actualTargetFolder = dirname
		}
		CONNECT := fmt.Sprintf("%s:%d", address, 445)
		conn, err := net.Dial("tcp", CONNECT)
		if err != nil {
			panic(err)
		}
		defer conn.Close()

		d := &smb2.Dialer{
			Initiator: &smb2.NTLMInitiator{
				User:     "USERNAME",
				Password: "PASSWORD",
			},
		}

		s, err := d.Dial(conn)
		if err != nil {
			panic(err)
		}
		defer s.Logoff()

		sh, err := s.Mount(target.TargetShare)
		if err != nil {
			panic(err)
		}

		_, err = sh.ReadDir(actualTargetFolder)
		if err == nil {
			break
		}
	}
}

Documentation

Overview

Package smb2 implements the SMB2/3 client in [MS-SMB2].

https://msdn.microsoft.com/en-us/library/cc246482.aspx

This package doesn't support CAP_UNIX extension. Symlink is supported by FSCTL_SET_REPARSE_POINT and FSCTL_GET_REPARSE_POINT. The symlink-following algorithm is explained in 2.2.2.2.1 and 2.2.2.2.1.1.

https://msdn.microsoft.com/en-us/library/cc246542.aspx

Supported features and protocol versions are declared in feature.go.

Example
d := &smb2.Dialer{
	Initiator: &smb2.NTLMInitiator{
		User:     "Guest",
		Password: "",
		Domain:   "MicrosoftAccount",
	},
}

c, err := d.Dial(context.Background(), "localhost:445")
if err != nil {
	panic(err)
}
defer func() {
	_ = c.Logoff()
}()

fs, err := c.Mount(`\\localhost\share`)
if err != nil {
	panic(err)
}
defer func() {
	_ = fs.Umount()
}()

f, err := fs.Create("hello.txt")
if err != nil {
	panic(err)
}
defer func() {
	f.Close()
	_ = fs.Remove("hello.txt")
}()

_, err = f.Write([]byte("Hello world!"))
if err != nil {
	panic(err)
}

_, err = f.Seek(0, io.SeekStart)
if err != nil {
	panic(err)
}

bs, err := io.ReadAll(f)
if err != nil {
	panic(err)
}

fmt.Println(string(bs))

// Hello world!
Output:

Index

Examples

Constants

View Source
const MaxReadSizeLimit = 0x100000 // deprecated constant
View Source
const PathSeparator = '\\'

Variables

View Source
var ErrBadPattern = errors.New("syntax error in pattern")

ErrBadPattern indicates a pattern was malformed.

View Source
var NORMALIZE_PATH = true // normalize path arguments automatically

Functions

func IsPathSeparator

func IsPathSeparator(c uint8) bool

func Match

func Match(pattern, name string) (matched bool, err error)

Match reports whether name matches the shell file name pattern. The pattern syntax is:

pattern:
	{ term }
term:
	'*'         matches any sequence of non-Separator characters
	'?'         matches any single non-Separator character
	'[' [ '^' ] { character-range } ']'
	            character class (must be non-empty)
	c           matches character c (c != '*', '?', '[')

character-range:
	c           matches character c (c != '-', ']')
	lo '-' hi   matches character c for lo <= c <= hi

Match requires pattern to match all of name, not just a substring. The only possible returned error is ErrBadPattern, when pattern is malformed.

Types

type Client

type Client = Session // deprecated type name

type DFSTarget

type DFSTarget struct {
	TargetAddress string
	TargetShare   string
	TargetFolder  string
	// contains filtered or unexported fields
}

DFSTarget response struct for a DFS request

type Dialer

type Dialer struct {
	MaxCreditBalance uint16 // if it's zero, clientMaxCreditBalance is used. (See feature.go for more details)
	Negotiator       Negotiator
	Initiator        Initiator
}

Dialer contains options for func (*Dialer) Dial.

func (*Dialer) Dial

func (d *Dialer) Dial(ctx context.Context, address string) (*Session, error)

Dial connects to the SMB server and performs negotiation and authentication. The returned session doesn't inherit the context. If you want to use the same context call Session.WithContext.

func (*Dialer) DialConn

func (d *Dialer) DialConn(ctx context.Context, tcpConn net.Conn, address string) (*Session, error)

DialConn performs negotation and authentication to the server at the other end of tcpConn. The returned session doesn't inherit the context. If you want to use the same context call Session.WithContext. Note: Multiple sessions may not be negotiated on a single TCP connection, so if you want another session you must provide another TCP connection.

type File

type File struct {
	// contains filtered or unexported fields
}

func (*File) Chmod

func (f *File) Chmod(mode os.FileMode) error

func (*File) Close

func (f *File) Close() error

func (*File) Name

func (f *File) Name() string

func (*File) Read

func (f *File) Read(b []byte) (n int, err error)

func (*File) ReadAt

func (f *File) ReadAt(b []byte, off int64) (n int, err error)

ReadAt implements io.ReaderAt.

func (*File) ReadFrom

func (f *File) ReadFrom(r io.Reader) (n int64, err error)

ReadFrom implements io.ReadFrom. If r is *File on the same *Share as f, it invokes server-side copy.

func (*File) Readdir

func (f *File) Readdir(n int) (fi []os.FileInfo, err error)

func (*File) Readdirnames

func (f *File) Readdirnames(n int) (names []string, err error)

func (*File) Seek

func (f *File) Seek(offset int64, whence int) (ret int64, err error)

Seek implements io.Seeker.

func (*File) Stat

func (f *File) Stat() (os.FileInfo, error)

func (*File) Statfs

func (f *File) Statfs() (FileFsInfo, error)

func (*File) Sync

func (f *File) Sync() (err error)

func (*File) Truncate

func (f *File) Truncate(size int64) error

func (*File) Write

func (f *File) Write(b []byte) (n int, err error)

func (*File) WriteAt

func (f *File) WriteAt(b []byte, off int64) (n int, err error)

WriteAt implements io.WriterAt.

func (*File) WriteString

func (f *File) WriteString(s string) (n int, err error)

func (*File) WriteTo

func (f *File) WriteTo(w io.Writer) (n int64, err error)

WriteTo implements io.WriteTo. If w is *File on the same *Share as f, it invokes server-side copy.

type FileFsInfo

type FileFsInfo interface {
	// The number of bytes in a single block on the volume. A block is the smallest allocation unit on the volume.
	BlockSize() uint64
	// Number of sectors in each block.
	FragmentSize() uint64
	// Total number of blocks on the volume that are available to the user.
	TotalBlockCount() uint64
	// Total number of blocks on the volume.
	FreeBlockCount() uint64
	// Total number of free blocks on the volume that are available to the user.
	AvailableBlockCount() uint64
}

type FileStat

type FileStat struct {
	CreationTime   time.Time
	LastAccessTime time.Time
	LastWriteTime  time.Time
	ChangeTime     time.Time
	EndOfFile      int64
	AllocationSize int64
	FileAttributes uint32
	FileName       string
}

func (*FileStat) IsDir

func (fs *FileStat) IsDir() bool

func (*FileStat) ModTime

func (fs *FileStat) ModTime() time.Time

func (*FileStat) Mode

func (fs *FileStat) Mode() os.FileMode

func (*FileStat) Name

func (fs *FileStat) Name() string

func (*FileStat) Size

func (fs *FileStat) Size() int64

func (*FileStat) Sys

func (fs *FileStat) Sys() interface{}

type Initiator

type Initiator interface {
	OID() asn1.ObjectIdentifier
	InitSecContext() ([]byte, error)            // GSS_Init_sec_context
	AcceptSecContext(sc []byte) ([]byte, error) // GSS_Accept_sec_context
	Sum(bs []byte) []byte                       // GSS_getMIC
	SessionKey() []byte                         // QueryContextAttributes(ctx, SECPKG_ATTR_SESSION_KEY, &out)
}

type InternalError

type InternalError struct {
	Message string
}

InternalError represents internal error.

func (*InternalError) Error

func (err *InternalError) Error() string

type InvalidResponseError

type InvalidResponseError struct {
	Message string
}

InvalidResponseError represents a data sent by the server is corrupted or unexpected.

func (*InvalidResponseError) Error

func (err *InvalidResponseError) Error() string

type MapChars

type MapChars int

Mapping strategies that can be used when a reserved character is encountered in a file name.

const (
	// Don't map reserved characters
	MapCharsNone MapChars = 0
	// Map reserved characters using the Services for Mac scheme. This is
	// equivalent to using the 'mapposix' when mounting a volume in Linux.
	MapCharsSFM MapChars = 1
	// Map reserved characters using the Services for Unix scheme. This is
	// equivalent to using 'mapchars' when mounting a volume in Linux.
	MapCharsSFU MapChars = 2
)

type MountOption

type MountOption func(*mountOptions)

configures a Session.Mount() call

func WithMapChars

func WithMapChars() MountOption

The mount will apply character mapping to file names with reserved characters equivalent to samba's `mapchars` option.

If no character mapping option is applied, reserved characters will be passed to the SMB server unchanged resulting in a file with an 8.3 file name (e.g. TEXTFI~1.TXT) https://en.wikipedia.org/wiki/8.3_filename.

func WithMapPosix

func WithMapPosix() MountOption

The mount will apply character mapping to file names with reserved characters equivalent to samba's 'mapposix' option.

If no character mapping option is applied, reserved characters will be passed to the SMB server unchanged resulting in a file with an 8.3 file name (e.g. TEXTFI~1.TXT) https://en.wikipedia.org/wiki/8.3_filename.

type NTLMInitiator

type NTLMInitiator struct {
	User        string
	Password    string
	Hash        []byte
	Domain      string
	Workstation string
	TargetSPN   string
	// contains filtered or unexported fields
}

NTLMInitiator implements session-setup through NTLMv2. It doesn't support NTLMv1. You can use Hash instead of Password.

func (*NTLMInitiator) AcceptSecContext

func (i *NTLMInitiator) AcceptSecContext(sc []byte) ([]byte, error)

func (*NTLMInitiator) InitSecContext

func (i *NTLMInitiator) InitSecContext() ([]byte, error)

func (*NTLMInitiator) OID

func (*NTLMInitiator) SessionKey

func (i *NTLMInitiator) SessionKey() []byte

func (*NTLMInitiator) Sum

func (i *NTLMInitiator) Sum(bs []byte) []byte

type Negotiator

type Negotiator struct {
	RequireMessageSigning bool     // enforce signing?
	ClientGuid            [16]byte // if it's zero, generated by crypto/rand.
	SpecifiedDialect      uint16   // if it's zero, clientDialects is used. (See feature.go for more details)
}

Negotiator contains options for func (*Dialer) Dial.

type RemoteFile

type RemoteFile = File // deprecated type name

type RemoteFileStat

type RemoteFileStat = FileStat // deprecated type name

type RemoteFileSystem

type RemoteFileSystem = Share // deprecated type name

type ResponseError

type ResponseError struct {
	Code uint32 // NTSTATUS
	// contains filtered or unexported fields
}

ResponseError represents a error with a nt status code sent by the server. The NTSTATUS is defined in [MS-ERREF]. https://msdn.microsoft.com/en-au/library/cc704588.aspx

func (*ResponseError) Error

func (err *ResponseError) Error() string

type Session

type Session struct {
	// contains filtered or unexported fields
}

Session represents a SMB session.

func (*Session) ListSharenames

func (c *Session) ListSharenames() ([]string, error)

func (*Session) Logoff

func (c *Session) Logoff() error

Logoff invalidates the current SMB session.

func (*Session) Mount

func (c *Session) Mount(sharename string, opts ...MountOption) (*Share, error)

Mount mounts the SMB share. sharename must follow format like `<share>` or `\\<server>\<share>`. Note that the mounted share doesn't inherit session's context. If you want to use the same context, call Share.WithContext manually.

func (*Session) WithContext

func (c *Session) WithContext(ctx context.Context) *Session

type Share

type Share struct {
	// contains filtered or unexported fields
}

Share represents a SMB tree connection with VFS interface.

func (*Share) Chmod

func (fs *Share) Chmod(name string, mode os.FileMode) error

func (*Share) Chtimes

func (fs *Share) Chtimes(name string, atime time.Time, mtime time.Time) error

func (*Share) Create

func (fs *Share) Create(name string) (*File, error)

func (*Share) DirFS

func (s *Share) DirFS(dirname string) fs.FS

func (*Share) GetDFSTargetList

func (fs *Share) GetDFSTargetList(c *Session, sharename, dirname string, isLink bool) ([]*DFSTarget, error)
GetDFSTargetList - This function fetches the DFS target for a directory. This is to invoked on $IPC share only.

INPUT:

  1. *Session --> This is required to get the target server name.
  2. sharename --> As this function is to be exposed on $IPC share, this function requires the sharename on which the DFS exists.
  3. dirname --> directory name for which the DFS target is to be fetched
  4. isLink --> If the target directory is a DFS Link

OUTPUT:

  1. []DFSTarget --> This is the list of folder targets(referrals) where this directory is present.
  2. error --> Error if we fetch DFS operation fails.

func (*Share) Glob

func (fs *Share) Glob(pattern string) (matches []string, err error)

Glob should work like filepath.Glob.

func (*Share) Lstat

func (fs *Share) Lstat(name string) (os.FileInfo, error)

func (*Share) Mkdir

func (fs *Share) Mkdir(name string, perm os.FileMode) error

func (*Share) MkdirAll

func (fs *Share) MkdirAll(path string, perm os.FileMode) error

MkdirAll mimics os.MkdirAll

func (*Share) Open

func (fs *Share) Open(name string) (*File, error)

func (*Share) OpenFile

func (fs *Share) OpenFile(name string, flag int, perm os.FileMode) (*File, error)

func (*Share) ReadDir

func (fs *Share) ReadDir(dirname string) ([]os.FileInfo, error)

func (*Share) ReadFile

func (fs *Share) ReadFile(filename string) ([]byte, error)
func (fs *Share) Readlink(name string) (string, error)

func (*Share) Remove

func (fs *Share) Remove(name string) error

func (*Share) RemoveAll

func (fs *Share) RemoveAll(path string) error

RemoveAll removes path and any children it contains. It removes everything it can but returns the first error it encounters. If the path does not exist, RemoveAll returns nil (no error).

func (*Share) Rename

func (fs *Share) Rename(oldpath, newpath string) error

func (*Share) Stat

func (fs *Share) Stat(name string) (os.FileInfo, error)

func (*Share) Statfs

func (fs *Share) Statfs(name string) (FileFsInfo, error)
func (fs *Share) Symlink(target, linkpath string) error

Symlink mimics os.Symlink. This API should work on latest Windows and latest MacOS. However it may not work on Linux because Samba doesn't support reparse point well. Also there is a restriction on target pathname. Generally, a pathname begins with leading backslash (e.g `\dir\name`) can be interpreted as two ways. On windows, it is evaluated as a relative path, on other systems, it is evaluated as an absolute path. This implementation always assumes that format is absolute path. So, if you know the target server is Windows, you should avoid that format. If you want to use an absolute target path on windows, you can use // `C:\dir\name` format instead.

func (*Share) Truncate

func (fs *Share) Truncate(name string, size int64) error

func (*Share) Umount

func (fs *Share) Umount() error

Umount disconects the current SMB tree.

func (*Share) WithContext

func (fs *Share) WithContext(ctx context.Context) *Share

func (*Share) WriteFile

func (fs *Share) WriteFile(filename string, data []byte, perm os.FileMode) error

type TransportError

type TransportError struct {
	Err error
}

TransportError represents a error come from net.Conn layer.

func (*TransportError) Error

func (err *TransportError) Error() string

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL