diff --git a/tor/add_onion.go b/tor/cmd_onion.go similarity index 83% rename from tor/add_onion.go rename to tor/cmd_onion.go index 602a2dab3..1e952428e 100644 --- a/tor/add_onion.go +++ b/tor/cmd_onion.go @@ -244,6 +244,9 @@ func (c *Controller) AddOnion(cfg AddOnionConfig) (*OnionAddr, error) { } } + c.activeServiceID = serviceID + log.Debugf("serviceID:%s added to tor controller", serviceID) + // Finally, we'll return the onion address composed of the service ID, // along with the onion suffix, and the port this onion service can be // reached at externally. @@ -252,3 +255,47 @@ func (c *Controller) AddOnion(cfg AddOnionConfig) (*OnionAddr, error) { Port: cfg.VirtualPort, }, nil } + +// DelOnion tells the Tor daemon to remove an onion service, which satisfies +// either, +// - the onion service was created on the same control connection as the +// "DEL_ONION" command. +// - the onion service was created using the "Detach" flag. +func (c *Controller) DelOnion(serviceID string) error { + log.Debugf("removing serviceID:%s from tor controller", serviceID) + + cmd := fmt.Sprintf("DEL_ONION %s", serviceID) + + // Send the command to create the onion service to the Tor server and + // await its response. + code, _, err := c.sendCommand(cmd) + + // Tor replies with "250 OK" on success, or a 512 if there are an + // invalid number of arguments, or a 552 if it doesn't recognize the + // ServiceID. + switch code { + // Replied 250 OK. + case success: + return nil + + // Replied 512 for invalid arguments. This is most likely that the + // serviceID is not set(empty string). + case invalidNumOfArguments: + return fmt.Errorf("invalid arguments: %w", err) + + // Replied 552, which means either, + // - the serviceID is invalid. + // - the onion service is not owned by the current control connection + // and, + // - the onion service is not a detached service. + // In either case, we will ignore the error and log a warning as there + // not much we can do from the controller side. + case serviceIDNotRecognized: + log.Warnf("removing serviceID:%v not found", serviceID) + return nil + + default: + return fmt.Errorf("undefined response code: %v, err: %w", + code, err) + } +} diff --git a/tor/add_onion_test.go b/tor/cmd_onion_test.go similarity index 100% rename from tor/add_onion_test.go rename to tor/cmd_onion_test.go diff --git a/tor/controller.go b/tor/controller.go index 9ec3f15a8..f2120a92c 100644 --- a/tor/controller.go +++ b/tor/controller.go @@ -20,6 +20,14 @@ const ( // request. success = 250 + // invalidNumOfArguments is the Tor Control response code representing + // there being an invalid number of arguments. + invalidNumOfArguments = 512 + + // serviceIDNotRecognized is the Tor Control response code representing + // the specified ServiceID is not recognized. + serviceIDNotRecognized = 552 + // nonceLen is the length of a nonce generated by either the controller // or the Tor server nonceLen = 32 @@ -100,6 +108,9 @@ type Controller struct { // to connect to the LND node. This is required when the Tor server // runs on another host, otherwise the service will not be reachable. targetIPAddress string + + // activeServiceID is the Onion ServiceID created by ADD_ONION. + activeServiceID string } // NewController returns a new Tor controller that will be able to interact with @@ -142,6 +153,15 @@ func (c *Controller) Stop() error { log.Info("Stopping tor controller") + // Remove the onion service. + if err := c.DelOnion(c.activeServiceID); err != nil { + log.Errorf("DEL_ONION got error: %v", err) + return err + } + + // Reset service ID. + c.activeServiceID = "" + return c.conn.Close() }