* add burn operation to the client, server, and protocol this provides a method for removing files from the server remotely without needing to restart the server example use case for this is if your server is publicly accessible but you don't expose SSH publicly and you're transferring data between two cloud servers and don't want the data to be stored on the server any longer than it has to be * updating documentation
243 lines
5.1 KiB
Go
243 lines
5.1 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"encoding/gob"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"os"
|
|
"time"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
"github.com/dustin/go-humanize"
|
|
"github.com/tardisx/netgiv/secure"
|
|
)
|
|
|
|
type Client struct {
|
|
address string
|
|
port int
|
|
list bool
|
|
send bool
|
|
burnNum int
|
|
receiveNum int
|
|
authToken string
|
|
}
|
|
|
|
func (c *Client) Connect() error {
|
|
address := fmt.Sprintf("%s:%d", c.address, c.port)
|
|
|
|
d := net.Dialer{Timeout: 5 * time.Second}
|
|
|
|
conn, err := d.Dial("tcp", address)
|
|
if err != nil {
|
|
return fmt.Errorf("problem connecting to server, is it running?: %v", err)
|
|
}
|
|
defer conn.Close()
|
|
|
|
log.Debugf("established connection on %s", address)
|
|
|
|
tcpConn, ok := conn.(*net.TCPConn)
|
|
if !ok {
|
|
log.Fatal("could not assert")
|
|
}
|
|
|
|
sharedKey := secure.Handshake(tcpConn)
|
|
secureConnection := secure.SecureConnection{Conn: conn, SharedKey: sharedKey, Buffer: &bytes.Buffer{}}
|
|
|
|
enc := gob.NewEncoder(&secureConnection)
|
|
dec := gob.NewDecoder(&secureConnection)
|
|
|
|
switch {
|
|
case c.list:
|
|
log.Debugf("requesting file list")
|
|
|
|
err := c.connectToServer(secure.OperationTypeList, enc, dec)
|
|
if err != nil {
|
|
return fmt.Errorf("could not connect and auth: %v", err)
|
|
}
|
|
|
|
// now we expect to get stuff back until we don't
|
|
numFiles := 0
|
|
for {
|
|
listPacket := secure.PacketListData{}
|
|
err := dec.Decode(&listPacket)
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
fmt.Printf("%d: %s (%s) - %s\n", listPacket.Id, listPacket.Kind, humanize.Bytes(uint64(listPacket.FileSize)), listPacket.Timestamp)
|
|
numFiles++
|
|
}
|
|
fmt.Printf("total: %d files\n", numFiles)
|
|
conn.Close()
|
|
log.Debugf("done listing")
|
|
case c.receiveNum >= 0:
|
|
log.Debugf("receiving file %d", c.receiveNum)
|
|
|
|
err := c.connectToServer(secure.OperationTypeReceive, enc, dec)
|
|
if err != nil {
|
|
return fmt.Errorf("could not connect and auth: %v", err)
|
|
}
|
|
|
|
req := secure.PacketReceiveDataStartRequest{
|
|
Id: uint32(c.receiveNum),
|
|
}
|
|
err = enc.Encode(req)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
// expect a response telling us if we can go ahead
|
|
res := secure.PacketReceiveDataStartResponse{}
|
|
err = dec.Decode(&res)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
switch res.Status {
|
|
case secure.ReceiveDataStartResponseOK:
|
|
for {
|
|
res := secure.PacketReceiveDataNext{}
|
|
err = dec.Decode(&res)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
os.Stdout.Write(res.Data[:res.Size])
|
|
if res.Last {
|
|
break
|
|
}
|
|
}
|
|
log.Debugf("finished")
|
|
case secure.ReceiveDataStartResponseNotFound:
|
|
log.Error("ngf not found")
|
|
default:
|
|
panic("unknown status")
|
|
}
|
|
|
|
conn.Close()
|
|
case c.send:
|
|
// send mode
|
|
|
|
err := c.connectToServer(secure.OperationTypeSend, enc, dec)
|
|
if err != nil {
|
|
return fmt.Errorf("could not connect and auth: %v", err)
|
|
}
|
|
|
|
data := secure.PacketSendDataStart{
|
|
Filename: "",
|
|
TotalSize: 0,
|
|
}
|
|
err = enc.Encode(data)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
nBytes, nChunks := int64(0), int64(0)
|
|
reader := bufio.NewReader(os.Stdin)
|
|
buf := make([]byte, 0, 1024)
|
|
|
|
for {
|
|
n, err := reader.Read(buf[:cap(buf)])
|
|
|
|
buf = buf[:n]
|
|
|
|
if n == 0 {
|
|
if err == nil {
|
|
continue
|
|
}
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
log.Fatal(err)
|
|
}
|
|
nChunks++
|
|
nBytes += int64(len(buf))
|
|
|
|
send := secure.PacketSendDataNext{
|
|
Size: 5000,
|
|
Data: buf,
|
|
}
|
|
err = enc.Encode(send)
|
|
// time.Sleep(time.Second)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
log.Debugf("Sent %s in %d chunks", humanize.Bytes(uint64(nBytes)), nChunks)
|
|
|
|
conn.Close()
|
|
case c.burnNum >= 0:
|
|
log.Debugf("burning file %d", c.burnNum)
|
|
|
|
err := c.connectToServer(secure.OperationTypeBurn, enc, dec)
|
|
if err != nil {
|
|
return fmt.Errorf("could not connect and auth: %v", err)
|
|
}
|
|
|
|
req := secure.PacketBurnRequest{
|
|
Id: uint32(c.burnNum),
|
|
}
|
|
err = enc.Encode(req)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
// expect a response telling us if we can go ahead
|
|
res := secure.PacketBurnResponse{}
|
|
err = dec.Decode(&res)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
switch res.Status {
|
|
case secure.BurnResponseOK:
|
|
log.Debugf("finished")
|
|
case secure.BurnResponseNotFound:
|
|
log.Error("ngf not found")
|
|
default:
|
|
panic("unknown status")
|
|
}
|
|
|
|
conn.Close()
|
|
default:
|
|
panic("no client mode set")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Client) connectToServer(op secure.OperationTypeEnum, enc *gob.Encoder, dec *gob.Decoder) error {
|
|
// list mode
|
|
startPacket := secure.PacketStartRequest{
|
|
OperationType: op,
|
|
ClientName: "",
|
|
ProtocolVersion: ProtocolVersion,
|
|
AuthToken: c.authToken,
|
|
}
|
|
err := enc.Encode(startPacket)
|
|
if err != nil {
|
|
return fmt.Errorf("could not send start packet: %v", err)
|
|
}
|
|
|
|
// check the response is ok
|
|
response := secure.PacketStartResponse{}
|
|
err = dec.Decode(&response)
|
|
if err != nil {
|
|
return fmt.Errorf("could not receive start packet response: %v", err)
|
|
}
|
|
|
|
if response.Response == secure.PacketStartResponseEnumWrongProtocol {
|
|
log.Print("wrong protocol version")
|
|
return errors.New("protocol version mismatch")
|
|
|
|
}
|
|
if response.Response == secure.PacketStartResponseEnumBadAuthToken {
|
|
log.Print("bad authtoken")
|
|
return errors.New("bad authtoken")
|
|
}
|
|
return nil
|
|
}
|