diff options
Diffstat (limited to 'whisper')
-rw-r--r-- | whisper/mailserver/mailserver.go | 170 | ||||
-rw-r--r-- | whisper/shhapi/api.go | 12 | ||||
-rw-r--r-- | whisper/whisperv5/doc.go | 2 | ||||
-rw-r--r-- | whisper/whisperv5/message_test.go | 33 | ||||
-rw-r--r-- | whisper/whisperv5/peer.go | 5 | ||||
-rw-r--r-- | whisper/whisperv5/whisper.go | 34 | ||||
-rw-r--r-- | whisper/whisperv5/whisper_test.go | 6 |
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.") |