func (ms MessageServer) notifyWatch() { var lastNotify, lastMessage int64 notifyTick := time.Tick(time.Duration(ms.NotifyDuration) * time.Second) fetchTick := time.Tick(time.Duration(ms.FetchDuration) * time.Second) expireTick := time.Tick(time.Duration(ms.ExpireDuration) * time.Second) var statTick <-chan time.Time if ms.Stat { // show statistics once a minute statTick = time.Tick(60 * time.Second) } for { select { case <-notifyTick: log.Debugs("Check notification.\n") if lastNotify < lastMessage { // both are zero when started log.Debugs("Notify run started.\n") lastNotify = CurrentTime() ms.NotifyPeers() } case <-fetchTick: log.Debugs("Fetch run started.\n") ms.FetchRun() case <-expireTick: log.Debugs("Expire run started.\n") ms.DB.ExpireFromIndex() case <-statTick: stat.Input <- stat.Show case <-ms.notifyChan: log.Debugs("Notification reason\n") lastMessage = CurrentTime() } } }
// LoadPeers from file. func (ms MessageServer) LoadPeers() { var prePeerlist PeerListEncoded var myPeerURLs []string myPeers := make(PeerList) peersEncoded, err := ioutil.ReadFile(ms.PeerFile) if err != nil { example := make(PeerListEncoded, 1) example[0].PubKey = "Example ed25519 public key encoded in base58" example[0].URL = exampleURL dat, _ := json.MarshalIndent(example, "", " ") _ = dat err := ioutil.WriteFile(ms.PeerFile, dat, 0600) if err != nil { log.Errorf("Could not write peerfile: %s\n", err) return } log.Debugs("Example peerfile written\n") return } err = json.Unmarshal(peersEncoded, &prePeerlist) if err != nil { log.Errorf("Could not read peerfile: %s\n", err) return } if ms.AddToPeer && !ms.HubOnly { myPeerURLs = append(myPeerURLs, ms.URL) } for _, p := range prePeerlist { if p.URL != exampleURL { var tkey [ed25519.PublicKeySize]byte t := utils.B58decode(p.PubKey) copy(tkey[:], t) if *ms.TokenPubKey != tkey || debug { myPeers[tkey] = Peer{ URL: p.URL, PubKey: tkey, } if !p.IsHub { myPeerURLs = append(myPeerURLs, p.URL) } // Create Peer Index File ms.DB.TouchPeer(&tkey) } } } defer ms.setPeerURLs(myPeerURLs) systemPeersMutex.Lock() defer systemPeersMutex.Unlock() systemPeers = myPeers log.Debugs("Peers loaded\n") }
// FetchPost fetches a post from a peer and adds it. func (ms MessageServer) FetchPost(url, auth string, msgID [message.MessageIDSize]byte, expireRequest uint64) error { // Fetch the post proto := repproto.New(ms.SocksProxy, "") data, err := proto.GetSpecificAuth(url, auth, msgID[:]) // data, err := proto.GetSpecific(url, msgID[:]) if err != nil { return err } // Verify and use it signheader, err := message.Base64Message(data).GetSignHeader() if err != nil { log.Debugf("Bad fetch:GetSignHeader: %s\n", err) return err } details, err := message.VerifySignature(*signheader, ms.MinHashCashBits) if err != nil { log.Debugf("Bad fetch:VerifySignature: %s\n", err) return err } constantRecipientPub, MessageID, err := deferVerify(data) if err != nil { log.Debugf("Bad fetch:deferVerify: %s\n", err) return err } if *MessageID != details.MsgID { log.Debugs("Bad fetch:MessageID\n") return ErrBadMessageID } msgStruct := &structs.MessageStruct{ MessageID: *MessageID, ReceiverConstantPubKey: *constantRecipientPub, SignerPub: details.PublicKey, OneTime: false, Sync: false, Hidden: false, ExpireRequest: expireRequest, } if message.KeyIsSync(constantRecipientPub) { msgStruct.Sync = true } if message.KeyIsHidden(constantRecipientPub) { msgStruct.Hidden = true } sigStruct := &structs.SignerStruct{ PublicKey: details.PublicKey, Nonce: details.HashCashNonce, Bits: details.HashCashBits, } sigStruct.MaxMessagesPosted, sigStruct.MaxMessagesRetained, sigStruct.ExpireTarget = ms.calcLimits(details.HashCashBits) return ms.DB.Put(msgStruct, sigStruct, data) }
func getPeers(force bool) error { log.Debugf("Peer update called. Last: %d\n", GlobalConfigVar.PeerUpdate) if GlobalConfigVar.PeerUpdate == 0 || GlobalConfigVar.PeerUpdate < (time.Now().Unix()-PeerUpdateDuration) { log.Debugs("Peer update forced. Prevent by running -peerlist.\n") force = true } if force { log.Debugs("Updating peers.\n") defer log.Debugs("Peer update done.\n") GlobalConfigVar.PeerUpdate = time.Now().Unix() server := OptionsVar.Server if server == "" && GlobalConfigVar.PasteServers != nil && len(GlobalConfigVar.PasteServers) > 0 { // no server defined, we select one servercount := len(GlobalConfigVar.PasteServers) rand.Seed(time.Now().UnixNano()) pos := rand.Int() % servercount server = GlobalConfigVar.PasteServers[pos] } if server == "" { server = GlobalConfigVar.BootStrapPeer } if server == "" { return ErrNoPeers } proto := repproto.New(OptionsVar.Socksserver, OptionsVar.Server, GlobalConfigVar.PasteServers...) serverInfo, err := proto.ID(server) if err != nil { return ErrNoPeers } if serverInfo.Peers != nil && len(serverInfo.Peers) > 0 { GlobalConfigVar.PasteServers = serverInfo.Peers GlobalConfigVar.MinHashCash = serverInfo.MinHashCashBits err := WriteConfigFile(GlobalConfigVar) if err != nil { log.Errorf("Error writing config-file: %s\n", err) } } } return nil }
// Fetch returns a single message. func (ms MessageServer) Fetch(w http.ResponseWriter, r *http.Request) { var messageID *[message.MessageIDSize]byte w.Header().Set("Content-Type", "text/plain; charset=us-ascii") getValues := r.URL.Query() if getValues != nil { if v, ok := getValues["messageid"]; ok { t := utils.B58decode(v[0]) if len(t) < message.MessageIDSize || len(t) > message.MessageIDSize { io.WriteString(w, "ERROR: Bad parameter\n") return } messageID = new([message.MessageIDSize]byte) copy(messageID[:], t) } if ms.HubOnly { if v, ok := getValues["auth"]; ok { err := ms.AuthenticatePeer(v[0]) if err != nil { io.WriteString(w, fmt.Sprintf("Error: %s", err)) return } } else { io.WriteString(w, "ERROR: Missing param\n") return } } } if messageID == nil { io.WriteString(w, "ERROR: Missing parameter\n") return } data, err := ms.DB.Fetch(messageID) if err != nil { log.Debugf("Fetch: %s\n", err) io.WriteString(w, "ERROR: No data") return } io.WriteString(w, "SUCCESS: Data follows\n") _, err = w.Write(data) if err != nil { log.Debugf("Write: %s\n", err) return } log.Debugs("Fetch OK\n") if ms.Stat { stat.Input <- stat.Fetch } return }
func (store Store) expireSigners() { //s.signers = fileback.NewRoundRobin(dir+signerDir, structs.SignerStructSize, 10, ' ', []byte("\n"), workers) // We keep signer history, last 10 entries now := time.Now().Unix() indices := store.signers.Indices() if indices == nil { log.Debugs("ExpireFS: No signers\n") return } for _, signer := range indices { last := store.signers.Index(signer).LastChange(0) if last != 0 && last < now-int64(FSExpire) { store.signers.Index(signer).Truncate() // We don't delete for real, otherwise the signer would become reusable. log.Debugf("ExpireFS: Signer truncated %x\n", signer) } else { log.Debugf("ExpireFS: Signer remains %x\n", signer) } } }
func (store Store) expireKeyIndices() { //s.keyindex = fileback.NewRolling(dir+keyindexDir, structs.MessageStructSize, 2048, ' ', []byte("\n"), workers) // Maximum 2048 entries per file now := time.Now().Unix() indices := store.keyindex.Indices() if indices == nil { log.Debugs("ExpireFS: No Key Indices\n") return } for _, keylist := range indices { entry := store.signers.Index(keylist) last := entry.LastChange(0) if last != 0 && last < now-int64(FSExpire) { // go forward lastEntry := entry.Entries() - 1 if lastEntry >= 0 { last := entry.LastChange(lastEntry) if last < now-int64(FSExpire) { log.Debugf("ExpireFS: Key Index complete delete %x\n", keylist) entry.Delete() // Key lists are deleted } else { maxEntries := entry.MaxEntries() deleteBefore := int64(-1) LastLoop: for pos := lastEntry; pos > 0; pos = pos - maxEntries { // Search the first entry that has expired last := entry.LastChange(pos) if last != 0 && last < now-int64(FSExpire) { break LastLoop } deleteBefore = pos - 1 // This will actually skip the last expired object } if deleteBefore >= 0 { log.Debugf("ExpireFS: Key Index delete before %d %x\n", deleteBefore, keylist) entry.DeleteBefore(deleteBefore) } else { log.Debugf("ExpireFS: Key Index no delete match %x\n", keylist) } } } } else { log.Debugf("ExpireFS: Key Index remains %x\n", keylist) } } }
// AuthenticatePeer verifies an existing authStr and matches it to the known peers. func (ms MessageServer) AuthenticatePeer(authStr string) error { var counterSig [keyproof.ProofTokenSignedSize]byte var auth []byte if len(authStr) > keyproof.ProofTokenSignedMax { return fmt.Errorf("Bad param") } auth = utils.B58decode(authStr) if len(auth) > keyproof.ProofTokenSignedSize { return fmt.Errorf("Bad param") } copy(counterSig[:], auth) ok, timestamp := keyproof.VerifyCounterSig(&counterSig, ms.TokenPubKey) if !ok { log.Debugs("List:Auth no verify\n") return fmt.Errorf("Authentication failed: No match") } now := CurrentTime() if enforceTimeOuts && (timestamp < now-ms.MaxAuthTokenAge-ms.MaxTimeSkew || timestamp > now+ms.MaxAuthTokenAge+ms.MaxTimeSkew) { return fmt.Errorf("Authentication failed: Timeout") } return nil }
// ProcessPost verifies and adds a post to the database. func (ms MessageServer) ProcessPost(postdata io.ReadCloser, oneTime bool, expireRequest uint64) string { data, err := utils.MaxRead(ms.MaxPostSize, postdata) if err != nil { return "ERROR: Message too big\n" } if len(data) < ms.MinPostSize { return "ERROR: Message too small\n" } signheader, err := message.Base64Message(data).GetSignHeader() if err != nil { log.Debugf("Post:GetSignHeader: %s\n", err) return "ERROR: Sign Header\n" } details, err := message.VerifySignature(*signheader, ms.MinHashCashBits) if err != nil { log.Debugf("Post:VerifySignature: %s\n", err) return "ERROR: HashCash\n" } constantRecipientPub, MessageID, err := deferVerify(data) if err != nil { log.Debugf("Post:deferVerify: %s\n", err) return "ERROR: Verify\n" } if *MessageID != details.MsgID { log.Debugs("Post:MessageID\n") return "ERROR: MessageID\n" } msgStruct := &structs.MessageStruct{ MessageID: *MessageID, ReceiverConstantPubKey: *constantRecipientPub, SignerPub: details.PublicKey, OneTime: oneTime, Sync: false, Hidden: false, ExpireRequest: expireRequest, } if !oneTime { if message.KeyIsSync(constantRecipientPub) { msgStruct.Sync = true } } else { msgStruct.Sync = false } if message.KeyIsHidden(constantRecipientPub) { msgStruct.Hidden = true } sigStruct := &structs.SignerStruct{ PublicKey: details.PublicKey, Nonce: details.HashCashNonce, Bits: details.HashCashBits, } sigStruct.MaxMessagesPosted, sigStruct.MaxMessagesRetained, sigStruct.ExpireTarget = ms.calcLimits(details.HashCashBits) _, _ = msgStruct, sigStruct ms.RandomSleep() // err = ms.DB.Put(msgStruct, sigStruct, data) err = ms.DB.PutNotify(msgStruct, sigStruct, data, ms.notifyChan) ms.RandomSleep() if err != nil { log.Debugf("Post:MessageDB: %s\n", err) return fmt.Sprintf("ERROR: %s\n", err) } log.Debugf("Post:Added: %x\n", MessageID[:12]) if ms.Stat { stat.Input <- stat.Post } return "SUCCESS: Connection close\n" }
// GetKeyIndex returns the index for a key. func (ms MessageServer) GetKeyIndex(w http.ResponseWriter, r *http.Request) { var pubKey *message.Curve25519Key var auth []byte start := int64(0) count := int64(10) w.Header().Set("Content-Type", "text/plain; charset=us-ascii") getValues := r.URL.Query() if getValues != nil { if v, ok := getValues["start"]; ok { t, err := strconv.Atoi(v[0]) if err == nil { start = int64(t) } } if v, ok := getValues["count"]; ok { t, err := strconv.Atoi(v[0]) if err == nil { count = int64(t) if count > ms.MaxIndexKey { count = ms.MaxIndexKey } } } if v, ok := getValues["key"]; ok { if len(v[0]) > message.Curve25519KeySize*10 { io.WriteString(w, "ERROR: Bad param\n") return } t := utils.B58decode(v[0]) if len(t) != message.Curve25519KeySize { io.WriteString(w, "ERROR: Bad param\n") return } pubKey = new(message.Curve25519Key) copy(pubKey[:], t) if v, ok := getValues["auth"]; ok { if len(v[0]) > keyauth.AnswerSize*10 { io.WriteString(w, "ERROR: Bad param\n") return } auth = utils.B58decode(v[0]) if len(auth) != keyauth.AnswerSize { io.WriteString(w, "ERROR: Bad param\n") return } } } else { io.WriteString(w, "ERROR: Missing param\n") return } } else { io.WriteString(w, "ERROR: Missing param\n") return } if pubKey == nil { io.WriteString(w, "ERROR: Missing param\n") return } if message.KeyIsHidden(pubKey) { if auth == nil { log.Debugs("List:Auth missing\n") io.WriteString(w, "ERROR: Authentication required\n") return } answer := [keyauth.AnswerSize]byte{} copy(answer[:], auth) now := uint64(time.Now().Unix() + ms.TimeSkew) if !keyauth.VerifyTime(&answer, now, ms.TimeGrace) { log.Debugs("List:Auth timeout\n") io.WriteString(w, "ERROR: Authentication failed: Timeout\n") return } privK := [32]byte(*ms.authPrivKey) testK := [32]byte(*pubKey) if !keyauth.Verify(&answer, &privK, &testK) { log.Debugs("List:Auth no verify\n") io.WriteString(w, "ERROR: Authentication failed: No Match\n") return } } messages, found, err := ms.DB.GetIndex(pubKey, start, count) if err != nil && err != fileback.ErrNoMore { log.Debugf("List:GetIndex: %s\n", err) log.Debugf("List:GetIndex: Key %x\n", pubKey) io.WriteString(w, "ERROR: List failed\n") return } io.WriteString(w, "SUCCESS: Data follows\n") for _, msg := range messages { io.WriteString(w, "IDX: "+strings.Trim(string(msg), " \t\n\r")+"\n") } if int64(found) < count { io.WriteString(w, "CMD: Exceeded\n") } else { io.WriteString(w, "CMD: Continue\n") } }