diff --git a/core/contact.go b/core/contact.go index 708f744..a6725cc 100644 --- a/core/contact.go +++ b/core/contact.go @@ -33,6 +33,8 @@ type Contact struct { connChannel chan *protocol.OpenConnection connClosedChannel chan struct{} + outboundConnAuthKnown bool + conversation *Conversation } @@ -247,10 +249,9 @@ func (c *Contact) connectOutbound(ctx context.Context, connChannel chan *protoco oc, err := protocol.Open(conn, hostname[0:16]) if err != nil { log.Printf("Contact connection protocol failure: %s", err) - oc.Close() - // XXX These failures are probably not worth retrying so much, - // but that would need to be investigated. For now, just do the - // same backoff behavior. + if oc != nil { + oc.Close() + } if err := connector.Backoff(ctx); err != nil { return } @@ -303,6 +304,14 @@ func (c *Contact) setConnection(conn *protocol.OpenConnection) error { return fmt.Errorf("Connection hostname %s doesn't match contact hostname %s when assigning connection", conn.OtherHostname, c.data.Hostname[0:16]) } + if conn.Client && !c.outboundConnAuthKnown && !c.data.Request.Pending { + log.Printf("Outbound connection to contact says we are not a known contact for %v", c) + // XXX Should move to rejected status, stop attempting connections. + c.mutex.Unlock() + conn.Close() + return fmt.Errorf("Outbound connection says we are not a known contact") + } + if c.connection != nil { if c.shouldReplaceConnection(conn) { // XXX Signal state change for connection loss? @@ -324,17 +333,17 @@ func (c *Contact) setConnection(conn *protocol.OpenConnection) error { log.Printf("Assigned connection %v to contact %v", c.connection, c) if c.data.Request.Pending { - if conn.Client { - // XXX Need to check knownContact flag in authentication and implicit accept also + if conn.Client && !c.outboundConnAuthKnown { // Outbound connection for contact request; send request message // XXX hardcoded channel ID log.Printf("Sending outbound contact request to %v", c) conn.SendContactRequest(5, c.data.Request.MyNickname, c.data.Request.Message) } else { - // Inbound connection for contact request; implicitly accept request - // and continue as contact - log.Printf("Contact request implicitly accepted by incoming connection for contact %v", c) - c.requestAccepted() + // Inbound connection or outbound connection with a positive + // 'isKnownContact' response implicitly accepts the contact request + // and can continue as a contact + log.Printf("Contact request implicitly accepted by contact %v", c) + c.updateContactRequest("Accepted") } } else { c.status = ricochet.Contact_ONLINE @@ -428,22 +437,72 @@ func (c *Contact) shouldReplaceConnection(conn *protocol.OpenConnection) bool { return false } -// Assumes mutex is held, and assumes the caller will send the UPDATE event -func (c *Contact) requestAccepted() { +// Update the status of a contact request from a protocol event. Returns +// true if the contact request channel should remain open. +func (c *Contact) UpdateContactRequest(status string) bool { + c.mutex.Lock() + defer c.mutex.Unlock() + + if !c.data.Request.Pending { + return false + } + + re := c.updateContactRequest(status) + + event := ricochet.ContactEvent{ + Type: ricochet.ContactEvent_UPDATE, + Subject: &ricochet.ContactEvent_Contact{ + Contact: c.Data(), + }, + } + c.events.Publish(event) + + return re +} + +// Same as above, but assumes the mutex is already held and that the caller +// will send an UPDATE event +func (c *Contact) updateContactRequest(status string) bool { config := c.core.Config.OpenWrite() - c.data.Request = ConfigContactRequest{} + now := time.Now().Format(time.RFC3339) + // Whether to keep the channel open + var re bool + + switch status { + case "Pending": + c.data.Request.WhenDelivered = now + re = true + + case "Accepted": + c.data.Request = ConfigContactRequest{} + if c.connection != nil { + c.status = ricochet.Contact_ONLINE + } else { + c.status = ricochet.Contact_UNKNOWN + } + + case "Rejected": + c.data.Request.WhenRejected = now + + case "Error": + c.data.Request.WhenRejected = now + c.data.Request.RemoteError = "error occurred" + + default: + log.Printf("Unknown contact request status '%s'", status) + } + config.Contacts[strconv.Itoa(c.id)] = c.data config.Save() - - if c.connection != nil { - c.status = ricochet.Contact_ONLINE - } else { - c.status = ricochet.Contact_UNKNOWN - } + return re } // XXX also will go away during protocol API rework -func (c *Contact) OnConnectionAuthenticated(conn *protocol.OpenConnection) { +func (c *Contact) OnConnectionAuthenticated(conn *protocol.OpenConnection, knownContact bool) { + // XXX this is ugly + if conn.Client { + c.outboundConnAuthKnown = knownContact + } c.connChannel <- conn } diff --git a/core/protocol.go b/core/protocol.go index 05151ef..6bc3924 100644 --- a/core/protocol.go +++ b/core/protocol.go @@ -73,7 +73,7 @@ func (pc *ProtocolConnection) OnAuthenticationProof(channelID int32, publicKey [ log.Printf("protocol: OnAuthenticationProof, result: %v, contact: %v", result, pc.Contact) if result && pc.Contact != nil { - pc.Contact.OnConnectionAuthenticated(pc.Conn) + pc.Contact.OnConnectionAuthenticated(pc.Conn, true) } } @@ -87,16 +87,9 @@ func (pc *ProtocolConnection) OnAuthenticationResult(channelID int32, result boo return } - // XXX Contact request, removed cases - if !isKnownContact { - log.Printf("protocol: Outbound connection authentication to %s succeeded, but we are not a known contact", pc.Conn.OtherHostname) - pc.Conn.Close() - return - } - log.Printf("protocol: Outbound connection to %s authenticated", pc.Conn.OtherHostname) if pc.Contact != nil { - pc.Contact.OnConnectionAuthenticated(pc.Conn) + pc.Contact.OnConnectionAuthenticated(pc.Conn, isKnownContact) } } @@ -105,6 +98,15 @@ func (pc *ProtocolConnection) OnContactRequest(channelID int32, nick string, mes } func (pc *ProtocolConnection) OnContactRequestAck(channelID int32, status string) { + if !pc.Conn.Client || pc.Contact == nil { + pc.Conn.CloseChannel(channelID) + return + } + + if !pc.Contact.UpdateContactRequest(status) { + pc.Conn.CloseChannel(channelID) + return + } } // Managing Channels