mirror of
https://github.com/btcsuite/btcd.git
synced 2025-03-13 03:17:07 +01:00
Sending RPC requests through unix sockets (#2168)
Sending RPC requests through unix sockets
This commit is contained in:
parent
b2eec96bea
commit
139669066c
5 changed files with 190 additions and 1 deletions
1
go.mod
1
go.mod
|
@ -27,6 +27,7 @@ require (
|
|||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
golang.org/x/net v0.24.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
|
|
4
go.sum
4
go.sum
|
@ -91,12 +91,15 @@ golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnf
|
|||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc h1:zK/HqS5bZxDptfPJNq8v7vJfXtkU7r9TLIoSr1bXaP4=
|
||||
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -108,6 +111,7 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed h1:J22ig1FUekjjkmZUM7pTKixYm8DvrYsvrBZdunYeIuQ=
|
||||
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
|
|
41
rpcclient/examples/bitcoincoreunixsocket/README.md
Normal file
41
rpcclient/examples/bitcoincoreunixsocket/README.md
Normal file
|
@ -0,0 +1,41 @@
|
|||
Bitcoin Core HTTP POST Over Unix Socket Example
|
||||
==============================
|
||||
|
||||
This example shows how to use the rpcclient package to connect to a Bitcoin
|
||||
Core RPC server using HTTP POST mode over a Unix Socket with TLS disabled
|
||||
and gets the current block count.
|
||||
|
||||
## Running the Example
|
||||
|
||||
The first step is to use `go get` to download and install the rpcclient package:
|
||||
|
||||
```bash
|
||||
$ go get github.com/btcsuite/btcd/rpcclient
|
||||
```
|
||||
|
||||
Next, modify the `main.go` source to specify the correct RPC username and
|
||||
password for the RPC server:
|
||||
|
||||
```Go
|
||||
User: "yourrpcuser",
|
||||
Pass: "yourrpcpass",
|
||||
```
|
||||
|
||||
As Bitcoin Core supports only TCP/IP, we'll redirect RPC requests from the
|
||||
Unix Socket to Bitcoin Core. For this example, we'll use the `socat` command:
|
||||
|
||||
```bash
|
||||
$ socat -d UNIX-LISTEN:"my-unix-socket-path",fork TCP:"host-address"
|
||||
$ socat -d UNIX-LISTEN:/tmp/test.XXXX,fork TCP:localhost:8332
|
||||
```
|
||||
|
||||
Finally, navigate to the example's directory and run it with:
|
||||
|
||||
```bash
|
||||
$ cd $GOPATH/src/github.com/btcsuite/btcd/rpcclient/examples/bitcoincorehttp
|
||||
$ go run *.go
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
This example is licensed under the [copyfree](http://copyfree.org) ISC License.
|
39
rpcclient/examples/bitcoincoreunixsocket/main.go
Normal file
39
rpcclient/examples/bitcoincoreunixsocket/main.go
Normal file
|
@ -0,0 +1,39 @@
|
|||
// Copyright (c) 2014-2017 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/btcsuite/btcd/rpcclient"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Connect to local bitcoin core RPC server using HTTP POST mode over a
|
||||
// Unix Socket.
|
||||
connCfg := &rpcclient.ConnConfig{
|
||||
// For unix sockets, use unix:// + "your unix socket path".
|
||||
Host: "unix:///tmp/test.XXXX",
|
||||
User: "yourrpcuser",
|
||||
Pass: "yourrpcpass",
|
||||
HTTPPostMode: true, // Bitcoin core only supports HTTP POST mode.
|
||||
DisableTLS: true, // Bitcoin core does not provide TLS by default.
|
||||
}
|
||||
|
||||
// Notice the notification parameter is nil since notifications are
|
||||
// not supported in HTTP POST mode.
|
||||
client, err := rpcclient.New(connCfg, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer client.Shutdown()
|
||||
|
||||
// Get the current block count.
|
||||
blockCount, err := client.GetBlockCount()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Printf("Block count: %d", blockCount)
|
||||
}
|
|
@ -7,6 +7,7 @@ package rpcclient
|
|||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
|
@ -20,6 +21,8 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
@ -756,7 +759,6 @@ func (c *Client) handleSendPostMessage(jReq *jsonRequest) {
|
|||
if !c.config.DisableTLS {
|
||||
protocol = "https"
|
||||
}
|
||||
url := protocol + "://" + c.config.Host
|
||||
|
||||
var (
|
||||
err, lastErr error
|
||||
|
@ -764,6 +766,24 @@ func (c *Client) handleSendPostMessage(jReq *jsonRequest) {
|
|||
httpResponse *http.Response
|
||||
)
|
||||
|
||||
parsedAddr, err := ParseAddressString(c.config.Host)
|
||||
if err != nil {
|
||||
jReq.responseChan <- &Response{
|
||||
err: fmt.Errorf("failed to parse address %v", err),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var url string
|
||||
switch parsedAddr.Network(){
|
||||
case "unix", "unixpacket":
|
||||
// Using a placeholder URL because a non-empty URL is required.
|
||||
// The Unix domain socket is specified in the DialContext.
|
||||
url = protocol + "://unix"
|
||||
default:
|
||||
url = protocol + "://" + c.config.Host
|
||||
}
|
||||
|
||||
tries := 10
|
||||
for i := 0; i < tries; i++ {
|
||||
var httpReq *http.Request
|
||||
|
@ -1331,10 +1351,17 @@ func newHTTPClient(config *ConnConfig) (*http.Client, error) {
|
|||
}
|
||||
}
|
||||
|
||||
parsedAddr, err := ParseAddressString(config.Host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client := http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: proxyFunc,
|
||||
TLSClientConfig: tlsConfig,
|
||||
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
|
||||
return net.Dial(parsedAddr.Network(), parsedAddr.String())
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -1698,3 +1725,80 @@ func (c *Client) Send() error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseAddressString converts an address in string format to a net.Addr that is
|
||||
// compatible with btcd. UDP is not supported because btcd needs reliable
|
||||
// connections. We accept a custom function to resolve any TCP addresses so
|
||||
// that caller is able control exactly how resolution is performed.
|
||||
func ParseAddressString(strAddress string) (net.Addr, error) {
|
||||
var parsedNetwork, parsedAddr string
|
||||
|
||||
// Addresses can either be in network://address:port format,
|
||||
// network:address:port, address:port, or just port. We want to support
|
||||
// all possible types.
|
||||
if strings.Contains(strAddress, "://") {
|
||||
parts := strings.Split(strAddress, "://")
|
||||
parsedNetwork, parsedAddr = parts[0], parts[1]
|
||||
} else if strings.Contains(strAddress, ":") {
|
||||
parts := strings.Split(strAddress, ":")
|
||||
parsedNetwork = parts[0]
|
||||
parsedAddr = strings.Join(parts[1:], ":")
|
||||
} else {
|
||||
parsedAddr = strAddress
|
||||
}
|
||||
|
||||
// Only TCP and Unix socket addresses are valid. We can't use IP or
|
||||
// UDP only connections for anything we do in lnd.
|
||||
switch parsedNetwork {
|
||||
case "unix", "unixpacket":
|
||||
return net.ResolveUnixAddr(parsedNetwork, parsedAddr)
|
||||
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
return net.ResolveTCPAddr(parsedNetwork, verifyPort(parsedAddr))
|
||||
|
||||
case "ip", "ip4", "ip6", "udp", "udp4", "udp6", "unixgram":
|
||||
return nil, fmt.Errorf("only TCP or unix socket "+
|
||||
"addresses are supported: %s", parsedAddr)
|
||||
|
||||
default:
|
||||
// We'll now possibly use the local host short circuit
|
||||
// or parse out an all interfaces listen.
|
||||
addrWithPort := verifyPort(strAddress)
|
||||
|
||||
// Otherwise, we'll attempt to resolve the host.
|
||||
return net.ResolveTCPAddr("tcp", addrWithPort)
|
||||
}
|
||||
}
|
||||
|
||||
// verifyPort makes sure that an address string has both a host and a port.
|
||||
// If the address is just a port, then we'll assume that the user is using the
|
||||
// short cut to specify a localhost:port address.
|
||||
func verifyPort(address string) string {
|
||||
host, port, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
// If the address itself is just an integer, then we'll assume
|
||||
// that we're mapping this directly to a localhost:port pair.
|
||||
// This ensures we maintain the legacy behavior.
|
||||
if _, err := strconv.Atoi(address); err == nil {
|
||||
return net.JoinHostPort("localhost", address)
|
||||
}
|
||||
|
||||
// Otherwise, we'll assume that the address just failed to
|
||||
// attach its own port, so we'll leave it as is. In the
|
||||
// case of IPv6 addresses, if the host is already surrounded by
|
||||
// brackets, then we'll avoid using the JoinHostPort function,
|
||||
// since it will always add a pair of brackets.
|
||||
if strings.HasPrefix(address, "[") {
|
||||
return address
|
||||
}
|
||||
return net.JoinHostPort(address, "")
|
||||
}
|
||||
|
||||
// In the case that both the host and port are empty, we'll use the
|
||||
// an empty port.
|
||||
if host == "" && port == "" {
|
||||
return ":"
|
||||
}
|
||||
|
||||
return address
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue