update
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -6,3 +6,6 @@ logs/
|
||||
|
||||
.env
|
||||
.json
|
||||
|
||||
|
||||
rns-announce
|
||||
@@ -1,266 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/Sudo-Ivan/reticulum-go/internal/config"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/common"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/destination"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/identity"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/link"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/packet"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/resource"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/transport"
|
||||
)
|
||||
|
||||
const (
|
||||
APP_NAME = "example_utilities"
|
||||
APP_ASPECT = "filetransfer"
|
||||
)
|
||||
|
||||
var (
|
||||
configPath = flag.String("config", "", "Path to config file")
|
||||
servePath = flag.String("serve", "", "Directory to serve files from")
|
||||
)
|
||||
|
||||
type FileServer struct {
|
||||
config *common.ReticulumConfig
|
||||
transport *transport.Transport
|
||||
interfaces []common.NetworkInterface
|
||||
identity *identity.Identity
|
||||
servePath string
|
||||
}
|
||||
|
||||
func NewFileServer(cfg *common.ReticulumConfig, servePath string) (*FileServer, error) {
|
||||
if cfg == nil {
|
||||
var err error
|
||||
cfg, err = config.InitConfig()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize config: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
t, err := transport.NewTransport(cfg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize transport: %v", err)
|
||||
}
|
||||
|
||||
id, err := identity.New()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create identity: %v", err)
|
||||
}
|
||||
|
||||
return &FileServer{
|
||||
config: cfg,
|
||||
transport: t,
|
||||
interfaces: make([]common.NetworkInterface, 0),
|
||||
identity: id,
|
||||
servePath: servePath,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *FileServer) OnLinkEstablished(l *link.Link) {
|
||||
s.handleLinkEstablished(l)
|
||||
}
|
||||
|
||||
func (s *FileServer) Start() error {
|
||||
dest, err := destination.New(
|
||||
s.identity,
|
||||
destination.OUT,
|
||||
destination.SINGLE,
|
||||
APP_NAME,
|
||||
APP_ASPECT,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create destination: %v", err)
|
||||
}
|
||||
|
||||
callback := func(l interface{}) {
|
||||
if link, ok := l.(*link.Link); ok {
|
||||
s.OnLinkEstablished(link)
|
||||
}
|
||||
}
|
||||
|
||||
dest.SetLinkEstablishedCallback(callback)
|
||||
|
||||
log.Printf("File server started. Server hash: %s", s.identity.Hex())
|
||||
log.Printf("Serving directory: %s", s.servePath)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FileServer) handleLinkEstablished(l *link.Link) {
|
||||
log.Printf("Client connected")
|
||||
|
||||
l.SetPacketCallback(func(data []byte, p *packet.Packet) {
|
||||
s.handlePacket(data, l)
|
||||
})
|
||||
|
||||
l.SetResourceCallback(func(r interface{}) bool {
|
||||
if res, ok := r.(*resource.Resource); ok {
|
||||
return s.handleResource(res)
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
func (s *FileServer) handlePacket(data []byte, l *link.Link) {
|
||||
if string(data) == "LIST" {
|
||||
files, err := s.getFileList()
|
||||
if err != nil {
|
||||
log.Printf("Error getting file list: %v", err)
|
||||
l.Teardown()
|
||||
return
|
||||
}
|
||||
|
||||
if err := l.SendPacket(files); err != nil {
|
||||
log.Printf("Error sending file list: %v", err)
|
||||
l.Teardown()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *FileServer) handleResource(r *resource.Resource) bool {
|
||||
filename := filepath.Join(s.servePath, r.GetName())
|
||||
file, err := os.Create(filename)
|
||||
if err != nil {
|
||||
log.Printf("Failed to create file: %v", err)
|
||||
return false
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
written, err := io.Copy(file, r)
|
||||
if err != nil {
|
||||
log.Printf("Failed to write file: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
log.Printf("Received file: %s (%d bytes)", filename, written)
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *FileServer) getFileList() ([]byte, error) {
|
||||
files, err := os.ReadDir(s.servePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var fileList []string
|
||||
for _, file := range files {
|
||||
if !file.IsDir() {
|
||||
fileList = append(fileList, file.Name())
|
||||
}
|
||||
}
|
||||
|
||||
return []byte(fmt.Sprintf("%v", fileList)), nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
if *servePath == "" {
|
||||
log.Fatal("Please specify a directory to serve with -serve")
|
||||
}
|
||||
|
||||
var cfg *common.ReticulumConfig
|
||||
var err error
|
||||
|
||||
if *configPath == "" {
|
||||
cfg, err = config.InitConfig()
|
||||
} else {
|
||||
cfg, err = config.LoadConfig(*configPath)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to load config: %v", err)
|
||||
}
|
||||
|
||||
server, err := NewFileServer(cfg, *servePath)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create server: %v", err)
|
||||
}
|
||||
|
||||
if err := server.Start(); err != nil {
|
||||
log.Fatalf("Failed to start server: %v", err)
|
||||
}
|
||||
|
||||
// Start watching the directory for changes
|
||||
go server.watchDirectory()
|
||||
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-sigChan
|
||||
}
|
||||
|
||||
func (s *FileServer) watchDirectory() {
|
||||
for {
|
||||
time.Sleep(1 * time.Second)
|
||||
files, err := os.ReadDir(s.servePath)
|
||||
if err != nil {
|
||||
log.Printf("Error reading directory: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if !file.IsDir() {
|
||||
// Try to send file to connected peers
|
||||
filePath := filepath.Join(s.servePath, file.Name())
|
||||
if err := s.sendFile(filePath); err != nil {
|
||||
log.Printf("Error sending file %s: %v", file.Name(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *FileServer) sendFile(filePath string) error {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open file: %v", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// Create a destination for the file transfer
|
||||
dest, err := destination.New(
|
||||
s.identity,
|
||||
destination.OUT,
|
||||
destination.SINGLE,
|
||||
APP_NAME,
|
||||
APP_ASPECT,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create destination: %v", err)
|
||||
}
|
||||
|
||||
// Set up link for file transfer
|
||||
callback := func(l interface{}) {
|
||||
if link, ok := l.(*link.Link); ok {
|
||||
// Create a new resource with auto-compression enabled
|
||||
res, err := resource.New(file, true)
|
||||
if err != nil {
|
||||
log.Printf("Error creating resource: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// The filename is automatically set from the file handle
|
||||
// in resource.New when using an io.ReadWriteSeeker
|
||||
|
||||
// Send the resource through the link
|
||||
if err := link.SendResource(res); err != nil {
|
||||
log.Printf("Error sending resource: %v", err)
|
||||
return
|
||||
}
|
||||
log.Printf("File %s sent successfully", filepath.Base(filePath))
|
||||
}
|
||||
}
|
||||
|
||||
dest.SetLinkEstablishedCallback(callback)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,499 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/binary"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/Sudo-Ivan/reticulum-go/internal/config"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/announce"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/common"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/destination"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/identity"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/interfaces"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/link"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/packet"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/transport"
|
||||
)
|
||||
|
||||
var (
|
||||
configPath = flag.String("config", "", "Path to config file")
|
||||
targetHash = flag.String("target", "", "Target destination hash")
|
||||
generateIdentity = flag.Bool("generate-identity", false, "Generate a new identity and print its hash")
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
config *common.ReticulumConfig
|
||||
transport *transport.Transport
|
||||
interfaces []common.NetworkInterface
|
||||
identity *identity.Identity
|
||||
}
|
||||
|
||||
func NewClient(cfg *common.ReticulumConfig) (*Client, error) {
|
||||
if cfg == nil {
|
||||
var err error
|
||||
cfg, err = config.InitConfig()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize config: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
t, err := transport.NewTransport(cfg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize transport: %v", err)
|
||||
}
|
||||
|
||||
id, err := identity.New()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create identity: %v", err)
|
||||
}
|
||||
|
||||
return &Client{
|
||||
config: cfg,
|
||||
transport: t,
|
||||
interfaces: make([]common.NetworkInterface, 0),
|
||||
identity: id,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) Start() error {
|
||||
log.Printf("Starting Reticulum client...")
|
||||
log.Printf("Configuration: %+v", c.config)
|
||||
|
||||
// Initialize transport
|
||||
t, err := transport.NewTransport(c.config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize transport: %v", err)
|
||||
}
|
||||
c.transport = t
|
||||
log.Printf("Transport initialized")
|
||||
|
||||
log.Printf("Initializing network interfaces...")
|
||||
for name, ifaceConfig := range c.config.Interfaces {
|
||||
if !ifaceConfig.Enabled {
|
||||
log.Printf("Skipping disabled interface %s", name)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("Configuring interface %s (%s)", name, ifaceConfig.Type)
|
||||
var iface common.NetworkInterface
|
||||
|
||||
switch ifaceConfig.Type {
|
||||
case "TCPClientInterface":
|
||||
log.Printf("Connecting to %s:%d via TCP...", ifaceConfig.TargetHost, ifaceConfig.TargetPort)
|
||||
client, err := interfaces.NewTCPClient(
|
||||
name,
|
||||
ifaceConfig.TargetHost,
|
||||
ifaceConfig.TargetPort,
|
||||
ifaceConfig.KISSFraming,
|
||||
ifaceConfig.I2PTunneled,
|
||||
ifaceConfig.Enabled,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create TCP interface %s: %v", name, err)
|
||||
}
|
||||
|
||||
if err := client.Start(); err != nil {
|
||||
return fmt.Errorf("failed to start TCP interface %s: %v", name, err)
|
||||
}
|
||||
|
||||
iface = client
|
||||
log.Printf("Successfully connected to %s:%d", ifaceConfig.TargetHost, ifaceConfig.TargetPort)
|
||||
|
||||
case "UDPInterface":
|
||||
addr := fmt.Sprintf("%s:%d", ifaceConfig.Address, ifaceConfig.Port)
|
||||
target := fmt.Sprintf("%s:%d", ifaceConfig.TargetHost, ifaceConfig.TargetPort)
|
||||
log.Printf("Starting UDP interface on %s...", addr)
|
||||
udp, err := interfaces.NewUDPInterface(
|
||||
name,
|
||||
addr,
|
||||
target,
|
||||
ifaceConfig.Enabled,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create UDP interface %s: %v", name, err)
|
||||
}
|
||||
|
||||
if err := udp.Start(); err != nil {
|
||||
return fmt.Errorf("failed to start UDP interface %s: %v", name, err)
|
||||
}
|
||||
|
||||
iface = udp
|
||||
log.Printf("UDP interface listening on %s", addr)
|
||||
}
|
||||
|
||||
if iface != nil {
|
||||
// Set packet callback
|
||||
iface.SetPacketCallback(c.transport.HandlePacket)
|
||||
c.interfaces = append(c.interfaces, iface)
|
||||
log.Printf("Created and started interface %s (type=%v, enabled=%v)",
|
||||
name, iface.GetType(), iface.IsEnabled())
|
||||
}
|
||||
}
|
||||
|
||||
// Register announce handler with explicit type
|
||||
var handler transport.AnnounceHandler = &ClientAnnounceHandler{client: c}
|
||||
c.transport.RegisterAnnounceHandler(handler)
|
||||
|
||||
// Send initial announce
|
||||
log.Printf("Sending initial announce...")
|
||||
if err := c.sendAnnounce(); err != nil {
|
||||
log.Printf("Warning: Failed to send initial announce: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) handlePacket(data []byte, p *packet.Packet) {
|
||||
if len(data) < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
header := data[0]
|
||||
packetType := header & 0x03 // Extract packet type from header
|
||||
|
||||
switch packetType {
|
||||
case announce.PACKET_TYPE_ANNOUNCE:
|
||||
log.Printf("Received announce packet:")
|
||||
log.Printf(" Raw data: %x", data)
|
||||
|
||||
// Create announce instance
|
||||
a, err := announce.New(c.identity, []byte("RNS.Go.Client"), false)
|
||||
if err != nil {
|
||||
log.Printf("Failed to create announce handler: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle the announce
|
||||
if err := a.HandleAnnounce(data[1:]); err != nil {
|
||||
log.Printf("Failed to handle announce: %v", err)
|
||||
}
|
||||
|
||||
default:
|
||||
c.transport.HandlePacket(data, p)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) handleAnnounce(data []byte) {
|
||||
if len(data) < 42 {
|
||||
log.Printf("Received malformed announce packet (too short)")
|
||||
return
|
||||
}
|
||||
|
||||
destHash := data[:32]
|
||||
timestamp := binary.BigEndian.Uint64(data[32:40])
|
||||
hops := data[40]
|
||||
flags := data[41]
|
||||
|
||||
log.Printf("Received announce from %x", destHash)
|
||||
log.Printf(" Timestamp: %d", timestamp)
|
||||
log.Printf(" Hops: %d", hops)
|
||||
log.Printf(" Flags: %x", flags)
|
||||
|
||||
// Extract public key if present (after flags)
|
||||
if len(data) > 42 {
|
||||
pubKeyLen := 32 // Ed25519 public key length
|
||||
pubKey := data[42 : 42+pubKeyLen]
|
||||
log.Printf(" Public Key: %x", pubKey)
|
||||
|
||||
// Extract app data if present
|
||||
var appData []byte
|
||||
if len(data) > 42+pubKeyLen+2 {
|
||||
dataLen := binary.BigEndian.Uint16(data[42+pubKeyLen : 42+pubKeyLen+2])
|
||||
if len(data) >= 42+pubKeyLen+2+int(dataLen) {
|
||||
appData = data[42+pubKeyLen+2 : 42+pubKeyLen+2+int(dataLen)]
|
||||
log.Printf(" App Data: %s", string(appData))
|
||||
}
|
||||
}
|
||||
|
||||
// Store the identity for future use with all required parameters
|
||||
if !identity.ValidateAnnounce(data, destHash, pubKey, data[len(data)-64:], appData) {
|
||||
log.Printf("Failed to validate announce")
|
||||
return
|
||||
}
|
||||
log.Printf("Successfully validated and stored announce")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) sendAnnounce() error {
|
||||
// Create announce packet
|
||||
identityHash := c.identity.Hash()
|
||||
announceData := make([]byte, 0)
|
||||
|
||||
// Add header
|
||||
header := []byte{0x01, 0x00} // Announce packet type
|
||||
announceData = append(announceData, header...)
|
||||
|
||||
// Add destination hash
|
||||
announceData = append(announceData, identityHash...)
|
||||
|
||||
// Add context byte
|
||||
announceData = append(announceData, announce.ANNOUNCE_IDENTITY)
|
||||
|
||||
// Add public key
|
||||
announceData = append(announceData, c.identity.GetPublicKey()...)
|
||||
|
||||
// App data with length prefix
|
||||
appData := []byte("RNS.Go.Client")
|
||||
lenBytes := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(lenBytes, uint16(len(appData)))
|
||||
announceData = append(announceData, lenBytes...)
|
||||
announceData = append(announceData, appData...)
|
||||
|
||||
// Add signature
|
||||
signData := append(identityHash, c.identity.GetPublicKey()...)
|
||||
signData = append(signData, appData...)
|
||||
signature := c.identity.Sign(signData)
|
||||
announceData = append(announceData, signature...)
|
||||
|
||||
log.Printf("Sending announce:")
|
||||
log.Printf(" Identity Hash: %x", identityHash)
|
||||
log.Printf(" Packet Length: %d bytes", len(announceData))
|
||||
log.Printf(" Full Packet: %x", announceData)
|
||||
|
||||
sentCount := 0
|
||||
// Send on all interfaces
|
||||
for _, iface := range c.interfaces {
|
||||
log.Printf("Attempting to send on interface %s:", iface.GetName())
|
||||
log.Printf(" Type: %v", iface.GetType())
|
||||
log.Printf(" MTU: %d bytes", iface.GetMTU())
|
||||
log.Printf(" Status: enabled=%v", iface.IsEnabled())
|
||||
|
||||
if !iface.IsEnabled() {
|
||||
log.Printf(" Skipping disabled interface")
|
||||
continue
|
||||
}
|
||||
|
||||
if err := iface.Send(announceData, ""); err != nil {
|
||||
log.Printf(" Failed to send: %v", err)
|
||||
} else {
|
||||
log.Printf(" Successfully sent announce")
|
||||
sentCount++
|
||||
}
|
||||
}
|
||||
|
||||
if sentCount == 0 {
|
||||
return fmt.Errorf("no interfaces available to send announce")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) Stop() {
|
||||
for _, iface := range c.interfaces {
|
||||
iface.Detach()
|
||||
}
|
||||
c.transport.Close()
|
||||
}
|
||||
|
||||
func (c *Client) Connect(destHash []byte) error {
|
||||
// Recall server identity
|
||||
serverIdentity, err := identity.Recall(destHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create destination
|
||||
dest, err := destination.New(
|
||||
serverIdentity,
|
||||
destination.OUT,
|
||||
destination.SINGLE,
|
||||
"example_utilities",
|
||||
"identifyexample",
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create link with all required parameters
|
||||
link := link.NewLink(
|
||||
dest,
|
||||
c.transport, // Add the transport instance
|
||||
c.handleLinkEstablished,
|
||||
c.handleLinkClosed,
|
||||
)
|
||||
|
||||
// Set callbacks
|
||||
link.SetPacketCallback(c.handlePacket)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) handleLinkEstablished(l *link.Link) {
|
||||
log.Printf("Link established with server, identifying...")
|
||||
|
||||
// Identify to server
|
||||
if err := l.Identify(c.identity); err != nil {
|
||||
log.Printf("Failed to identify: %v", err)
|
||||
l.Teardown()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) handleLinkClosed(l *link.Link) {
|
||||
log.Printf("Link closed")
|
||||
}
|
||||
|
||||
type ClientAnnounceHandler struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
func (h *ClientAnnounceHandler) AspectFilter() []string {
|
||||
return []string{"RNS.Go.Client"}
|
||||
}
|
||||
|
||||
func (h *ClientAnnounceHandler) ReceivedAnnounce(destinationHash []byte, announcedIdentity interface{}, appData []byte) error {
|
||||
log.Printf("=== Received Announce Details ===")
|
||||
log.Printf("Destination Hash: %x", destinationHash)
|
||||
log.Printf("App Data: %s", string(appData))
|
||||
|
||||
// Type assert the identity
|
||||
if id, ok := announcedIdentity.(*identity.Identity); ok {
|
||||
log.Printf("Identity Public Key: %x", id.GetPublicKey())
|
||||
|
||||
// Create packet hash for storage
|
||||
packetHash := identity.TruncatedHash(append(destinationHash, id.GetPublicKey()...))
|
||||
log.Printf("Generated Packet Hash: %x", packetHash)
|
||||
|
||||
// Store the peer identity with all required parameters
|
||||
identity.Remember(packetHash, destinationHash, id.GetPublicKey(), appData)
|
||||
log.Printf("Identity stored successfully")
|
||||
log.Printf("===========================")
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Printf("Error: Invalid identity type")
|
||||
log.Printf("===========================")
|
||||
return fmt.Errorf("invalid identity type")
|
||||
}
|
||||
|
||||
func (h *ClientAnnounceHandler) ReceivePathResponses() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
log.Printf("Starting Reticulum Go client...")
|
||||
log.Printf("Config path: %s", *configPath)
|
||||
log.Printf("Target hash: %s", *targetHash)
|
||||
|
||||
var cfg *common.ReticulumConfig
|
||||
var err error
|
||||
|
||||
if *configPath == "" {
|
||||
log.Printf("No config path specified, using default configuration")
|
||||
cfg, err = config.InitConfig()
|
||||
} else {
|
||||
log.Printf("Loading configuration from: %s", *configPath)
|
||||
cfg, err = config.LoadConfig(*configPath)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to load config: %v", err)
|
||||
}
|
||||
log.Printf("Configuration loaded successfully")
|
||||
|
||||
if *generateIdentity {
|
||||
log.Printf("Generating new identity...")
|
||||
id, err := identity.New()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to generate identity: %v", err)
|
||||
}
|
||||
fmt.Printf("Identity hash: %s\n", id.Hex())
|
||||
return
|
||||
}
|
||||
|
||||
client, err := NewClient(cfg)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create client: %v", err)
|
||||
}
|
||||
defer client.Stop()
|
||||
|
||||
if err := client.Start(); err != nil {
|
||||
log.Fatalf("Failed to start client: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("Client running, press Ctrl+C to exit")
|
||||
|
||||
// If target is specified, start interactive mode
|
||||
if *targetHash != "" {
|
||||
targetBytes, err := identity.HashFromString(*targetHash)
|
||||
if err != nil {
|
||||
log.Fatalf("Invalid target hash: %v", err)
|
||||
}
|
||||
link, err := client.transport.GetLink(targetBytes)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to get link: %v", err)
|
||||
}
|
||||
log.Printf("Starting interactive mode...")
|
||||
interactiveLoop(link)
|
||||
return
|
||||
}
|
||||
|
||||
// Wait for interrupt if no target specified
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-sigChan
|
||||
log.Printf("Received interrupt signal, shutting down...")
|
||||
}
|
||||
|
||||
func interactiveLoop(link *transport.Link) {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
connected := make(chan struct{})
|
||||
disconnected := make(chan struct{})
|
||||
|
||||
// Set up connection status handlers
|
||||
link.OnConnected(func() {
|
||||
connected <- struct{}{}
|
||||
})
|
||||
|
||||
link.OnDisconnected(func() {
|
||||
disconnected <- struct{}{}
|
||||
})
|
||||
|
||||
// Wait for initial connection
|
||||
select {
|
||||
case <-connected:
|
||||
log.Println("Connected to target")
|
||||
case <-time.After(10 * time.Second):
|
||||
log.Fatal("Connection timeout")
|
||||
return
|
||||
}
|
||||
|
||||
// Start input loop
|
||||
for {
|
||||
select {
|
||||
case <-disconnected:
|
||||
log.Println("Connection lost")
|
||||
return
|
||||
default:
|
||||
fmt.Print("> ")
|
||||
input, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return
|
||||
}
|
||||
log.Printf("Error reading input: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
input = strings.TrimSpace(input)
|
||||
if input == "quit" || input == "exit" {
|
||||
return
|
||||
}
|
||||
|
||||
if err := link.Send([]byte(input)); err != nil {
|
||||
log.Printf("Failed to send: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
4
scripts/build.sh
Normal file
4
scripts/build.sh
Normal file
@@ -0,0 +1,4 @@
|
||||
go build -o bin/reticulum ./cmd/reticulum
|
||||
go build -o bin/rns-announce ./cmd/rns-announce
|
||||
|
||||
# Add other commands here
|
||||
@@ -1,117 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Function to show usage
|
||||
show_usage() {
|
||||
echo "Usage: $0 [--type TYPE]"
|
||||
echo " --type Type of client to run (default: client, options: client, ftp)"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Parse command line arguments
|
||||
CLIENT_TYPE="client"
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--type)
|
||||
CLIENT_TYPE="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
show_usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Validate client type
|
||||
if [[ "$CLIENT_TYPE" != "client" && "$CLIENT_TYPE" != "ftp" ]]; then
|
||||
echo "Error: Invalid client type. Must be 'client' or 'ftp'"
|
||||
show_usage
|
||||
fi
|
||||
|
||||
# Build the appropriate binaries
|
||||
echo "Building Reticulum binaries..."
|
||||
go build -o bin/reticulum ./cmd/reticulum
|
||||
|
||||
case $CLIENT_TYPE in
|
||||
"client")
|
||||
go build -o bin/reticulum-client ./cmd/client
|
||||
CLIENT_BIN="reticulum-client"
|
||||
;;
|
||||
"ftp")
|
||||
go build -o bin/reticulum-client-ftp ./cmd/client-ftp
|
||||
CLIENT_BIN="reticulum-client-ftp"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Check if build was successful
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Build failed!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create directories
|
||||
mkdir -p logs
|
||||
mkdir -p bin
|
||||
|
||||
# Start the Reticulum server first
|
||||
echo "Starting Reticulum server..."
|
||||
./bin/reticulum > logs/server.log 2>&1 &
|
||||
echo $! > logs/server.pid
|
||||
sleep 2 # Give server time to start
|
||||
|
||||
# Generate identities for both clients
|
||||
echo "Generating identities..."
|
||||
CLIENT1_HASH=$(./bin/"$CLIENT_BIN" -config configs/test-client1.toml -generate-identity 2>&1 | grep "Identity hash:" | cut -d' ' -f3)
|
||||
CLIENT2_HASH=$(./bin/"$CLIENT_BIN" -config configs/test-client2.toml -generate-identity 2>&1 | grep "Identity hash:" | cut -d' ' -f3)
|
||||
|
||||
echo "Client 1 Hash: $CLIENT1_HASH"
|
||||
echo "Client 2 Hash: $CLIENT2_HASH"
|
||||
|
||||
# Function to run client
|
||||
run_client() {
|
||||
local config=$1
|
||||
local target=$2
|
||||
local logfile=$3
|
||||
|
||||
case $CLIENT_TYPE in
|
||||
"client")
|
||||
echo "Starting regular client with config: $config targeting: $target"
|
||||
./bin/"$CLIENT_BIN" -config "$config" -target "$target" > "$logfile" 2>&1 &
|
||||
;;
|
||||
"ftp")
|
||||
echo "Starting FTP client with config: $config serving directory: $target"
|
||||
./bin/"$CLIENT_BIN" -config "$config" -serve "$target" > "$logfile" 2>&1 &
|
||||
;;
|
||||
esac
|
||||
|
||||
echo $! > "$logfile.pid"
|
||||
echo "Client started with PID: $(cat $logfile.pid)"
|
||||
}
|
||||
|
||||
# Run both clients with appropriate parameters
|
||||
case $CLIENT_TYPE in
|
||||
"client")
|
||||
run_client "configs/test-client1.toml" "$CLIENT2_HASH" "logs/client1.log"
|
||||
run_client "configs/test-client2.toml" "$CLIENT1_HASH" "logs/client2.log"
|
||||
;;
|
||||
"ftp")
|
||||
# Create shared directories for FTP clients
|
||||
mkdir -p ./shared/client1 ./shared/client2
|
||||
run_client "configs/test-client1.toml" "./shared/client1" "logs/client1.log" "$CLIENT2_HASH"
|
||||
run_client "configs/test-client2.toml" "./shared/client2" "logs/client2.log" "$CLIENT1_HASH"
|
||||
;;
|
||||
esac
|
||||
|
||||
echo
|
||||
echo "Both clients are running. To stop everything:"
|
||||
echo "kill \$(cat logs/*.pid)"
|
||||
echo
|
||||
echo "To view logs:"
|
||||
echo "tail -f logs/client1.log"
|
||||
echo "tail -f logs/client2.log"
|
||||
|
||||
if [ "$CLIENT_TYPE" = "ftp" ]; then
|
||||
echo
|
||||
echo "FTP shared directories:"
|
||||
echo "./shared/client1"
|
||||
echo "./shared/client2"
|
||||
fi
|
||||
Reference in New Issue
Block a user