netgiv/server.go

395 lines
8.7 KiB
Go
Raw Permalink Normal View History

package main
import (
"bytes"
"encoding/gob"
"fmt"
"io"
"net"
"os"
"os/signal"
2022-01-14 23:30:14 +10:30
"sync/atomic"
"time"
"unicode/utf8"
log "github.com/sirupsen/logrus"
"github.com/h2non/filetype"
"github.com/tardisx/netgiv/secure"
)
type Server struct {
port int
authToken string
}
// An NGF is a Netgiv File
type NGF struct {
2022-01-14 23:30:14 +10:30
Id uint32
StorePath string
Filename string // could be empty string if we were not supplied with one
Kind string //
Size uint64 // file size
Timestamp time.Time
}
2025-04-25 13:16:11 +09:30
func (ngf NGF) String() string {
return fmt.Sprintf("id: %d, stored: %s, size: %d, kind: %s", ngf.Id, ngf.StorePath, ngf.Size, ngf.Kind)
}
2022-01-14 23:30:14 +10:30
var ngfs []NGF
var globalId uint32
func (s *Server) Run() {
2025-04-25 13:20:43 +09:30
log.Info(versionInfo(false))
log.Infof("starting server on :%d", s.port)
2022-01-13 22:20:35 +10:30
address := fmt.Sprintf(":%d", s.port)
networkAddress, _ := net.ResolveTCPAddr("tcp", address)
listener, err := net.ListenTCP("tcp", networkAddress)
if err != nil {
log.Fatalf("error creating listener: %v", err)
}
2022-01-14 23:30:14 +10:30
ngfs = make([]NGF, 0)
go func() {
sigchan := make(chan os.Signal, 1)
signal.Notify(sigchan, os.Interrupt)
<-sigchan
for _, ngf := range ngfs {
log.Printf("removing file: %s", ngf.StorePath)
err := os.Remove(ngf.StorePath)
if err != nil {
log.Errorf("could not remove %s: %v", ngf.StorePath, err)
}
}
os.Exit(0)
}()
// start main program tasks
for {
conn, err := listener.AcceptTCP()
if err != nil {
fmt.Print(err)
}
go s.handleConnection(conn)
}
}
func (s *Server) handleConnection(conn *net.TCPConn) {
defer conn.Close()
_ = conn.SetDeadline(time.Now().Add(time.Second * 5))
sharedKey := secure.Handshake(conn)
secureConnection := secure.SecureConnection{Conn: conn, SharedKey: sharedKey, Buffer: &bytes.Buffer{}}
gob.Register(secure.PacketStartRequest{})
gob.Register(secure.PacketSendDataStart{})
dec := gob.NewDecoder(&secureConnection)
2022-01-14 23:30:14 +10:30
enc := gob.NewEncoder(&secureConnection)
2022-01-13 22:20:35 +10:30
// Get the start packet
start := secure.PacketStartRequest{}
2022-01-13 22:20:35 +10:30
err := dec.Decode(&start)
if err == io.EOF {
2022-01-16 20:35:35 +10:30
log.Errorf("connection has been closed prematurely")
2022-01-13 22:20:35 +10:30
return
}
2022-01-13 22:20:35 +10:30
if err != nil {
2022-01-16 20:35:35 +10:30
log.Errorf("error while expecting PacketStart: %v", err)
2022-01-13 22:20:35 +10:30
return
}
2022-01-26 10:49:23 +10:30
// tell the client if the connection is ok.
startResponse := secure.PacketStartResponse{}
2022-01-26 10:49:23 +10:30
if start.ProtocolVersion != ProtocolVersion {
2022-01-16 20:35:35 +10:30
log.Errorf("bad protocol version")
startResponse.Response = secure.PacketStartResponseEnumWrongProtocol
_ = enc.Encode(startResponse)
return
}
if start.AuthToken != s.authToken {
2022-01-16 20:35:35 +10:30
log.Errorf("bad authtoken")
startResponse.Response = secure.PacketStartResponseEnumBadAuthToken
_ = enc.Encode(startResponse)
return
}
// otherwise we are good to continue, tell the client that
startResponse.Response = secure.PacketStartResponseEnumOK
_ = enc.Encode(startResponse)
_ = conn.SetDeadline(time.Now().Add(time.Second * 5))
switch start.OperationType {
case secure.OperationTypeSend:
2022-01-16 20:35:35 +10:30
log.Debugf("file incoming")
2022-01-14 23:30:14 +10:30
2022-01-13 22:20:35 +10:30
sendStart := secure.PacketSendDataStart{}
2022-01-13 22:20:35 +10:30
err = dec.Decode(&sendStart)
if err != nil {
2022-01-16 20:35:35 +10:30
log.Errorf("error - expecting PacketSendDataStart: %v", err)
return
}
2022-01-13 22:20:35 +10:30
file, err := os.CreateTemp("", "netgiv_")
2022-01-14 23:30:14 +10:30
if err != nil {
log.Fatalf("can't open tempfile: %v", err)
}
defer file.Close()
ngf := NGF{
StorePath: file.Name(),
Filename: sendStart.Filename,
Kind: "",
Size: 0,
2022-01-14 23:30:14 +10:30
Id: atomic.AddUint32(&globalId, 1),
Timestamp: time.Now(),
}
if err != nil {
2022-01-16 20:35:35 +10:30
log.Errorf("got error with temp file: %v", err)
2022-01-13 22:20:35 +10:30
return
}
2022-01-13 22:20:35 +10:30
sendData := secure.PacketSendDataNext{}
determinedKind := false
2022-01-13 22:20:35 +10:30
for {
_ = conn.SetDeadline(time.Now().Add(time.Second * 5))
2022-01-13 22:20:35 +10:30
err = dec.Decode(&sendData)
if err == io.EOF {
break
}
if err != nil {
2022-01-16 20:35:35 +10:30
log.Errorf("error while expecting PacketSendDataNext: %s", err)
2022-01-13 22:20:35 +10:30
return
}
// filetype.Match needs a few hundred bytes - I guess there is a chance
// we don't have enough in the very first packet? This might need rework.
if !determinedKind {
kind, _ := filetype.Match(sendData.Data)
if kind.MIME.Value == "" {
// this is pretty fragile. If our chunk boundary happens in the
// middle of an actual UTF-8 character, we will fail this test.
// However it's good for small chunks of text which fit in a
// single chunk, which I suspect to be a common use case.
if utf8.ValidString(string(sendData.Data)) {
ngf.Kind = "UTF-8 text"
}
} else {
ngf.Kind = kind.MIME.Value
}
determinedKind = true
}
_, _ = file.Write(sendData.Data)
2022-01-13 22:20:35 +10:30
}
info, err := file.Stat()
if err != nil {
2022-01-16 20:35:35 +10:30
log.Errorf("couldn't stat file %s", err)
return
}
ngf.Size = uint64(info.Size())
file.Close()
2022-01-14 23:30:14 +10:30
ngfs = append(ngfs, ngf)
2022-01-17 08:28:08 +10:30
log.Printf("done receiving file: %v", ngf)
2022-01-14 23:30:14 +10:30
2022-01-13 22:20:35 +10:30
return
case secure.OperationTypeReceive:
2022-01-15 14:04:13 +10:30
log.Printf("client requesting file receive")
// wait for them to send the request
req := secure.PacketReceiveDataStartRequest{}
err := dec.Decode(&req)
if err != nil {
log.Errorf("error expecting PacketReceiveDataStartRequest: %v", err)
2022-01-15 14:04:13 +10:30
return
}
2022-01-17 08:28:08 +10:30
log.Debugf("The asked for %v", req)
// do we have this ngf by id?
2022-01-17 08:28:08 +10:30
var requestedNGF NGF
if len(ngfs) > 0 {
if req.Id == 0 {
// they want the most recent one
2022-01-17 08:28:08 +10:30
requestedNGF = ngfs[len(ngfs)-1]
} else {
for _, ngf := range ngfs {
if ngf.Id == req.Id {
2022-01-17 08:28:08 +10:30
requestedNGF = ngf
}
}
}
}
2022-01-17 08:28:08 +10:30
log.Debugf("going to deliver %v", requestedNGF)
if requestedNGF.Id == 0 {
// not found
2022-01-16 20:35:35 +10:30
log.Errorf("user requested %d, not found", req.Id)
res := secure.PacketReceiveDataStartResponse{
Status: secure.ReceiveDataStartResponseNotFound,
}
err = enc.Encode(res)
if err != nil {
log.Errorf("could not send NotFound: %v", err)
}
return
}
2022-01-15 14:04:13 +10:30
res := secure.PacketReceiveDataStartResponse{
Status: secure.ReceiveDataStartResponseOK,
Filename: requestedNGF.Filename,
Kind: requestedNGF.Kind,
TotalSize: uint32(requestedNGF.Size),
2022-01-15 14:04:13 +10:30
}
err = enc.Encode(res)
if err != nil {
2022-01-16 20:35:35 +10:30
log.Errorf("error sending PacketReceiveDataStartResponse: %v", err)
2022-01-15 14:04:13 +10:30
return
}
// now just start sending the file in batches
buf := make([]byte, 2048)
filename := requestedNGF.StorePath
2022-01-16 20:35:35 +10:30
log.Debugf("opening %s", filename)
2022-01-15 14:04:13 +10:30
f, err := os.Open(filename)
if err != nil {
2022-01-16 20:35:35 +10:30
log.Errorf("could not find file %s: %v", filename, err)
return
}
2022-01-15 14:04:13 +10:30
for {
n, err := f.Read(buf)
eof := false
if err != nil && err != io.EOF {
2022-01-16 20:35:35 +10:30
log.Errorf("error reading data: %v", err)
2022-01-15 14:04:13 +10:30
break
}
if err == io.EOF {
eof = true
}
chunk := secure.PacketReceiveDataNext{
Size: uint16(n),
Data: buf[:n],
Last: eof,
}
err = enc.Encode(chunk)
if err != nil {
2022-01-16 20:35:35 +10:30
log.Errorf("error sending chunk: %v", err)
2022-01-15 14:04:13 +10:30
}
if eof {
break
}
}
log.Printf("sending done")
return
case secure.OperationTypeList:
2022-01-16 20:35:35 +10:30
log.Debugf("client requesting file list")
2022-01-14 23:30:14 +10:30
for _, ngf := range ngfs {
p := secure.PacketListData{}
p.FileSize = uint32(ngf.Size)
p.Kind = ngf.Kind
p.Id = ngf.Id
p.Filename = ngf.Filename
2022-01-26 08:49:54 +10:30
p.Timestamp = ngf.Timestamp
_ = enc.Encode(p)
2022-01-14 23:30:14 +10:30
}
2022-01-16 20:35:35 +10:30
log.Debugf("done sending list, closing connection")
2022-01-14 23:30:14 +10:30
return
case secure.OperationTypeBurn:
log.Debugf("client requesting burn")
// wait for them to send the request
req := secure.PacketBurnRequest{}
err := dec.Decode(&req)
if err != nil {
log.Errorf("error expecting PacketBurnRequest: %v", err)
return
}
log.Debugf("The client asked for %v to be burned", req)
// do we have this ngf by id?
var requestedNGF NGF
if len(ngfs) > 0 {
if req.Id == 0 {
// they want the most recent one
requestedNGF = ngfs[len(ngfs)-1]
} else {
for _, ngf := range ngfs {
if ngf.Id == req.Id {
requestedNGF = ngf
}
}
}
}
log.Debugf("going to burn %v", requestedNGF)
2022-01-14 23:30:14 +10:30
if requestedNGF.Id == 0 {
// not found
log.Errorf("user requested burning %d, not found", req.Id)
res := secure.PacketBurnResponse{
Status: secure.BurnResponseNotFound,
}
err = enc.Encode(res)
if err != nil {
log.Errorf("could not send NotFound: %v", err)
}
return
}
// remove the file
err = os.Remove(requestedNGF.StorePath)
if err != nil {
log.Errorf("could not remove file %s: %v", requestedNGF.StorePath, err)
return
}
// remove the ngf from the list
for i, ngf := range ngfs {
if ngf.Id == requestedNGF.Id {
ngfs = append(ngfs[:i], ngfs[i+1:]...)
break
}
}
res := secure.PacketBurnResponse{
Status: secure.BurnResponseOK,
}
err = enc.Encode(res)
if err != nil {
log.Errorf("error sending PacketBurnResponse: %v", err)
return
}
log.Printf("burn complete")
return
default:
2022-01-16 20:35:35 +10:30
log.Errorf("bad operation")
2022-01-13 22:20:35 +10:30
return
}
}