aboutsummaryrefslogtreecommitdiffstats
path: root/whisper
diff options
context:
space:
mode:
Diffstat (limited to 'whisper')
-rw-r--r--whisper/mailserver/mailserver.go170
-rw-r--r--whisper/shhapi/api.go12
-rw-r--r--whisper/whisperv5/doc.go2
-rw-r--r--whisper/whisperv5/message_test.go33
-rw-r--r--whisper/whisperv5/peer.go5
-rw-r--r--whisper/whisperv5/whisper.go34
-rw-r--r--whisper/whisperv5/whisper_test.go6
7 files changed, 232 insertions, 30 deletions
diff --git a/whisper/mailserver/mailserver.go b/whisper/mailserver/mailserver.go
new file mode 100644
index 000000000..f7d6c3e5c
--- /dev/null
+++ b/whisper/mailserver/mailserver.go
@@ -0,0 +1,170 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
+package mailserver
+
+import (
+ "bytes"
+ "encoding/binary"
+
+ "github.com/ethereum/go-ethereum/cmd/utils"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/logger/glog"
+ "github.com/ethereum/go-ethereum/rlp"
+ whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
+ "github.com/syndtr/goleveldb/leveldb"
+ "github.com/syndtr/goleveldb/leveldb/util"
+)
+
+const MailServerKeyName = "958e04ab302fb36ad2616a352cbac79d"
+
+type WMailServer struct {
+ db *leveldb.DB
+ w *whisper.Whisper
+ pow float64
+ key []byte
+}
+
+type DBKey struct {
+ timestamp uint32
+ hash common.Hash
+ raw []byte
+}
+
+func NewDbKey(t uint32, h common.Hash) *DBKey {
+ const sz = common.HashLength + 4
+ var k DBKey
+ k.timestamp = t
+ k.hash = h
+ k.raw = make([]byte, sz)
+ binary.BigEndian.PutUint32(k.raw, k.timestamp)
+ copy(k.raw[4:], k.hash[:])
+ return &k
+}
+
+func (s *WMailServer) Init(shh *whisper.Whisper, path string, password string, pow float64) {
+ var err error
+ if len(path) == 0 {
+ utils.Fatalf("DB file is not specified")
+ }
+
+ if len(password) == 0 {
+ utils.Fatalf("Password is not specified for MailServer")
+ }
+
+ s.db, err = leveldb.OpenFile(path, nil)
+ if err != nil {
+ utils.Fatalf("Failed to open DB file: %s", err)
+ }
+
+ s.w = shh
+ s.pow = pow
+
+ err = s.w.AddSymKey(MailServerKeyName, []byte(password))
+ if err != nil {
+ utils.Fatalf("Failed to create symmetric key for MailServer: %s", err)
+ }
+ s.key = s.w.GetSymKey(MailServerKeyName)
+}
+
+func (s *WMailServer) Close() {
+ if s.db != nil {
+ s.db.Close()
+ }
+}
+
+func (s *WMailServer) Archive(env *whisper.Envelope) {
+ key := NewDbKey(env.Expiry-env.TTL, env.Hash())
+ rawEnvelope, err := rlp.EncodeToBytes(env)
+ if err != nil {
+ glog.V(logger.Error).Infof("rlp.EncodeToBytes failed: %s", err)
+ } else {
+ err = s.db.Put(key.raw, rawEnvelope, nil)
+ if err != nil {
+ glog.V(logger.Error).Infof("Writing to DB failed: %s", err)
+ }
+ }
+}
+
+func (s *WMailServer) DeliverMail(peer *whisper.Peer, request *whisper.Envelope) {
+ ok, lower, upper, topic := s.validate(peer, request)
+ if !ok {
+ return
+ }
+
+ var err error
+ var zero common.Hash
+ var empty whisper.TopicType
+ kl := NewDbKey(lower, zero)
+ ku := NewDbKey(upper, zero)
+ i := s.db.NewIterator(&util.Range{Start: kl.raw, Limit: ku.raw}, nil)
+ defer i.Release()
+
+ for i.Next() {
+ var envelope whisper.Envelope
+ err = rlp.DecodeBytes(i.Value(), &envelope)
+ if err != nil {
+ glog.V(logger.Error).Infof("RLP decoding failed: %s", err)
+ }
+
+ if topic == empty || envelope.Topic == topic {
+ err = s.w.SendP2PDirect(peer, &envelope)
+ if err != nil {
+ glog.V(logger.Error).Infof("Failed to send direct message to peer: %s", err)
+ return
+ }
+ }
+ }
+
+ err = i.Error()
+ if err != nil {
+ glog.V(logger.Error).Infof("Level DB iterator error: %s", err)
+ }
+}
+
+func (s *WMailServer) validate(peer *whisper.Peer, request *whisper.Envelope) (bool, uint32, uint32, whisper.TopicType) {
+ var topic whisper.TopicType
+ if s.pow > 0.0 && request.PoW() < s.pow {
+ return false, 0, 0, topic
+ }
+
+ f := whisper.Filter{KeySym: s.key}
+ decrypted := request.Open(&f)
+ if decrypted == nil {
+ glog.V(logger.Warn).Infof("Failed to decrypt p2p request")
+ return false, 0, 0, topic
+ }
+
+ if len(decrypted.Payload) < 8 {
+ glog.V(logger.Warn).Infof("Undersized p2p request")
+ return false, 0, 0, topic
+ }
+
+ if bytes.Equal(peer.ID(), decrypted.Signature) {
+ glog.V(logger.Warn).Infof("Wrong signature of p2p request")
+ return false, 0, 0, topic
+ }
+
+ lower := binary.BigEndian.Uint32(decrypted.Payload[:4])
+ upper := binary.BigEndian.Uint32(decrypted.Payload[4:8])
+
+ if len(decrypted.Payload) >= 8+whisper.TopicLength {
+ topic = whisper.BytesToTopic(decrypted.Payload[8:])
+ }
+
+ return true, lower, upper, topic
+}
diff --git a/whisper/shhapi/api.go b/whisper/shhapi/api.go
index 379bb90d3..b273053ec 100644
--- a/whisper/shhapi/api.go
+++ b/whisper/shhapi/api.go
@@ -93,12 +93,12 @@ func (api *PublicWhisperAPI) MarkPeerTrusted(peerID hexutil.Bytes) error {
// data contains parameters (time frame, payment details, etc.), required
// by the remote email-like server. Whisper is not aware about the data format,
// it will just forward the raw data to the server.
-func (api *PublicWhisperAPI) RequestHistoricMessages(peerID hexutil.Bytes, data hexutil.Bytes) error {
- if api.whisper == nil {
- return whisperOffLineErr
- }
- return api.whisper.RequestHistoricMessages(peerID, data)
-}
+//func (api *PublicWhisperAPI) RequestHistoricMessages(peerID hexutil.Bytes, data hexutil.Bytes) error {
+// if api.whisper == nil {
+// return whisperOffLineErr
+// }
+// return api.whisper.RequestHistoricMessages(peerID, data)
+//}
// HasIdentity checks if the whisper node is configured with the private key
// of the specified public pair.
diff --git a/whisper/whisperv5/doc.go b/whisper/whisperv5/doc.go
index 8ec81b180..b82a82468 100644
--- a/whisper/whisperv5/doc.go
+++ b/whisper/whisperv5/doc.go
@@ -83,5 +83,5 @@ func (e unknownVersionError) Error() string {
// in order to bypass the expiry checks.
type MailServer interface {
Archive(env *Envelope)
- DeliverMail(whisperPeer *Peer, data []byte)
+ DeliverMail(whisperPeer *Peer, request *Envelope)
}
diff --git a/whisper/whisperv5/message_test.go b/whisper/whisperv5/message_test.go
index 7c90f59c0..c6f1ca2ca 100644
--- a/whisper/whisperv5/message_test.go
+++ b/whisper/whisperv5/message_test.go
@@ -22,6 +22,7 @@ import (
"testing"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/rlp"
)
func copyFromBuf(dst []byte, src []byte, beg int) int {
@@ -311,3 +312,35 @@ func TestEncryptWithZeroKey(t *testing.T) {
t.Fatalf("wrapped with nil key, seed: %d.", seed)
}
}
+
+func TestRlpEncode(t *testing.T) {
+ InitSingleTest()
+
+ params, err := generateMessageParams()
+ if err != nil {
+ t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
+ }
+ msg := NewSentMessage(params)
+ env, err := msg.Wrap(params)
+ if err != nil {
+ t.Fatalf("wrapped with zero key, seed: %d.", seed)
+ }
+
+ raw, err := rlp.EncodeToBytes(env)
+ if err != nil {
+ t.Fatalf("RLP encode failed: %s.", err)
+ }
+
+ var decoded Envelope
+ rlp.DecodeBytes(raw, &decoded)
+ if err != nil {
+ t.Fatalf("RLP decode failed: %s.", err)
+ }
+
+ he := env.Hash()
+ hd := decoded.Hash()
+
+ if he != hd {
+ t.Fatalf("Hashes are not equal: %x vs. %x", he, hd)
+ }
+}
diff --git a/whisper/whisperv5/peer.go b/whisper/whisperv5/peer.go
index 634045504..42394a0a3 100644
--- a/whisper/whisperv5/peer.go
+++ b/whisper/whisperv5/peer.go
@@ -175,3 +175,8 @@ func (p *Peer) broadcast() error {
glog.V(logger.Detail).Infoln(p.peer, "broadcasted", len(transmit), "message(s)")
return nil
}
+
+func (p *Peer) ID() []byte {
+ id := p.peer.ID()
+ return id[:]
+}
diff --git a/whisper/whisperv5/whisper.go b/whisper/whisperv5/whisper.go
index b514c022e..ff31aab2d 100644
--- a/whisper/whisperv5/whisper.go
+++ b/whisper/whisperv5/whisper.go
@@ -31,7 +31,6 @@ import (
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/p2p"
- "github.com/ethereum/go-ethereum/rlp"
"golang.org/x/crypto/pbkdf2"
set "gopkg.in/fatih/set.v0"
)
@@ -125,13 +124,13 @@ func (w *Whisper) MarkPeerTrusted(peerID []byte) error {
return nil
}
-func (w *Whisper) RequestHistoricMessages(peerID []byte, data []byte) error {
+func (w *Whisper) RequestHistoricMessages(peerID []byte, envelope *Envelope) error {
p, err := w.getPeer(peerID)
if err != nil {
return err
}
p.trusted = true
- return p2p.Send(p.ws, p2pRequestCode, data)
+ return p2p.Send(p.ws, p2pRequestCode, envelope)
}
func (w *Whisper) SendP2PMessage(peerID []byte, envelope *Envelope) error {
@@ -142,6 +141,10 @@ func (w *Whisper) SendP2PMessage(peerID []byte, envelope *Envelope) error {
return p2p.Send(p.ws, p2pCode, envelope)
}
+func (w *Whisper) SendP2PDirect(peer *Peer, envelope *Envelope) error {
+ return p2p.Send(peer.ws, p2pCode, envelope)
+}
+
// NewIdentity generates a new cryptographic identity for the client, and injects
// it into the known identities for message decryption.
func (w *Whisper) NewIdentity() *ecdsa.PrivateKey {
@@ -347,9 +350,6 @@ func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
return fmt.Errorf("invalid envelope")
}
p.mark(envelope)
- if wh.mailServer != nil {
- wh.mailServer.Archive(envelope)
- }
}
case p2pCode:
// peer-to-peer message, sent directly to peer bypassing PoW checks, etc.
@@ -357,25 +357,22 @@ func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
// therefore might not satisfy the PoW, expiry and other requirements.
// these messages are only accepted from the trusted peer.
if p.trusted {
- var envelopes []*Envelope
- if err := packet.Decode(&envelopes); err != nil {
+ var envelope Envelope
+ if err := packet.Decode(&envelope); err != nil {
glog.V(logger.Warn).Infof("%v: failed to decode direct message: [%v], peer will be disconnected", p.peer, err)
return fmt.Errorf("garbage received (directMessage)")
}
- for _, envelope := range envelopes {
- wh.postEvent(envelope, true)
- }
+ wh.postEvent(&envelope, true)
}
case p2pRequestCode:
// Must be processed if mail server is implemented. Otherwise ignore.
if wh.mailServer != nil {
- s := rlp.NewStream(packet.Payload, uint64(packet.Size))
- data, err := s.Bytes()
- if err == nil {
- wh.mailServer.DeliverMail(p, data)
- } else {
- glog.V(logger.Error).Infof("%v: bad requestHistoricMessages received: [%v]", p.peer, err)
+ var request Envelope
+ if err := packet.Decode(&request); err != nil {
+ glog.V(logger.Warn).Infof("%v: failed to decode p2p request message: [%v], peer will be disconnected", p.peer, err)
+ return fmt.Errorf("garbage received (p2p request)")
}
+ wh.mailServer.DeliverMail(p, &request)
}
default:
// New message types might be implemented in the future versions of Whisper.
@@ -454,6 +451,9 @@ func (wh *Whisper) add(envelope *Envelope) error {
} else {
glog.V(logger.Detail).Infof("cached whisper envelope [%x]: %v\n", envelope.Hash(), envelope)
wh.postEvent(envelope, false) // notify the local node about the new message
+ if wh.mailServer != nil {
+ wh.mailServer.Archive(envelope)
+ }
}
return nil
}
diff --git a/whisper/whisperv5/whisper_test.go b/whisper/whisperv5/whisper_test.go
index b9cd9b1a0..a3ded7b37 100644
--- a/whisper/whisperv5/whisper_test.go
+++ b/whisper/whisperv5/whisper_test.go
@@ -57,12 +57,6 @@ func TestWhisperBasic(t *testing.T) {
if err := w.MarkPeerTrusted(peerID); err == nil {
t.Fatalf("failed MarkPeerTrusted.")
}
- if err := w.RequestHistoricMessages(peerID, peerID); err == nil {
- t.Fatalf("failed RequestHistoricMessages.")
- }
- if err := w.SendP2PMessage(peerID, nil); err == nil {
- t.Fatalf("failed SendP2PMessage.")
- }
exist := w.HasSymKey("non-existing")
if exist {
t.Fatalf("failed HasSymKey.")