github.com/kapoio/go-kapoio@v1.9.7/whisper/mailserver/mailserver.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 // Package mailserver provides a naive, example mailserver implementation 18 package mailserver 19 20 import ( 21 "encoding/binary" 22 "fmt" 23 24 "github.com/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/crypto" 26 "github.com/ethereum/go-ethereum/log" 27 "github.com/ethereum/go-ethereum/rlp" 28 whisper "github.com/ethereum/go-ethereum/whisper/whisperv6" 29 "github.com/syndtr/goleveldb/leveldb" 30 "github.com/syndtr/goleveldb/leveldb/opt" 31 "github.com/syndtr/goleveldb/leveldb/util" 32 ) 33 34 // WMailServer represents the state data of the mailserver. 35 type WMailServer struct { 36 db *leveldb.DB 37 w *whisper.Whisper 38 pow float64 39 key []byte 40 } 41 42 type DBKey struct { 43 timestamp uint32 44 hash common.Hash 45 raw []byte 46 } 47 48 // NewDbKey is a helper function that creates a levelDB 49 // key from a hash and an integer. 50 func NewDbKey(t uint32, h common.Hash) *DBKey { 51 const sz = common.HashLength + 4 52 var k DBKey 53 k.timestamp = t 54 k.hash = h 55 k.raw = make([]byte, sz) 56 binary.BigEndian.PutUint32(k.raw, k.timestamp) 57 copy(k.raw[4:], k.hash[:]) 58 return &k 59 } 60 61 // Init initializes the mail server. 62 func (s *WMailServer) Init(shh *whisper.Whisper, path string, password string, pow float64) error { 63 var err error 64 if len(path) == 0 { 65 return fmt.Errorf("DB file is not specified") 66 } 67 68 if len(password) == 0 { 69 return fmt.Errorf("password is not specified") 70 } 71 72 s.db, err = leveldb.OpenFile(path, &opt.Options{OpenFilesCacheCapacity: 32}) 73 if err != nil { 74 return fmt.Errorf("open DB file: %s", err) 75 } 76 77 s.w = shh 78 s.pow = pow 79 80 MailServerKeyID, err := s.w.AddSymKeyFromPassword(password) 81 if err != nil { 82 return fmt.Errorf("create symmetric key: %s", err) 83 } 84 s.key, err = s.w.GetSymKey(MailServerKeyID) 85 if err != nil { 86 return fmt.Errorf("save symmetric key: %s", err) 87 } 88 return nil 89 } 90 91 // Close cleans up before shutdown. 92 func (s *WMailServer) Close() { 93 if s.db != nil { 94 s.db.Close() 95 } 96 } 97 98 // Archive stores the 99 func (s *WMailServer) Archive(env *whisper.Envelope) { 100 key := NewDbKey(env.Expiry-env.TTL, env.Hash()) 101 rawEnvelope, err := rlp.EncodeToBytes(env) 102 if err != nil { 103 log.Error(fmt.Sprintf("rlp.EncodeToBytes failed: %s", err)) 104 } else { 105 err = s.db.Put(key.raw, rawEnvelope, nil) 106 if err != nil { 107 log.Error(fmt.Sprintf("Writing to DB failed: %s", err)) 108 } 109 } 110 } 111 112 // DeliverMail responds with saved messages upon request by the 113 // messages' owner. 114 func (s *WMailServer) DeliverMail(peer *whisper.Peer, request *whisper.Envelope) { 115 if peer == nil { 116 log.Error("Whisper peer is nil") 117 return 118 } 119 120 ok, lower, upper, bloom := s.validateRequest(peer.ID(), request) 121 if ok { 122 s.processRequest(peer, lower, upper, bloom) 123 } 124 } 125 126 func (s *WMailServer) processRequest(peer *whisper.Peer, lower, upper uint32, bloom []byte) []*whisper.Envelope { 127 ret := make([]*whisper.Envelope, 0) 128 var err error 129 var zero common.Hash 130 kl := NewDbKey(lower, zero) 131 ku := NewDbKey(upper+1, zero) // LevelDB is exclusive, while the Whisper API is inclusive 132 i := s.db.NewIterator(&util.Range{Start: kl.raw, Limit: ku.raw}, nil) 133 defer i.Release() 134 135 for i.Next() { 136 var envelope whisper.Envelope 137 err = rlp.DecodeBytes(i.Value(), &envelope) 138 if err != nil { 139 log.Error(fmt.Sprintf("RLP decoding failed: %s", err)) 140 } 141 142 if whisper.BloomFilterMatch(bloom, envelope.Bloom()) { 143 if peer == nil { 144 // used for test purposes 145 ret = append(ret, &envelope) 146 } else { 147 err = s.w.SendP2PDirect(peer, &envelope) 148 if err != nil { 149 log.Error(fmt.Sprintf("Failed to send direct message to peer: %s", err)) 150 return nil 151 } 152 } 153 } 154 } 155 156 err = i.Error() 157 if err != nil { 158 log.Error(fmt.Sprintf("Level DB iterator error: %s", err)) 159 } 160 161 return ret 162 } 163 164 func (s *WMailServer) validateRequest(peerID []byte, request *whisper.Envelope) (bool, uint32, uint32, []byte) { 165 if s.pow > 0.0 && request.PoW() < s.pow { 166 return false, 0, 0, nil 167 } 168 169 f := whisper.Filter{KeySym: s.key} 170 decrypted := request.Open(&f) 171 if decrypted == nil { 172 log.Warn(fmt.Sprintf("Failed to decrypt p2p request")) 173 return false, 0, 0, nil 174 } 175 176 src := crypto.FromECDSAPub(decrypted.Src) 177 if len(src)-len(peerID) == 1 { 178 src = src[1:] 179 } 180 181 // if you want to check the signature, you can do it here. e.g.: 182 // if !bytes.Equal(peerID, src) { 183 if src == nil { 184 log.Warn(fmt.Sprintf("Wrong signature of p2p request")) 185 return false, 0, 0, nil 186 } 187 188 var bloom []byte 189 payloadSize := len(decrypted.Payload) 190 if payloadSize < 8 { 191 log.Warn(fmt.Sprintf("Undersized p2p request")) 192 return false, 0, 0, nil 193 } else if payloadSize == 8 { 194 bloom = whisper.MakeFullNodeBloom() 195 } else if payloadSize < 8+whisper.BloomFilterSize { 196 log.Warn(fmt.Sprintf("Undersized bloom filter in p2p request")) 197 return false, 0, 0, nil 198 } else { 199 bloom = decrypted.Payload[8 : 8+whisper.BloomFilterSize] 200 } 201 202 lower := binary.BigEndian.Uint32(decrypted.Payload[:4]) 203 upper := binary.BigEndian.Uint32(decrypted.Payload[4:8]) 204 return true, lower, upper, bloom 205 }