diff --git a/cmd/wnode/main.go b/cmd/wnode/main.go index f9a1296879..5fb10d52a8 100644 --- a/cmd/wnode/main.go +++ b/cmd/wnode/main.go @@ -32,6 +32,10 @@ import ( "strings" "time" + "io/ioutil" + + "path/filepath" + "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/console" @@ -80,6 +84,7 @@ var ( asymmetricMode = flag.Bool("a", false, "use asymmetric encryption") testMode = flag.Bool("t", false, "use of predefined parameters for diagnostics") generateKey = flag.Bool("k", false, "generate and show the private key") + fileExMode = flag.Bool("x", false, "file exchange mode") argVerbosity = flag.Int("verbosity", int(log.LvlWarn), "log verbosity level") argTTL = flag.Uint("ttl", 30, "time-to-live for messages in seconds") @@ -87,12 +92,13 @@ var ( argPoW = flag.Float64("pow", whisper.DefaultMinimumPoW, "PoW for normal messages in float format (e.g. 2.7)") argServerPoW = flag.Float64("mspow", whisper.DefaultMinimumPoW, "PoW requirement for Mail Server request") - argIP = flag.String("ip", "", "IP address and port of this node (e.g. 127.0.0.1:30303)") - argPub = flag.String("pub", "", "public key for asymmetric encryption") - argDBPath = flag.String("dbpath", "", "path to the server's DB directory") - argIDFile = flag.String("idfile", "", "file name with node id (private key)") - argEnode = flag.String("boot", "", "bootstrap node you want to connect to (e.g. enode://e454......08d50@52.176.211.200:16428)") - argTopic = flag.String("topic", "", "topic in hexadecimal format (e.g. 70a4beef)") + argIP = flag.String("ip", "", "IP address and port of this node (e.g. 127.0.0.1:30303)") + argPub = flag.String("pub", "", "public key for asymmetric encryption") + argDBPath = flag.String("dbpath", "", "path to the server's DB directory") + argIDFile = flag.String("idfile", "", "file name with node id (private key)") + argEnode = flag.String("boot", "", "bootstrap node you want to connect to (e.g. enode://e454......08d50@52.176.211.200:16428)") + argTopic = flag.String("topic", "", "topic in hexadecimal format (e.g. 70a4beef)") + argSaveDir = flag.String("savedir", "", "directory where incoming messages will be saved as files") ) func main() { @@ -134,6 +140,14 @@ func processArgs() { } } + if len(*argSaveDir) > 0 { + if _, err := os.Stat(*argSaveDir); os.IsNotExist(err) { + utils.Fatalf("Download directory '%s' does not exist", *argSaveDir) + } + } else if *fileExMode { + utils.Fatalf("Parameter 'savedir' is mandatory for file exchange mode") + } + if *echoMode { echo() } @@ -374,6 +388,8 @@ func run() { if *requestMail { requestExpiredMessagesLoop() + } else if *fileExMode { + sendFilesLoop() } else { sendLoop() } @@ -399,6 +415,31 @@ func sendLoop() { } } +func sendFilesLoop() { + for { + s := scanLine("") + if s == quitCommand { + fmt.Println("Quit command received") + close(done) + break + } + b, err := ioutil.ReadFile(s) + if err != nil { + fmt.Printf(">>> Error: %s \n", err) + continue + } else { + h := sendMsg(b) + if (h == common.Hash{}) { + fmt.Printf(">>> Error: message was not sent \n") + } else { + timestamp := time.Now().Unix() + from := crypto.PubkeyToAddress(asymKey.PublicKey) + fmt.Printf("\n%d <%x>: sent message with hash %x\n", timestamp, from, h) + } + } + } +} + func scanLine(prompt string) string { if len(prompt) > 0 { fmt.Print(prompt) @@ -425,7 +466,7 @@ func scanUint(prompt string) uint32 { return uint32(i) } -func sendMsg(payload []byte) { +func sendMsg(payload []byte) common.Hash { params := whisper.MessageParams{ Src: asymKey, Dst: pub, @@ -441,13 +482,16 @@ func sendMsg(payload []byte) { envelope, err := msg.Wrap(¶ms) if err != nil { fmt.Printf("failed to seal message: %v \n", err) - return + return common.Hash{} } err = shh.Send(envelope) if err != nil { fmt.Printf("failed to send message: %v \n", err) + return common.Hash{} } + + return envelope.Hash() } func messageLoop() { @@ -463,7 +507,11 @@ func messageLoop() { case <-ticker.C: messages := f.Retrieve() for _, msg := range messages { - printMessageInfo(msg) + if *fileExMode { + saveMessageInFile(msg) + } else { + printMessageInfo(msg) + } } case <-done: return @@ -487,6 +535,29 @@ func printMessageInfo(msg *whisper.ReceivedMessage) { } } +func saveMessageInFile(msg *whisper.ReceivedMessage) { + timestamp := fmt.Sprintf("%d", msg.Sent) + name := fmt.Sprintf("%x", msg.EnvelopeHash) + + var address common.Address + if msg.Src != nil { + address = crypto.PubkeyToAddress(*msg.Src) + } + + if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) { + // message from myself: don't save, only report + fmt.Printf("\n%s <%x>: message received: '%s'\n", timestamp, address, name) + } else { + fullpath := filepath.Join(*argSaveDir, name) + err := ioutil.WriteFile(fullpath, msg.Payload, 0644) + if err != nil { + fmt.Printf("\n%s [%x]: message received but not saved: %s\n", timestamp, address, err) + } else { + fmt.Printf("\n%s [%x]: message received and saved as '%s'\n", timestamp, address, name) + } + } +} + func requestExpiredMessagesLoop() { var key, peerID []byte var timeLow, timeUpp uint32 @@ -502,7 +573,7 @@ func requestExpiredMessagesLoop() { utils.Fatalf("Failed to save symmetric key for mail request: %s", err) } peerID = extractIdFromEnode(*argEnode) - shh.MarkPeerTrusted(peerID) + shh.AllowP2PMessagesFromPeer(peerID) for { timeLow = scanUint("Please enter the lower limit of the time range (unix timestamp): ") diff --git a/whisper/whisperv5/api.go b/whisper/whisperv5/api.go index 1093906a39..f965508df3 100644 --- a/whisper/whisperv5/api.go +++ b/whisper/whisperv5/api.go @@ -86,13 +86,19 @@ func (api *PublicWhisperAPI) SetMinimumPoW(val float64) error { return api.whisper.SetMinimumPoW(val) } -// MarkPeerTrusted marks specific peer trusted, which will allow it +// AllowP2PMessagesFromPeer marks specific peer trusted, which will allow it // to send historic (expired) messages. -func (api *PublicWhisperAPI) MarkPeerTrusted(peerID hexutil.Bytes) error { +func (api *PublicWhisperAPI) AllowP2PMessagesFromPeer(enode string) error { if api.whisper == nil { return whisperOffLineErr } - return api.whisper.MarkPeerTrusted(peerID) + n, err := discover.ParseNode(enode) + if err != nil { + info := "Failed to parse enode of trusted peer: " + err.Error() + log.Error(info) + return errors.New(info) + } + return api.whisper.AllowP2PMessagesFromPeer(n.ID[:]) } // RequestHistoricMessages requests the peer to deliver the old (expired) messages. diff --git a/whisper/whisperv5/doc.go b/whisper/whisperv5/doc.go index 4b735cbc6c..58f2bd9852 100644 --- a/whisper/whisperv5/doc.go +++ b/whisper/whisperv5/doc.go @@ -57,7 +57,7 @@ const ( keyIdSize = 32 DefaultMaxMessageLength = 1024 * 1024 - DefaultMinimumPoW = 10.0 // todo: review after testing. + DefaultMinimumPoW = 2.0 // todo: review after testing. padSizeLimitLower = 128 // it can not be less - we don't want to reveal the absence of signature padSizeLimitUpper = 256 // just an arbitrary number, could be changed without losing compatibility diff --git a/whisper/whisperv5/whisper.go b/whisper/whisperv5/whisper.go index e4637dc6ad..8771f37780 100644 --- a/whisper/whisperv5/whisper.go +++ b/whisper/whisperv5/whisper.go @@ -77,8 +77,7 @@ type Whisper struct { // Param s should be passed if you want to implement mail server, otherwise nil. func New() *Whisper { whisper := &Whisper{ - privateKeys: make(map[string]*ecdsa.PrivateKey), - //identities: make(map[string]*ecdsa.PrivateKey), + privateKeys: make(map[string]*ecdsa.PrivateKey), symKeys: make(map[string][]byte), envelopes: make(map[common.Hash]*Envelope), expirations: make(map[uint32]*set.SetNonTS), @@ -158,7 +157,7 @@ func (w *Whisper) getPeer(peerID []byte) (*Peer, error) { // MarkPeerTrusted marks specific peer trusted, which will allow it // to send historic (expired) messages. -func (w *Whisper) MarkPeerTrusted(peerID []byte) error { +func (w *Whisper) AllowP2PMessagesFromPeer(peerID []byte) error { p, err := w.getPeer(peerID) if err != nil { return err diff --git a/whisper/whisperv5/whisper_test.go b/whisper/whisperv5/whisper_test.go index de86ff4ad7..7a7db360e8 100644 --- a/whisper/whisperv5/whisper_test.go +++ b/whisper/whisperv5/whisper_test.go @@ -51,7 +51,7 @@ func TestWhisperBasic(t *testing.T) { if peer != nil { t.Fatal("found peer for random key.") } - if err := w.MarkPeerTrusted(peerID); err == nil { + if err := w.AllowP2PMessagesFromPeer(peerID); err == nil { t.Fatalf("failed MarkPeerTrusted.") } exist := w.HasSymKey("non-existing")