func sessionConnectionHandler(conn net.Conn) { if err := conn.SetDeadline(time.Now().Add(messageTimeout)); err != nil { if debug { log.Println("Weird error setting deadline:", err, "on", conn.RemoteAddr()) } return } message, err := protocol.ReadMessage(conn) if err != nil { return } switch msg := message.(type) { case protocol.JoinSessionRequest: ses := findSession(string(msg.Key)) if debug { log.Println(conn.RemoteAddr(), "session lookup", ses) } if ses == nil { protocol.WriteMessage(conn, protocol.ResponseNotFound) conn.Close() return } if !ses.AddConnection(conn) { if debug { log.Println("Failed to add", conn.RemoteAddr(), "to session", ses) } protocol.WriteMessage(conn, protocol.ResponseAlreadyConnected) conn.Close() return } if err := protocol.WriteMessage(conn, protocol.ResponseSuccess); err != nil { if debug { log.Println("Failed to send session join response to ", conn.RemoteAddr(), "for", ses) } return } if err := conn.SetDeadline(time.Time{}); err != nil { if debug { log.Println("Weird error setting deadline:", err, "on", conn.RemoteAddr()) } conn.Close() return } default: if debug { log.Println("Unexpected message from", conn.RemoteAddr(), message) } protocol.WriteMessage(conn, protocol.ResponseUnexpectedMessage) conn.Close() } }
func JoinSession(invitation protocol.SessionInvitation) (net.Conn, error) { addr := net.JoinHostPort(net.IP(invitation.Address).String(), strconv.Itoa(int(invitation.Port))) conn, err := net.Dial("tcp", addr) if err != nil { return nil, err } request := protocol.JoinSessionRequest{ Key: invitation.Key, } conn.SetDeadline(time.Now().Add(10 * time.Second)) err = protocol.WriteMessage(conn, request) if err != nil { return nil, err } message, err := protocol.ReadMessage(conn) if err != nil { return nil, err } conn.SetDeadline(time.Time{}) switch msg := message.(type) { case protocol.Response: if msg.Code != 0 { return nil, fmt.Errorf("Incorrect response code %d: %s", msg.Code, msg.Message) } return conn, nil default: return nil, fmt.Errorf("protocol error: expecting response got %v", msg) } }
func GetInvitationFromRelay(uri *url.URL, id syncthingprotocol.DeviceID, certs []tls.Certificate) (protocol.SessionInvitation, error) { if uri.Scheme != "relay" { return protocol.SessionInvitation{}, fmt.Errorf("Unsupported relay scheme:", uri.Scheme) } conn, err := tls.Dial("tcp", uri.Host, configForCerts(certs)) if err != nil { return protocol.SessionInvitation{}, err } conn.SetDeadline(time.Now().Add(10 * time.Second)) if err := performHandshakeAndValidation(conn, uri); err != nil { return protocol.SessionInvitation{}, err } defer conn.Close() request := protocol.ConnectRequest{ ID: id[:], } if err := protocol.WriteMessage(conn, request); err != nil { return protocol.SessionInvitation{}, err } message, err := protocol.ReadMessage(conn) if err != nil { return protocol.SessionInvitation{}, err } switch msg := message.(type) { case protocol.Response: return protocol.SessionInvitation{}, fmt.Errorf("Incorrect response code %d: %s", msg.Code, msg.Message) case protocol.SessionInvitation: if debug { l.Debugln("Received invitation", msg, "via", conn.LocalAddr()) } ip := net.IP(msg.Address) if len(ip) == 0 || ip.IsUnspecified() { msg.Address = conn.RemoteAddr().(*net.TCPAddr).IP[:] } return msg, nil default: return protocol.SessionInvitation{}, fmt.Errorf("protocol error: unexpected message %v", msg) } }
func (c *ProtocolClient) join() error { if err := protocol.WriteMessage(c.conn, protocol.JoinRelayRequest{}); err != nil { return err } message, err := protocol.ReadMessage(c.conn) if err != nil { return err } switch msg := message.(type) { case protocol.Response: if msg.Code != 0 { return fmt.Errorf("Incorrect response code %d: %s", msg.Code, msg.Message) } default: return fmt.Errorf("protocol error: expecting response got %v", msg) } return nil }
func protocolConnectionHandler(tcpConn net.Conn, config *tls.Config) { conn := tls.Server(tcpConn, config) err := conn.Handshake() if err != nil { if debug { log.Println("Protocol connection TLS handshake:", conn.RemoteAddr(), err) } conn.Close() return } state := conn.ConnectionState() if (!state.NegotiatedProtocolIsMutual || state.NegotiatedProtocol != protocol.ProtocolName) && debug { log.Println("Protocol negotiation error") } certs := state.PeerCertificates if len(certs) != 1 { if debug { log.Println("Certificate list error") } conn.Close() return } id := syncthingprotocol.NewDeviceID(certs[0].Raw) messages := make(chan interface{}) errors := make(chan error, 1) outbox := make(chan interface{}) // Read messages from the connection and send them on the messages // channel. When there is an error, send it on the error channel and // return. Applies also when the connection gets closed, so the pattern // below is to close the connection on error, then wait for the error // signal from messageReader to exit. go messageReader(conn, messages, errors) pingTicker := time.NewTicker(pingInterval) timeoutTicker := time.NewTimer(networkTimeout) joined := false for { select { case message := <-messages: timeoutTicker.Reset(networkTimeout) if debug { log.Printf("Message %T from %s", message, id) } switch msg := message.(type) { case protocol.JoinRelayRequest: outboxesMut.RLock() _, ok := outboxes[id] outboxesMut.RUnlock() if ok { protocol.WriteMessage(conn, protocol.ResponseAlreadyConnected) if debug { log.Println("Already have a peer with the same ID", id, conn.RemoteAddr()) } conn.Close() continue } outboxesMut.Lock() outboxes[id] = outbox outboxesMut.Unlock() joined = true protocol.WriteMessage(conn, protocol.ResponseSuccess) case protocol.ConnectRequest: requestedPeer := syncthingprotocol.DeviceIDFromBytes(msg.ID) outboxesMut.RLock() peerOutbox, ok := outboxes[requestedPeer] outboxesMut.RUnlock() if !ok { if debug { log.Println(id, "is looking for", requestedPeer, "which does not exist") } protocol.WriteMessage(conn, protocol.ResponseNotFound) conn.Close() continue } ses := newSession(sessionLimiter, globalLimiter) go ses.Serve() clientInvitation := ses.GetClientInvitationMessage(requestedPeer) serverInvitation := ses.GetServerInvitationMessage(id) if err := protocol.WriteMessage(conn, clientInvitation); err != nil { if debug { log.Printf("Error sending invitation from %s to client: %s", id, err) } conn.Close() continue } peerOutbox <- serverInvitation if debug { log.Println("Sent invitation from", id, "to", requestedPeer) } conn.Close() case protocol.Pong: // Nothing default: if debug { log.Printf("Unknown message %s: %T", id, message) } protocol.WriteMessage(conn, protocol.ResponseUnexpectedMessage) conn.Close() } case err := <-errors: if debug { log.Printf("Closing connection %s: %s", id, err) } close(outbox) // Potentially closing a second time. conn.Close() // Only delete the outbox if the client is joined, as it might be // a lookup request coming from the same client. if joined { outboxesMut.Lock() delete(outboxes, id) outboxesMut.Unlock() } return case <-pingTicker.C: if !joined { if debug { log.Println(id, "didn't join within", pingInterval) } conn.Close() continue } if err := protocol.WriteMessage(conn, protocol.Ping{}); err != nil { if debug { log.Println(id, err) } conn.Close() } case <-timeoutTicker.C: // We should receive a error from the reader loop, which will cause // us to quit this loop. if debug { log.Printf("%s timed out", id) } conn.Close() case msg := <-outbox: if debug { log.Printf("Sending message %T to %s", msg, id) } if err := protocol.WriteMessage(conn, msg); err != nil { if debug { log.Println(id, err) } conn.Close() } } } }
func (c *ProtocolClient) Serve() { c.stop = make(chan struct{}) c.stopped = make(chan struct{}) defer close(c.stopped) if err := c.connect(); err != nil { if debug { l.Debugln("Relay connect:", err) } return } if debug { l.Debugln(c, "connected", c.conn.RemoteAddr()) } if err := c.join(); err != nil { c.conn.Close() l.Infoln("Relay join:", err) return } if err := c.conn.SetDeadline(time.Time{}); err != nil { l.Infoln("Relay set deadline:", err) return } if debug { l.Debugln(c, "joined", c.conn.RemoteAddr(), "via", c.conn.LocalAddr()) } defer c.cleanup() c.mut.Lock() c.connected = true c.mut.Unlock() messages := make(chan interface{}) errors := make(chan error, 1) go messageReader(c.conn, messages, errors) timeout := time.NewTimer(c.timeout) for { select { case message := <-messages: timeout.Reset(c.timeout) if debug { log.Printf("%s received message %T", c, message) } switch msg := message.(type) { case protocol.Ping: if err := protocol.WriteMessage(c.conn, protocol.Pong{}); err != nil { l.Infoln("Relay write:", err) return } if debug { l.Debugln(c, "sent pong") } case protocol.SessionInvitation: ip := net.IP(msg.Address) if len(ip) == 0 || ip.IsUnspecified() { msg.Address = c.conn.RemoteAddr().(*net.TCPAddr).IP[:] } c.Invitations <- msg default: l.Infoln("Relay: protocol error: unexpected message %v", msg) return } case <-c.stop: if debug { l.Debugln(c, "stopping") } return case err := <-errors: l.Infoln("Relay received:", err) return case <-timeout.C: if debug { l.Debugln(c, "timed out") } return } } }