github.com/decred/dcrlnd@v0.7.6/tor/cmd_info.go (about) 1 package tor 2 3 import ( 4 "errors" 5 "fmt" 6 "strings" 7 ) 8 9 var ( 10 // ErrServiceNotCreated is used when we want to query info on an onion 11 // service while it's not been created yet. 12 ErrServiceNotCreated = errors.New("onion service hasn't been created") 13 14 // ErrServiceIDMismatch is used when the serviceID the controller has 15 // doesn't match the serviceID the Tor daemon has. 16 ErrServiceIDMismatch = errors.New("onion serviceIDs don't match") 17 18 // ErrNoServiceFound is used when the Tor daemon replies no active 19 // onion services found for the current control connection while we 20 // expect one. 21 ErrNoServiceFound = errors.New("no active service found") 22 ) 23 24 // CheckOnionService checks that the onion service created by the controller 25 // is active. It queries the Tor daemon using the endpoint "onions/current" to 26 // get the current onion service and checks that service ID matches the 27 // activeServiceID. 28 func (c *Controller) CheckOnionService() error { 29 // Check that we have a hidden service created. 30 if c.activeServiceID == "" { 31 return ErrServiceNotCreated 32 } 33 34 // Fetch the onion services that live in current control connection. 35 cmd := "GETINFO onions/current" 36 code, reply, err := c.sendCommand(cmd) 37 38 // Exit early if we got an error or Tor daemon didn't respond success. 39 // TODO(yy): unify the usage of err and code so we could rely on a 40 // single source to change our state. 41 if err != nil || code != success { 42 log.Debugf("query service:%v got err:%v, reply:%v", 43 c.activeServiceID, err, reply) 44 45 return fmt.Errorf("%w: %v", err, reply) 46 } 47 48 // Parse the reply, which should have the following format, 49 // onions/current=serviceID 50 // After parsing, we get a map as, 51 // [onion/current: serviceID] 52 // 53 // NOTE: our current tor controller does NOT support multiple onion 54 // services to be created at the same time, thus we expect the reply to 55 // only contain one serviceID. If multiple serviceIDs are returned, we 56 // would expected the reply to have the following format, 57 // onions/current=serviceID1, serviceID2, serviceID3,... 58 // Thus a new parser is need to parse that reply. 59 resp := parseTorReply(reply) 60 serviceID, ok := resp["onions/current"] 61 if !ok { 62 return ErrNoServiceFound 63 } 64 65 // Check that our active service is indeed the service acknowledged by 66 // Tor daemon. The controller is only aware of a single service but the 67 // Tor daemon might have multiple services registered (for example for 68 // the watchtower as well as the node p2p connections). So we just want 69 // to check that our current controller's ID is contained in the list of 70 // registered services. 71 if !strings.Contains(serviceID, c.activeServiceID) { 72 return fmt.Errorf("%w: controller has: %v, Tor daemon has: %v", 73 ErrServiceIDMismatch, c.activeServiceID, serviceID) 74 } 75 76 return nil 77 }