Mailserver: return error response. (#1244)
This commit is contained in:
parent
b71058d73f
commit
ba504e99c4
|
@ -821,12 +821,12 @@
|
|||
revision = "55370cdfd9f288059b03c04e23784bb8829a894c"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:a2075e4e314cff4aadaf3ffe18b77659d259b1a46be9d2bdb0d5b9f8222f420c"
|
||||
digest = "1:9552f5ea7e82e95910e182bd41b6c71933b50717990a61ac517bfa1cc6f653dc"
|
||||
name = "github.com/status-im/whisper"
|
||||
packages = ["whisperv6"]
|
||||
pruneopts = "NUT"
|
||||
revision = "f41584950703450b0370e85560ba8d9322794ead"
|
||||
revision = "14e1bbfd9ba956e7fc7649e815aa19e4386b26da"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:572c783a763db6383aca3179976eb80e4c900f52eba56cba8bb2e3cea7ce720e"
|
||||
|
|
|
@ -163,5 +163,5 @@
|
|||
branch = "master"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
version = "v1.2.0"
|
||||
name = "github.com/status-im/whisper"
|
||||
|
|
|
@ -247,25 +247,33 @@ func (s *WMailServer) DeliverMail(peer *whisper.Peer, request *whisper.Envelope)
|
|||
}
|
||||
if s.exceedsPeerRequests(peer.ID()) {
|
||||
requestErrorsCounter.Inc(1)
|
||||
log.Error("Peer exceeded request per seconds limit", "peerID", peer.ID())
|
||||
s.trySendHistoricMessageErrorResponse(peer, request, fmt.Errorf("rate limit exceeded"))
|
||||
return
|
||||
}
|
||||
|
||||
defer recoverLevelDBPanics("DeliverMail")
|
||||
|
||||
if ok, lower, upper, bloom, limit, cursor := s.validateRequest(peer.ID(), request); ok {
|
||||
if lower, upper, bloom, limit, cursor, err := s.validateRequest(peer.ID(), request); err != nil {
|
||||
requestValidationErrorsCounter.Inc(1)
|
||||
log.Error("Mailserver request failed validaton", "peerID", peer.ID())
|
||||
s.trySendHistoricMessageErrorResponse(peer, request, err)
|
||||
} else {
|
||||
_, lastEnvelopeHash, nextPageCursor, err := s.processRequest(peer, lower, upper, bloom, limit, cursor)
|
||||
|
||||
if err != nil {
|
||||
processRequestErrorsCounter.Inc(1)
|
||||
log.Error(fmt.Sprintf("error in DeliverMail: %s", err))
|
||||
log.Error("Error while delivering mail to the peer", "err", err, "peerID", peer.ID())
|
||||
s.trySendHistoricMessageErrorResponse(peer, request, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.sendHistoricMessageResponse(peer, request, lastEnvelopeHash, nextPageCursor); err != nil {
|
||||
historicResponseErrorsCounter.Inc(1)
|
||||
log.Error(fmt.Sprintf("SendHistoricMessageResponse error: %s", err))
|
||||
log.Error("Error while sending historic message response", "err", err, "peerID", peer.ID())
|
||||
// we still want to try to report error even it it is a p2p error and it is unlikely
|
||||
s.trySendHistoricMessageErrorResponse(peer, request, err)
|
||||
}
|
||||
} else {
|
||||
requestValidationErrorsCounter.Inc(1)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -325,7 +333,7 @@ func (s *WMailServer) processRequest(peer *whisper.Peer, lower, upper uint32, bl
|
|||
var envelope whisper.Envelope
|
||||
decodeErr := rlp.DecodeBytes(i.Value(), &envelope)
|
||||
if decodeErr != nil {
|
||||
log.Error(fmt.Sprintf("RLP decoding failed: %s", decodeErr))
|
||||
log.Error(fmt.Sprintf("failed to decode RLP: %s", decodeErr))
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -336,7 +344,7 @@ func (s *WMailServer) processRequest(peer *whisper.Peer, lower, upper uint32, bl
|
|||
} else {
|
||||
err = s.w.SendP2PDirect(peer, &envelope)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Failed to send direct message to peer: %s", err))
|
||||
log.Error(fmt.Sprintf("failed to send direct message to peer: %s", err))
|
||||
return
|
||||
}
|
||||
lastEnvelopeHash = envelope.Hash()
|
||||
|
@ -364,12 +372,22 @@ func (s *WMailServer) processRequest(peer *whisper.Peer, lower, upper uint32, bl
|
|||
}
|
||||
|
||||
func (s *WMailServer) sendHistoricMessageResponse(peer *whisper.Peer, request *whisper.Envelope, lastEnvelopeHash common.Hash, cursor cursorType) error {
|
||||
requestID := request.Hash()
|
||||
payload := append(requestID[:], lastEnvelopeHash[:]...)
|
||||
payload = append(payload, cursor...)
|
||||
payload := whisper.CreateMailServerRequestCompletedPayload(request.Hash(), lastEnvelopeHash, cursor)
|
||||
return s.w.SendHistoricMessageResponse(peer, payload)
|
||||
}
|
||||
|
||||
// this method doesn't return an error because it is already in the error handling chain
|
||||
func (s *WMailServer) trySendHistoricMessageErrorResponse(peer *whisper.Peer, request *whisper.Envelope, errorToReport error) {
|
||||
payload := whisper.CreateMailServerRequestFailedPayload(request.Hash(), errorToReport)
|
||||
|
||||
err := s.w.SendHistoricMessageResponse(peer, payload)
|
||||
// if we can't report an error, probably something is wrong with p2p connection,
|
||||
// so we just print a log entry to document this sad fact
|
||||
if err != nil {
|
||||
log.Error("Error while reporting error response", "err", err, "peerID", peer.ID())
|
||||
}
|
||||
}
|
||||
|
||||
// openEnvelope tries to decrypt an envelope, first based on asymetric key (if
|
||||
// provided) and second on the symetric key (if provided)
|
||||
func (s *WMailServer) openEnvelope(request *whisper.Envelope) *whisper.ReceivedMessage {
|
||||
|
@ -387,41 +405,38 @@ func (s *WMailServer) openEnvelope(request *whisper.Envelope) *whisper.ReceivedM
|
|||
}
|
||||
|
||||
// validateRequest runs different validations on the current request.
|
||||
func (s *WMailServer) validateRequest(peerID []byte, request *whisper.Envelope) (bool, uint32, uint32, []byte, uint32, cursorType) {
|
||||
func (s *WMailServer) validateRequest(peerID []byte, request *whisper.Envelope) (uint32, uint32, []byte, uint32, cursorType, error) {
|
||||
if s.pow > 0.0 && request.PoW() < s.pow {
|
||||
return false, 0, 0, nil, 0, nil
|
||||
return 0, 0, nil, 0, nil, fmt.Errorf("PoW() is too low")
|
||||
}
|
||||
|
||||
decrypted := s.openEnvelope(request)
|
||||
if decrypted == nil {
|
||||
log.Warn("Failed to decrypt p2p request")
|
||||
return false, 0, 0, nil, 0, nil
|
||||
return 0, 0, nil, 0, nil, fmt.Errorf("failed to decrypt p2p request")
|
||||
}
|
||||
|
||||
if err := s.checkMsgSignature(decrypted, peerID); err != nil {
|
||||
log.Warn(err.Error())
|
||||
return false, 0, 0, nil, 0, nil
|
||||
return 0, 0, nil, 0, nil, err
|
||||
}
|
||||
|
||||
bloom, err := s.bloomFromReceivedMessage(decrypted)
|
||||
if err != nil {
|
||||
log.Warn(err.Error())
|
||||
return false, 0, 0, nil, 0, nil
|
||||
return 0, 0, nil, 0, nil, err
|
||||
}
|
||||
|
||||
lower := binary.BigEndian.Uint32(decrypted.Payload[:4])
|
||||
upper := binary.BigEndian.Uint32(decrypted.Payload[4:8])
|
||||
|
||||
if upper < lower {
|
||||
log.Error(fmt.Sprintf("Query range is invalid: from > to (%d > %d)", lower, upper))
|
||||
return false, 0, 0, nil, 0, nil
|
||||
err := fmt.Errorf("query range is invalid: from > to (%d > %d)", lower, upper)
|
||||
return 0, 0, nil, 0, nil, err
|
||||
}
|
||||
|
||||
lowerTime := time.Unix(int64(lower), 0)
|
||||
upperTime := time.Unix(int64(upper), 0)
|
||||
if upperTime.Sub(lowerTime) > maxQueryRange {
|
||||
log.Warn(fmt.Sprintf("Query range too big for peer %s", string(peerID)))
|
||||
return false, 0, 0, nil, 0, nil
|
||||
err := fmt.Errorf("query range too big for peer %s", string(peerID))
|
||||
return 0, 0, nil, 0, nil, err
|
||||
}
|
||||
|
||||
var limit uint32
|
||||
|
@ -434,7 +449,8 @@ func (s *WMailServer) validateRequest(peerID []byte, request *whisper.Envelope)
|
|||
cursor = decrypted.Payload[requestTimeRangeLength+whisper.BloomFilterSize+requestLimitLength:]
|
||||
}
|
||||
|
||||
return true, lower, upper, bloom, limit, cursor
|
||||
err = nil
|
||||
return lower, upper, bloom, limit, cursor, err
|
||||
}
|
||||
|
||||
// checkMsgSignature returns an error in case the message is not correcly signed
|
||||
|
|
|
@ -322,8 +322,8 @@ func (s *MailserverSuite) TestRequestPaginationLimit() {
|
|||
params.limit = 6
|
||||
request := s.createRequest(params)
|
||||
src := crypto.FromECDSAPub(¶ms.key.PublicKey)
|
||||
ok, lower, upper, bloom, limit, cursor := s.server.validateRequest(src, request)
|
||||
s.True(ok)
|
||||
lower, upper, bloom, limit, cursor, err := s.server.validateRequest(src, request)
|
||||
s.True(err == nil)
|
||||
s.Nil(cursor)
|
||||
s.Equal(params.limit, limit)
|
||||
|
||||
|
@ -428,9 +428,9 @@ func (s *MailserverSuite) TestMailServer() {
|
|||
s.T().Run(tc.info, func(*testing.T) {
|
||||
request := s.createRequest(tc.params)
|
||||
src := crypto.FromECDSAPub(&tc.params.key.PublicKey)
|
||||
ok, lower, upper, bloom, limit, _ := s.server.validateRequest(src, request)
|
||||
s.Equal(tc.isOK, ok)
|
||||
if ok {
|
||||
lower, upper, bloom, limit, _, err := s.server.validateRequest(src, request)
|
||||
s.Equal(tc.isOK, err == nil)
|
||||
if err == nil {
|
||||
s.Equal(tc.params.low, lower)
|
||||
s.Equal(tc.params.upp, upper)
|
||||
s.Equal(tc.params.limit, limit)
|
||||
|
@ -438,8 +438,8 @@ func (s *MailserverSuite) TestMailServer() {
|
|||
s.Equal(tc.expect, s.messageExists(env, tc.params.low, tc.params.upp, bloom, tc.params.limit))
|
||||
|
||||
src[0]++
|
||||
ok, _, _, _, _, _ = s.server.validateRequest(src, request)
|
||||
s.True(ok)
|
||||
_, _, _, _, _, err = s.server.validateRequest(src, request)
|
||||
s.True(err == nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ const (
|
|||
type EnvelopeEventsHandler interface {
|
||||
EnvelopeSent(common.Hash)
|
||||
EnvelopeExpired(common.Hash)
|
||||
MailServerRequestCompleted(common.Hash, common.Hash, []byte)
|
||||
MailServerRequestCompleted(common.Hash, common.Hash, []byte, error)
|
||||
MailServerRequestExpired(common.Hash)
|
||||
}
|
||||
|
||||
|
@ -291,7 +291,7 @@ func (t *tracker) handleEventMailServerRequestCompleted(event whisper.EnvelopeEv
|
|||
delete(t.cache, event.Hash)
|
||||
if t.handler != nil {
|
||||
if resp, ok := event.Data.(*whisper.MailServerResponse); ok {
|
||||
t.handler.MailServerRequestCompleted(event.Hash, resp.LastEnvelopeHash, resp.Cursor)
|
||||
t.handler.MailServerRequestCompleted(event.Hash, resp.LastEnvelopeHash, resp.Cursor, resp.Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ func newHandlerMock(buf int) handlerMock {
|
|||
expirations: make(chan common.Hash, buf),
|
||||
requestsCompleted: make(chan common.Hash, buf),
|
||||
requestsExpired: make(chan common.Hash, buf),
|
||||
requestsFailed: make(chan common.Hash, buf),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,6 +32,7 @@ type handlerMock struct {
|
|||
expirations chan common.Hash
|
||||
requestsCompleted chan common.Hash
|
||||
requestsExpired chan common.Hash
|
||||
requestsFailed chan common.Hash
|
||||
}
|
||||
|
||||
func (t handlerMock) EnvelopeSent(hash common.Hash) {
|
||||
|
@ -41,8 +43,12 @@ func (t handlerMock) EnvelopeExpired(hash common.Hash) {
|
|||
t.expirations <- hash
|
||||
}
|
||||
|
||||
func (t handlerMock) MailServerRequestCompleted(requestID common.Hash, lastEnvelopeHash common.Hash, cursor []byte) {
|
||||
t.requestsCompleted <- requestID
|
||||
func (t handlerMock) MailServerRequestCompleted(requestID common.Hash, lastEnvelopeHash common.Hash, cursor []byte, err error) {
|
||||
if err == nil {
|
||||
t.requestsCompleted <- requestID
|
||||
} else {
|
||||
t.requestsFailed <- requestID
|
||||
}
|
||||
}
|
||||
|
||||
func (t handlerMock) MailServerRequestExpired(hash common.Hash) {
|
||||
|
@ -459,6 +465,26 @@ func (s *TrackerSuite) TestRequestCompleted() {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *TrackerSuite) TestRequestFailed() {
|
||||
mock := newHandlerMock(1)
|
||||
s.tracker.handler = mock
|
||||
s.tracker.AddRequest(testHash, time.After(defaultRequestTimeout*time.Second))
|
||||
s.Contains(s.tracker.cache, testHash)
|
||||
s.Equal(MailServerRequestSent, s.tracker.cache[testHash])
|
||||
s.tracker.handleEvent(whisper.EnvelopeEvent{
|
||||
Event: whisper.EventMailServerRequestCompleted,
|
||||
Hash: testHash,
|
||||
Data: &whisper.MailServerResponse{Error: errors.New("test error")},
|
||||
})
|
||||
select {
|
||||
case requestID := <-mock.requestsFailed:
|
||||
s.Equal(testHash, requestID)
|
||||
s.NotContains(s.tracker.cache, testHash)
|
||||
case <-time.After(10 * time.Second):
|
||||
s.Fail("timed out while waiting for a request to be failed")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TrackerSuite) TestRequestExpiration() {
|
||||
mock := newHandlerMock(1)
|
||||
s.tracker.handler = mock
|
||||
|
|
|
@ -19,8 +19,8 @@ func (h EnvelopeSignalHandler) EnvelopeExpired(hash common.Hash) {
|
|||
}
|
||||
|
||||
// MailServerRequestCompleted triggered when the mailserver sends a message to notify that the request has been completed
|
||||
func (h EnvelopeSignalHandler) MailServerRequestCompleted(requestID common.Hash, lastEnvelopeHash common.Hash, cursor []byte) {
|
||||
signal.SendMailServerRequestCompleted(requestID, lastEnvelopeHash, cursor)
|
||||
func (h EnvelopeSignalHandler) MailServerRequestCompleted(requestID common.Hash, lastEnvelopeHash common.Hash, cursor []byte, err error) {
|
||||
signal.SendMailServerRequestCompleted(requestID, lastEnvelopeHash, cursor, err)
|
||||
}
|
||||
|
||||
// MailServerRequestExpired triggered when the mailserver request expires
|
||||
|
|
|
@ -38,6 +38,7 @@ type MailServerResponseSignal struct {
|
|||
RequestID common.Hash `json:"requestID"`
|
||||
LastEnvelopeHash common.Hash `json:"lastEnvelopeHash"`
|
||||
Cursor string `json:"cursor"`
|
||||
ErrorMsg string `json:"errorMessage"`
|
||||
}
|
||||
|
||||
// DecryptMessageFailedSignal holds the sender of the message that could not be decrypted
|
||||
|
@ -62,11 +63,16 @@ func SendEnvelopeExpired(hash common.Hash) {
|
|||
}
|
||||
|
||||
// SendMailServerRequestCompleted triggered when mail server response has been received
|
||||
func SendMailServerRequestCompleted(requestID common.Hash, lastEnvelopeHash common.Hash, cursor []byte) {
|
||||
func SendMailServerRequestCompleted(requestID common.Hash, lastEnvelopeHash common.Hash, cursor []byte, err error) {
|
||||
errorMsg := ""
|
||||
if err != nil {
|
||||
errorMsg = err.Error()
|
||||
}
|
||||
sig := MailServerResponseSignal{
|
||||
RequestID: requestID,
|
||||
LastEnvelopeHash: lastEnvelopeHash,
|
||||
Cursor: string(cursor),
|
||||
ErrorMsg: errorMsg,
|
||||
}
|
||||
send(EventMailServerRequestCompleted, sig)
|
||||
}
|
||||
|
|
136
vendor/github.com/status-im/whisper/whisperv6/mailserver_response.go
generated
vendored
Normal file
136
vendor/github.com/status-im/whisper/whisperv6/mailserver_response.go
generated
vendored
Normal file
|
@ -0,0 +1,136 @@
|
|||
package whisperv6
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
const (
|
||||
mailServerFailedPayloadPrefix = "ERROR="
|
||||
cursorSize = 36
|
||||
)
|
||||
|
||||
func invalidResponseSizeError(size int) error {
|
||||
return fmt.Errorf("unexpected payload size: %d", size)
|
||||
}
|
||||
|
||||
// CreateMailServerRequestCompletedPayload creates a payload representing
|
||||
// a successful request to mailserver
|
||||
func CreateMailServerRequestCompletedPayload(requestID, lastEnvelopeHash common.Hash, cursor []byte) []byte {
|
||||
payload := append(requestID[:], lastEnvelopeHash[:]...)
|
||||
payload = append(payload, cursor...)
|
||||
return payload
|
||||
}
|
||||
|
||||
// CreateMailServerRequestFailedPayload creates a payload representing
|
||||
// a failed request to a mailserver
|
||||
func CreateMailServerRequestFailedPayload(requestID common.Hash, err error) []byte {
|
||||
payloadPrefix := []byte(mailServerFailedPayloadPrefix)
|
||||
errorString := []byte(err.Error())
|
||||
payload := append(payloadPrefix, requestID[:]...)
|
||||
payload = append(payload, errorString[:]...)
|
||||
return payload
|
||||
}
|
||||
|
||||
// CreateMailServerEvent returns EnvelopeEvent with correct data
|
||||
// if payload corresponds to any of the know mailserver events:
|
||||
// * request completed successfully
|
||||
// * request failed
|
||||
// If the payload is unknown/unparseable, it returns `nil`
|
||||
func CreateMailServerEvent(payload []byte) (*EnvelopeEvent, error) {
|
||||
|
||||
if len(payload) < common.HashLength {
|
||||
return nil, invalidResponseSizeError(len(payload))
|
||||
}
|
||||
|
||||
event, err := tryCreateMailServerRequestFailedEvent(payload)
|
||||
|
||||
if err != nil || event != nil {
|
||||
return event, err
|
||||
}
|
||||
|
||||
return tryCreateMailServerRequestCompletedEvent(payload)
|
||||
}
|
||||
|
||||
func tryCreateMailServerRequestFailedEvent(payload []byte) (*EnvelopeEvent, error) {
|
||||
if len(payload) < common.HashLength+len(mailServerFailedPayloadPrefix) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
prefix, remainder := extractPrefix(payload, len(mailServerFailedPayloadPrefix))
|
||||
|
||||
if !bytes.Equal(prefix, []byte(mailServerFailedPayloadPrefix)) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var (
|
||||
requestID common.Hash
|
||||
errorMsg string
|
||||
)
|
||||
|
||||
requestID, remainder = extractHash(remainder)
|
||||
errorMsg = string(remainder)
|
||||
|
||||
event := EnvelopeEvent{
|
||||
Hash: requestID,
|
||||
Event: EventMailServerRequestCompleted,
|
||||
Data: &MailServerResponse{
|
||||
Error: errors.New(errorMsg),
|
||||
},
|
||||
}
|
||||
|
||||
return &event, nil
|
||||
|
||||
}
|
||||
|
||||
func tryCreateMailServerRequestCompletedEvent(payload []byte) (*EnvelopeEvent, error) {
|
||||
// check if payload is
|
||||
// - requestID or
|
||||
// - requestID + lastEnvelopeHash or
|
||||
// - requestID + lastEnvelopeHash + cursor
|
||||
// requestID is the hash of the request envelope.
|
||||
// lastEnvelopeHash is the last envelope sent by the mail server
|
||||
// cursor is the db key, 36 bytes: 4 for the timestamp + 32 for the envelope hash.
|
||||
if len(payload) > common.HashLength*2+cursorSize {
|
||||
return nil, invalidResponseSizeError(len(payload))
|
||||
}
|
||||
|
||||
var (
|
||||
requestID common.Hash
|
||||
lastEnvelopeHash common.Hash
|
||||
cursor []byte
|
||||
)
|
||||
|
||||
requestID, remainder := extractHash(payload)
|
||||
|
||||
if len(remainder) >= common.HashLength {
|
||||
lastEnvelopeHash, remainder = extractHash(remainder)
|
||||
}
|
||||
|
||||
if len(remainder) >= cursorSize {
|
||||
cursor = remainder
|
||||
}
|
||||
|
||||
event := EnvelopeEvent{
|
||||
Hash: requestID,
|
||||
Event: EventMailServerRequestCompleted,
|
||||
Data: &MailServerResponse{
|
||||
LastEnvelopeHash: lastEnvelopeHash,
|
||||
Cursor: cursor,
|
||||
},
|
||||
}
|
||||
|
||||
return &event, nil
|
||||
}
|
||||
|
||||
func extractHash(payload []byte) (common.Hash, []byte) {
|
||||
prefix, remainder := extractPrefix(payload, common.HashLength)
|
||||
return common.BytesToHash(prefix), remainder
|
||||
}
|
||||
|
||||
func extractPrefix(payload []byte, size int) ([]byte, []byte) {
|
||||
return payload[:size], payload[size:]
|
||||
}
|
|
@ -21,6 +21,8 @@ import (
|
|||
"crypto/ecdsa"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
@ -64,6 +66,7 @@ const (
|
|||
type MailServerResponse struct {
|
||||
LastEnvelopeHash common.Hash
|
||||
Cursor []byte
|
||||
Error error
|
||||
}
|
||||
|
||||
// Whisper represents a dark communication interface through the Ethereum
|
||||
|
@ -418,17 +421,23 @@ func (whisper *Whisper) SendHistoricMessageResponse(peer *Peer, payload []byte)
|
|||
}
|
||||
|
||||
// SendP2PMessage sends a peer-to-peer message to a specific peer.
|
||||
func (whisper *Whisper) SendP2PMessage(peerID []byte, envelope *Envelope) error {
|
||||
func (whisper *Whisper) SendP2PMessage(peerID []byte, envelopes ...*Envelope) error {
|
||||
p, err := whisper.getPeer(peerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return whisper.SendP2PDirect(p, envelope)
|
||||
return whisper.SendP2PDirect(p, envelopes...)
|
||||
}
|
||||
|
||||
// SendP2PDirect sends a peer-to-peer message to a specific peer.
|
||||
func (whisper *Whisper) SendP2PDirect(peer *Peer, envelope *Envelope) error {
|
||||
return p2p.Send(peer.ws, p2pMessageCode, envelope)
|
||||
// If only a single envelope is given, data is sent as a single object
|
||||
// rather than a slice. This is important to keep this method backward compatible
|
||||
// as it used to send only single envelopes.
|
||||
func (whisper *Whisper) SendP2PDirect(peer *Peer, envelopes ...*Envelope) error {
|
||||
if len(envelopes) == 1 {
|
||||
return p2p.Send(peer.ws, p2pMessageCode, envelopes[0])
|
||||
}
|
||||
return p2p.Send(peer.ws, p2pMessageCode, envelopes)
|
||||
}
|
||||
|
||||
// NewKeyPair generates a new cryptographic identity for the client, and injects
|
||||
|
@ -843,12 +852,46 @@ func (whisper *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 envelope Envelope
|
||||
if err := packet.Decode(&envelope); err != nil {
|
||||
log.Warn("failed to decode direct message, peer will be disconnected", "peer", p.peer.ID(), "err", err)
|
||||
return errors.New("invalid direct message")
|
||||
var (
|
||||
envelope *Envelope
|
||||
envelopes []*Envelope
|
||||
err error
|
||||
)
|
||||
|
||||
// Read all data as we will try to decode it possibly twice
|
||||
// to keep backward compatibility.
|
||||
data, err := ioutil.ReadAll(packet.Payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid direct messages: %v", err)
|
||||
}
|
||||
r := bytes.NewReader(data)
|
||||
|
||||
packet.Payload = r
|
||||
|
||||
if err = packet.Decode(&envelopes); err == nil {
|
||||
for _, envelope := range envelopes {
|
||||
whisper.postEvent(envelope, true)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// As we failed to decode envelopes, let's set the offset
|
||||
// to the beginning and try decode data again.
|
||||
// Decoding to a single Envelope is required
|
||||
// to be backward compatible.
|
||||
if _, err := r.Seek(0, io.SeekStart); err != nil {
|
||||
return fmt.Errorf("invalid direct messages: %v", err)
|
||||
}
|
||||
|
||||
if err = packet.Decode(&envelope); err == nil {
|
||||
whisper.postEvent(envelope, true)
|
||||
continue
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Warn("failed to decode direct message, peer will be disconnected", "peer", p.peer.ID(), "err", err)
|
||||
return fmt.Errorf("invalid direct message: %v", err)
|
||||
}
|
||||
whisper.postEvent(&envelope, true)
|
||||
}
|
||||
case p2pRequestCode:
|
||||
// Must be processed if mail server is implemented. Otherwise ignore.
|
||||
|
@ -869,44 +912,17 @@ func (whisper *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
|
|||
return errors.New("invalid request response message")
|
||||
}
|
||||
|
||||
// check if payload is
|
||||
// - requestID or
|
||||
// - requestID + lastEnvelopeHash or
|
||||
// - requestID + lastEnvelopeHash + cursor
|
||||
// requestID is the hash of the request envelope.
|
||||
// lastEnvelopeHash is the last envelope sent by the mail server
|
||||
// cursor is the db key, 36 bytes: 4 for the timestamp + 32 for the envelope hash.
|
||||
// length := len(payload)
|
||||
event, err := CreateMailServerEvent(payload)
|
||||
|
||||
if len(payload) < common.HashLength || len(payload) > common.HashLength*3+4 {
|
||||
log.Warn("invalid response message, peer will be disconnected", "peer", p.peer.ID(), "err", err, "payload size", len(payload))
|
||||
return errors.New("invalid response size")
|
||||
if err != nil {
|
||||
log.Warn("error while parsing request complete code, peer will be disconnected", "peer", p.peer.ID(), "err", err)
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
requestID common.Hash
|
||||
lastEnvelopeHash common.Hash
|
||||
cursor []byte
|
||||
)
|
||||
|
||||
requestID = common.BytesToHash(payload[:common.HashLength])
|
||||
|
||||
if len(payload) >= common.HashLength*2 {
|
||||
lastEnvelopeHash = common.BytesToHash(payload[common.HashLength : common.HashLength*2])
|
||||
if event != nil {
|
||||
whisper.envelopeFeed.Send(*event)
|
||||
}
|
||||
|
||||
if len(payload) >= common.HashLength*2+36 {
|
||||
cursor = payload[common.HashLength*2 : common.HashLength*2+36]
|
||||
}
|
||||
|
||||
whisper.envelopeFeed.Send(EnvelopeEvent{
|
||||
Hash: requestID,
|
||||
Event: EventMailServerRequestCompleted,
|
||||
Data: &MailServerResponse{
|
||||
LastEnvelopeHash: lastEnvelopeHash,
|
||||
Cursor: cursor,
|
||||
},
|
||||
})
|
||||
}
|
||||
default:
|
||||
// New message types might be implemented in the future versions of Whisper.
|
||||
|
|
Loading…
Reference in New Issue