mirror of
https://github.com/go-i2p/onramp.git
synced 2025-08-20 02:45:28 -04:00
Compare commits
29 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
48e87d68a9 | ||
![]() |
3f93dec9db | ||
![]() |
daeaa91183 | ||
![]() |
ed7e6f56ae | ||
![]() |
982a496406 | ||
![]() |
d7abd8af30 | ||
![]() |
0d05b3c3f7 | ||
![]() |
00aafc2ede | ||
![]() |
8920b7c847 | ||
![]() |
2791849c67 | ||
![]() |
0d9a9ee10a | ||
![]() |
867026628f | ||
![]() |
56eed1e88f | ||
![]() |
c4e23394dc | ||
![]() |
f9fc572938 | ||
![]() |
e184eda3c1 | ||
![]() |
a0a35dd81c | ||
![]() |
7ef5793e5a | ||
![]() |
c2b8660d5e | ||
![]() |
680d5c000b | ||
![]() |
c2b56d35e2 | ||
![]() |
2a53e8347c | ||
![]() |
dcb997a327 | ||
![]() |
d6a5a60d6f | ||
![]() |
c60c135814 | ||
![]() |
8f08410320 | ||
![]() |
cdad2747a7 | ||
![]() |
394c9f9fb8 | ||
![]() |
f32a695cff |
20
.github/workflows/auto-assign.yml
vendored
Normal file
20
.github/workflows/auto-assign.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: Auto Assign
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
pull_request:
|
||||
types: [opened]
|
||||
jobs:
|
||||
run:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: 'Auto-assign issue'
|
||||
uses: pozil/auto-assign-issue@v1
|
||||
with:
|
||||
repo-token:${{ secrets.GITHUB_TOKEN }}
|
||||
assignees: eyedeekay
|
||||
numOfAssignee: 1
|
||||
|
@@ -12,7 +12,7 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/eyedeekay/onramp"
|
||||
"github.com/go-i2p/onramp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -44,7 +44,7 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/eyedeekay/onramp"
|
||||
"github.com/go-i2p/onramp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -135,7 +135,7 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/eyedeekay/onramp"
|
||||
"github.com/go-i2p/onramp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -167,7 +167,7 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/eyedeekay/onramp"
|
||||
"github.com/go-i2p/onramp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
7
Makefile
7
Makefile
@@ -1,13 +1,14 @@
|
||||
|
||||
USER_GH=eyedeekay
|
||||
VERSION=0.33.1
|
||||
USER_GH=go-i2p
|
||||
VERSION=0.33.92
|
||||
CREDIT='contributors to this release: @hkh4n, @eyedeekay'
|
||||
packagename=onramp
|
||||
|
||||
echo: fmt
|
||||
@echo "type make version to do release $(VERSION)"
|
||||
|
||||
version:
|
||||
github-release release -s $(GITHUB_TOKEN) -u $(USER_GH) -r $(packagename) -t v$(VERSION) -d "version $(VERSION)"
|
||||
github-release release -s $(GITHUB_TOKEN) -u $(USER_GH) -r $(packagename) -t v$(VERSION) -d "version $(VERSION) $(CREDIT)"
|
||||
|
||||
del:
|
||||
github-release delete -s $(GITHUB_TOKEN) -u $(USER_GH) -r $(packagename) -t v$(VERSION)
|
||||
|
38
README.md
38
README.md
@@ -1,6 +1,8 @@
|
||||
onramp
|
||||
======
|
||||
|
||||
[](https://goreportcard.com/report/github.com/go-i2p/onramp)
|
||||
|
||||
High-level, easy-to-use listeners and clients for I2P and onion URL's from Go.
|
||||
Provides only the most widely-used functions in a basic way. It expects nothing
|
||||
from the users, an otherwise empty instance of the structs will listen and dial
|
||||
@@ -12,9 +14,9 @@ This means that hidden services will maintain their identities, and that clients
|
||||
will always have the same return addresses. If you don't want this behavior,
|
||||
make sure to delete the "keystore" when your app closes or when your application
|
||||
needs to cycle keys by calling the `Garlic.DeleteKeys()` or `Onion.DeleteKeys()`
|
||||
function. For more information, check out the [godoc](http://pkg.go.dev/github.com/eyedeekay/onramp).
|
||||
function. For more information, check out the [godoc](http://pkg.go.dev/github.com/go-i2p/onramp).
|
||||
|
||||
- **[Source Code](https://github.com/eyedeekay/onramp)**
|
||||
- **[Source Code](https://github.com/go-i2p/onramp)**
|
||||
|
||||
STATUS: This project is maintained. I will respond to issues, pull requests, and feature requests within a few days.
|
||||
|
||||
@@ -38,7 +40,7 @@ package main
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/eyedeekay/onramp"
|
||||
"github.com/go-i2p/onramp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -64,7 +66,7 @@ package main
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/eyedeekay/onramp"
|
||||
"github.com/go-i2p/onramp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -77,3 +79,31 @@ func main() {
|
||||
defer listener.Close()
|
||||
}
|
||||
```
|
||||
|
||||
## Verbosity ##
|
||||
Logging can be enabled and configured using the DEBUG_I2P environment variable. By default, logging is disabled.
|
||||
|
||||
There are three available log levels:
|
||||
|
||||
- Debug
|
||||
```shell
|
||||
export DEBUG_I2P=debug
|
||||
```
|
||||
- Warn
|
||||
```shell
|
||||
export DEBUG_I2P=warn
|
||||
```
|
||||
- Error
|
||||
```shell
|
||||
export DEBUG_I2P=error
|
||||
```
|
||||
|
||||
If DEBUG_I2P is set to an unrecognized variable, it will fall back to "debug".
|
||||
|
||||
## Contributing
|
||||
|
||||
See CONTRIBUTING.md for more information.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT license, see LICENSE for more information.
|
||||
|
76
common.go
76
common.go
@@ -9,24 +9,36 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/go-i2p/logger"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var log = logger.GetGoI2PLogger()
|
||||
|
||||
//go:generate go run -tags gen ./gen.go
|
||||
|
||||
// GetJoinedWD returns the working directory joined with the given path.
|
||||
func GetJoinedWD(dir string) (string, error) {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
//log.WithError(err).Error("Failed to get working directory")
|
||||
return "", err
|
||||
}
|
||||
jwd := filepath.Join(wd, dir)
|
||||
ajwd, err := filepath.Abs(jwd)
|
||||
if err != nil {
|
||||
//log.WithError(err).WithField("path", jwd).Error("Failed to get absolute path")
|
||||
return "", err
|
||||
}
|
||||
if _, err := os.Stat(ajwd); err != nil {
|
||||
os.MkdirAll(ajwd, 0755)
|
||||
//log.WithField("path", ajwd).Debug("Directory does not exist, creating")
|
||||
if err := os.MkdirAll(ajwd, 0755); err != nil {
|
||||
//log.WithError(err).WithField("path", ajwd).Error("Failed to create directory")
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
//log.WithField("path", ajwd).Debug("Successfully got joined working directory")
|
||||
return ajwd, nil
|
||||
}
|
||||
|
||||
@@ -53,68 +65,112 @@ var TLS_KEYSTORE_PATH = tlsdefault
|
||||
// path is not set, it returns the default path. If the path does
|
||||
// not exist, it creates it.
|
||||
func I2PKeystorePath() (string, error) {
|
||||
log.WithField("path", I2P_KEYSTORE_PATH).Debug("Checking I2P keystore path")
|
||||
if _, err := os.Stat(I2P_KEYSTORE_PATH); err != nil {
|
||||
log.WithField("path", I2P_KEYSTORE_PATH).Debug("I2P keystore directory does not exist, creating")
|
||||
err := os.MkdirAll(I2P_KEYSTORE_PATH, 0755)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("path", I2P_KEYSTORE_PATH).Error("Failed to create I2P keystore directory")
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
log.WithField("path", I2P_KEYSTORE_PATH).Debug("I2P keystore path verified")
|
||||
return I2P_KEYSTORE_PATH, nil
|
||||
}
|
||||
|
||||
// DeleteI2PKeyStore deletes the I2P Keystore.
|
||||
func DeleteI2PKeyStore() error {
|
||||
return os.RemoveAll(I2P_KEYSTORE_PATH)
|
||||
log.WithField("path", I2P_KEYSTORE_PATH).Debug("Attempting to delete I2P keystore")
|
||||
err := os.RemoveAll(I2P_KEYSTORE_PATH)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("path", I2P_KEYSTORE_PATH).Error("Failed to delete I2P keystore")
|
||||
return err
|
||||
}
|
||||
log.WithField("path", I2P_KEYSTORE_PATH).Debug("Successfully deleted I2P keystore")
|
||||
return nil
|
||||
//return os.RemoveAll(I2P_KEYSTORE_PATH)
|
||||
}
|
||||
|
||||
// TorKeystorePath returns the path to the Onion Keystore. If the
|
||||
// path is not set, it returns the default path. If the path does
|
||||
// not exist, it creates it.
|
||||
func TorKeystorePath() (string, error) {
|
||||
log.WithField("path", ONION_KEYSTORE_PATH).Debug("Checking Tor keystore path")
|
||||
if _, err := os.Stat(ONION_KEYSTORE_PATH); err != nil {
|
||||
log.WithField("path", ONION_KEYSTORE_PATH).Debug("Tor keystore directory does not exist, creating")
|
||||
err := os.MkdirAll(ONION_KEYSTORE_PATH, 0755)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("path", ONION_KEYSTORE_PATH).Error("Failed to create Tor keystore directory")
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
log.WithField("path", ONION_KEYSTORE_PATH).Debug("Tor keystore path verified")
|
||||
return ONION_KEYSTORE_PATH, nil
|
||||
}
|
||||
|
||||
// DeleteTorKeyStore deletes the Onion Keystore.
|
||||
func DeleteTorKeyStore() error {
|
||||
return os.RemoveAll(ONION_KEYSTORE_PATH)
|
||||
log.WithField("path", ONION_KEYSTORE_PATH).Debug("Attempting to delete Tor keystore")
|
||||
err := os.RemoveAll(ONION_KEYSTORE_PATH)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("path", ONION_KEYSTORE_PATH).Error("Failed to delete Tor keystore")
|
||||
return err
|
||||
}
|
||||
log.WithField("path", ONION_KEYSTORE_PATH).Debug("Successfully deleted Tor keystore")
|
||||
return nil
|
||||
//return os.RemoveAll(ONION_KEYSTORE_PATH)
|
||||
}
|
||||
|
||||
// TLSKeystorePath returns the path to the TLS Keystore. If the
|
||||
// path is not set, it returns the default path. If the path does
|
||||
// not exist, it creates it.
|
||||
func TLSKeystorePath() (string, error) {
|
||||
log.WithField("path", TLS_KEYSTORE_PATH).Debug("Checking TLS keystore path")
|
||||
if _, err := os.Stat(TLS_KEYSTORE_PATH); err != nil {
|
||||
log.WithField("path", TLS_KEYSTORE_PATH).Debug("TLS keystore directory does not exist, creating")
|
||||
err := os.MkdirAll(TLS_KEYSTORE_PATH, 0755)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("path", TLS_KEYSTORE_PATH).Error("Failed to create TLS keystore directory")
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
log.WithField("path", TLS_KEYSTORE_PATH).Debug("TLS keystore path verified")
|
||||
return TLS_KEYSTORE_PATH, nil
|
||||
}
|
||||
|
||||
// DeleteTLSKeyStore deletes the TLS Keystore.
|
||||
func DeleteTLSKeyStore() error {
|
||||
return os.RemoveAll(TLS_KEYSTORE_PATH)
|
||||
log.WithField("path", TLS_KEYSTORE_PATH).Debug("Attempting to delete TLS keystore")
|
||||
err := os.RemoveAll(TLS_KEYSTORE_PATH)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("path", TLS_KEYSTORE_PATH).Error("Failed to delete TLS keystore")
|
||||
return err
|
||||
}
|
||||
log.WithField("path", TLS_KEYSTORE_PATH).Debug("Successfully deleted TLS keystore")
|
||||
return nil
|
||||
//return os.RemoveAll(TLS_KEYSTORE_PATH)
|
||||
}
|
||||
|
||||
// Dial returns a connection for the given network and address.
|
||||
// network is ignored. If the address ends in i2p, it returns an I2P connection.
|
||||
// if the address ends in anything else, it returns a Tor connection.
|
||||
func Dial(network, addr string) (net.Conn, error) {
|
||||
log.WithFields(logrus.Fields{
|
||||
"network": network,
|
||||
"address": addr,
|
||||
}).Debug("Attempting to dial")
|
||||
|
||||
url, err := url.Parse(addr)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("address", addr).Error("Failed to parse address")
|
||||
return nil, err
|
||||
}
|
||||
hostname := url.Hostname()
|
||||
if strings.HasSuffix(hostname, ".i2p") {
|
||||
log.WithField("hostname", hostname).Debug("Using I2P connection for .i2p address")
|
||||
return DialGarlic(network, addr)
|
||||
}
|
||||
log.WithField("hostname", hostname).Debug("Using Tor connection for non-i2p address")
|
||||
return DialOnion(network, addr)
|
||||
}
|
||||
|
||||
@@ -123,19 +179,31 @@ func Dial(network, addr string) (net.Conn, error) {
|
||||
// if network is tor or onion, it returns an Onion listener.
|
||||
// if keys ends with ".i2p", it returns an I2P listener.
|
||||
func Listen(network, keys string) (net.Listener, error) {
|
||||
log.WithFields(logrus.Fields{
|
||||
"network": network,
|
||||
"keys": keys,
|
||||
}).Debug("Attempting to create listener")
|
||||
|
||||
if network == "i2p" || network == "garlic" {
|
||||
log.Debug("Creating I2P listener based on network type")
|
||||
return ListenGarlic(network, keys)
|
||||
}
|
||||
if network == "tor" || network == "onion" {
|
||||
log.Debug("Creating Tor listener based on network type")
|
||||
return ListenOnion(network, keys)
|
||||
}
|
||||
|
||||
url, err := url.Parse(keys)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("keys", keys).Error("Failed to parse keys URL")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hostname := url.Hostname()
|
||||
if strings.HasSuffix(hostname, ".i2p") {
|
||||
log.WithField("hostname", hostname).Debug("Creating I2P listener based on .i2p hostname")
|
||||
return ListenGarlic(network, keys)
|
||||
}
|
||||
log.WithField("hostname", hostname).Debug("Creating Tor listener for non-i2p hostname")
|
||||
return ListenOnion(network, keys)
|
||||
}
|
||||
|
235
garlic.go
235
garlic.go
@@ -7,14 +7,14 @@ import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/eyedeekay/i2pkeys"
|
||||
"github.com/eyedeekay/sam3"
|
||||
"github.com/go-i2p/i2pkeys"
|
||||
"github.com/go-i2p/sam3"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Garlic is a ready-made I2P streaming manager. Once initialized it always
|
||||
@@ -75,7 +75,7 @@ func (g *Garlic) String() string {
|
||||
default:
|
||||
r = g.ServiceKeys.Address.DestHash().Hash()
|
||||
}
|
||||
return g.addrString(r) //r //strings.TrimLeft(strings.TrimRight(r, "\n"), "\n") //strings.TrimSpace(r)
|
||||
return g.addrString(r) // r //strings.TrimLeft(strings.TrimRight(r, "\n"), "\n") //strings.TrimSpace(r)
|
||||
}
|
||||
|
||||
func (g *Garlic) getName() string {
|
||||
@@ -101,27 +101,35 @@ func (g *Garlic) getOptions() []string {
|
||||
|
||||
func (g *Garlic) samSession() (*sam3.SAM, error) {
|
||||
if g.SAM == nil {
|
||||
log.WithField("address", g.getAddr()).Debug("Creating new SAM session")
|
||||
var err error
|
||||
g.SAM, err = sam3.NewSAM(g.getAddr())
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to create SAM session")
|
||||
return nil, fmt.Errorf("onramp samSession: %v", err)
|
||||
}
|
||||
log.Debug("SAM session created successfully")
|
||||
}
|
||||
return g.SAM, nil
|
||||
}
|
||||
|
||||
func (g *Garlic) setupStreamSession() (*sam3.StreamSession, error) {
|
||||
if g.StreamSession == nil {
|
||||
log.WithField("name", g.getName()).Debug("Setting up stream session")
|
||||
var err error
|
||||
g.ServiceKeys, err = g.Keys()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to get keys for stream session")
|
||||
return nil, fmt.Errorf("onramp setupStreamSession: %v", err)
|
||||
}
|
||||
log.WithField("address", g.ServiceKeys.Address.Base32()).Debug("Creating stream session with keys")
|
||||
log.Println("Creating stream session with keys:", g.ServiceKeys.Address.Base32())
|
||||
g.StreamSession, err = g.SAM.NewStreamSession(g.getName(), *g.ServiceKeys, g.getOptions())
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to create stream session")
|
||||
return nil, fmt.Errorf("onramp setupStreamSession: %v", err)
|
||||
}
|
||||
log.Debug("Stream session created successfully")
|
||||
return g.StreamSession, nil
|
||||
}
|
||||
return g.StreamSession, nil
|
||||
@@ -129,78 +137,131 @@ func (g *Garlic) setupStreamSession() (*sam3.StreamSession, error) {
|
||||
|
||||
func (g *Garlic) setupDatagramSession() (*sam3.DatagramSession, error) {
|
||||
if g.DatagramSession == nil {
|
||||
log.WithField("name", g.getName()).Debug("Setting up datagram session")
|
||||
var err error
|
||||
g.ServiceKeys, err = g.Keys()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to get keys for datagram session")
|
||||
return nil, fmt.Errorf("onramp setupDatagramSession: %v", err)
|
||||
}
|
||||
log.WithField("address", g.ServiceKeys.Address.Base32()).Debug("Creating datagram session with keys")
|
||||
log.Println("Creating datagram session with keys:", g.ServiceKeys.Address.Base32())
|
||||
g.DatagramSession, err = g.SAM.NewDatagramSession(g.getName(), *g.ServiceKeys, g.getOptions(), 0)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to create datagram session")
|
||||
return nil, fmt.Errorf("onramp setupDatagramSession: %v", err)
|
||||
}
|
||||
log.Debug("Datagram session created successfully")
|
||||
return g.DatagramSession, nil
|
||||
}
|
||||
|
||||
log.Debug("Using existing datagram session")
|
||||
return g.DatagramSession, nil
|
||||
}
|
||||
|
||||
// NewListener returns a net.Listener for the Garlic structure's I2P keys.
|
||||
// accepts a variable list of arguments, arguments after the first one are ignored.
|
||||
func (g *Garlic) NewListener(n, addr string) (net.Listener, error) {
|
||||
return g.Listen(n)
|
||||
log.WithFields(logrus.Fields{
|
||||
"network": n,
|
||||
"address": addr,
|
||||
"name": g.getName(),
|
||||
}).Debug("Creating new listener")
|
||||
listener, err := g.Listen(n)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to create listener")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debug("Successfully created listener")
|
||||
return listener, nil
|
||||
// return g.Listen(n)
|
||||
}
|
||||
|
||||
// Listen returns a net.Listener for the Garlic structure's I2P keys.
|
||||
// accepts a variable list of arguments, arguments after the first one are ignored.
|
||||
func (g *Garlic) Listen(args ...string) (net.Listener, error) {
|
||||
return g.OldListen(args...)
|
||||
log.WithFields(logrus.Fields{
|
||||
"args": args,
|
||||
"name": g.getName(),
|
||||
}).Debug("Setting up listener")
|
||||
|
||||
listener, err := g.OldListen(args...)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to create listener")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debug("Successfully created listener")
|
||||
return listener, nil
|
||||
// return g.OldListen(args...)
|
||||
}
|
||||
|
||||
// OldListen returns a net.Listener for the Garlic structure's I2P keys.
|
||||
// accepts a variable list of arguments, arguments after the first one are ignored.
|
||||
func (g *Garlic) OldListen(args ...string) (net.Listener, error) {
|
||||
log.WithField("args", args).Debug("Starting OldListen")
|
||||
if len(args) > 0 {
|
||||
if args[0] == "tcp" || args[0] == "tcp6" || args[0] == "st" || args[0] == "st6" {
|
||||
protocol := args[0]
|
||||
log.WithField("protocol", protocol).Debug("Checking protocol type")
|
||||
// if args[0] == "tcp" || args[0] == "tcp6" || args[0] == "st" || args[0] == "st6" {
|
||||
if protocol == "tcp" || protocol == "tcp6" || protocol == "st" || protocol == "st6" {
|
||||
log.Debug("Using TCP stream listener")
|
||||
return g.ListenStream()
|
||||
} else if args[0] == "udp" || args[0] == "udp6" || args[0] == "dg" || args[0] == "dg6" {
|
||||
//} else if args[0] == "udp" || args[0] == "udp6" || args[0] == "dg" || args[0] == "dg6" {
|
||||
} else if protocol == "udp" || protocol == "udp6" || protocol == "dg" || protocol == "dg6" {
|
||||
log.Debug("Using UDP datagram listener")
|
||||
pk, err := g.ListenPacket()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to create packet listener")
|
||||
return nil, err
|
||||
}
|
||||
log.Debug("Successfully created datagram session")
|
||||
return pk.(*sam3.DatagramSession), nil
|
||||
}
|
||||
|
||||
}
|
||||
log.Debug("No protocol specified, defaulting to stream listener")
|
||||
return g.ListenStream()
|
||||
}
|
||||
|
||||
// Listen returns a net.Listener for the Garlic structure's I2P keys.
|
||||
func (g *Garlic) ListenStream() (net.Listener, error) {
|
||||
log.Debug("Setting up stream listener")
|
||||
var err error
|
||||
if g.SAM, err = g.samSession(); err != nil {
|
||||
log.WithError(err).Error("Failed to create SAM session for stream listener")
|
||||
return nil, fmt.Errorf("onramp NewGarlic: %v", err)
|
||||
}
|
||||
if g.StreamSession, err = g.setupStreamSession(); err != nil {
|
||||
log.WithError(err).Error("Failed to setup stream session")
|
||||
return nil, fmt.Errorf("onramp Listen: %v", err)
|
||||
}
|
||||
if g.StreamListener == nil {
|
||||
log.Debug("Creating new stream listener")
|
||||
g.StreamListener, err = g.StreamSession.Listen()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to create stream listener")
|
||||
return nil, fmt.Errorf("onramp Listen: %v", err)
|
||||
}
|
||||
log.Debug("Stream listener created successfully")
|
||||
}
|
||||
return g.StreamListener, nil
|
||||
}
|
||||
|
||||
// ListenPacket returns a net.PacketConn for the Garlic structure's I2P keys.
|
||||
func (g *Garlic) ListenPacket() (net.PacketConn, error) {
|
||||
log.Debug("Setting up packet connection")
|
||||
var err error
|
||||
if g.SAM, err = g.samSession(); err != nil {
|
||||
log.WithError(err).Error("Failed to create SAM session for packet connection")
|
||||
return nil, fmt.Errorf("onramp NewGarlic: %v", err)
|
||||
}
|
||||
if g.DatagramSession, err = g.setupDatagramSession(); err != nil {
|
||||
log.WithError(err).Error("Failed to setup datagram session")
|
||||
return nil, fmt.Errorf("onramp Listen: %v", err)
|
||||
}
|
||||
log.Debug("Packet connection successfully established")
|
||||
return g.DatagramSession, nil
|
||||
}
|
||||
|
||||
@@ -208,23 +269,33 @@ func (g *Garlic) ListenPacket() (net.PacketConn, error) {
|
||||
// which also uses TLS either for additional encryption, authentication,
|
||||
// or browser-compatibility.
|
||||
func (g *Garlic) ListenTLS(args ...string) (net.Listener, error) {
|
||||
log.WithField("args", args).Debug("Starting TLS listener")
|
||||
listener, err := g.Listen(args...)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to create base listener")
|
||||
return nil, err
|
||||
}
|
||||
cert, err := g.TLSKeys()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to get TLS keys")
|
||||
return nil, fmt.Errorf("onramp ListenTLS: %v", err)
|
||||
}
|
||||
if len(args) > 0 {
|
||||
if args[0] == "tcp" || args[0] == "tcp6" || args[0] == "st" || args[0] == "st6" {
|
||||
protocol := args[0]
|
||||
log.WithField("protocol", protocol).Debug("Creating TLS listener for protocol")
|
||||
|
||||
// if args[0] == "tcp" || args[0] == "tcp6" || args[0] == "st" || args[0] == "st6" {
|
||||
if protocol == "tcp" || protocol == "tcp6" || protocol == "st" || protocol == "st6" {
|
||||
log.Debug("Creating TLS stream listener")
|
||||
return tls.NewListener(
|
||||
g.StreamListener,
|
||||
&tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
},
|
||||
), nil
|
||||
} else if args[0] == "udp" || args[0] == "udp6" || args[0] == "dg" || args[0] == "dg6" {
|
||||
//} else if args[0] == "udp" || args[0] == "udp6" || args[0] == "dg" || args[0] == "dg6" {
|
||||
} else if protocol == "udp" || protocol == "udp6" || protocol == "dg" || protocol == "dg6" {
|
||||
log.Debug("Creating TLS datagram listener")
|
||||
return tls.NewListener(
|
||||
g.DatagramSession,
|
||||
&tls.Config{
|
||||
@@ -234,8 +305,10 @@ func (g *Garlic) ListenTLS(args ...string) (net.Listener, error) {
|
||||
}
|
||||
|
||||
} else {
|
||||
log.Debug("No protocol specified, using stream listener")
|
||||
g.StreamListener = listener.(*sam3.StreamListener)
|
||||
}
|
||||
log.Debug("Successfully created TLS listener")
|
||||
return tls.NewListener(
|
||||
g.StreamListener,
|
||||
&tls.Config{
|
||||
@@ -246,76 +319,143 @@ func (g *Garlic) ListenTLS(args ...string) (net.Listener, error) {
|
||||
|
||||
// Dial returns a net.Conn for the Garlic structure's I2P keys.
|
||||
func (g *Garlic) Dial(net, addr string) (net.Conn, error) {
|
||||
log.WithFields(logrus.Fields{
|
||||
"network": net,
|
||||
"address": addr,
|
||||
}).Debug("Attempting to dial")
|
||||
if !strings.Contains(addr, ".i2p") {
|
||||
log.Debug("Non-I2P address detected, returning null connection")
|
||||
return &NullConn{}, nil
|
||||
}
|
||||
var err error
|
||||
if g.SAM, err = g.samSession(); err != nil {
|
||||
log.WithError(err).Error("Failed to create SAM session")
|
||||
return nil, fmt.Errorf("onramp NewGarlic: %v", err)
|
||||
}
|
||||
if g.StreamSession, err = g.setupStreamSession(); err != nil {
|
||||
log.WithError(err).Error("Failed to setup stream session")
|
||||
return nil, fmt.Errorf("onramp Dial: %v", err)
|
||||
}
|
||||
return g.StreamSession.Dial(net, addr)
|
||||
log.Debug("Attempting to establish connection")
|
||||
conn, err := g.StreamSession.Dial(net, addr)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to establish connection")
|
||||
return nil, err
|
||||
}
|
||||
log.Debug("Successfully established connection")
|
||||
return conn, nil
|
||||
// return g.StreamSession.Dial(net, addr)
|
||||
}
|
||||
|
||||
// DialContext returns a net.Conn for the Garlic structure's I2P keys.
|
||||
func (g *Garlic) DialContext(ctx context.Context, net, addr string) (net.Conn, error) {
|
||||
log.WithFields(logrus.Fields{
|
||||
"network": net,
|
||||
"address": addr,
|
||||
}).Debug("Attempting to dial with context")
|
||||
if !strings.Contains(addr, ".i2p") {
|
||||
log.Debug("Non-I2P address detected, returning null connection")
|
||||
return &NullConn{}, nil
|
||||
}
|
||||
var err error
|
||||
if g.SAM, err = g.samSession(); err != nil {
|
||||
log.WithError(err).Error("Failed to create SAM session")
|
||||
return nil, fmt.Errorf("onramp NewGarlic: %v", err)
|
||||
}
|
||||
if g.StreamSession, err = g.setupStreamSession(); err != nil {
|
||||
log.WithError(err).Error("Failed to setup stream session")
|
||||
return nil, fmt.Errorf("onramp Dial: %v", err)
|
||||
}
|
||||
return g.StreamSession.DialContext(ctx, net, addr)
|
||||
log.Debug("Attempting to establish connection with context")
|
||||
conn, err := g.StreamSession.DialContext(ctx, net, addr)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to establish connection")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debug("Successfully established connection")
|
||||
return conn, nil
|
||||
// return g.StreamSession.DialContext(ctx, net, addr)
|
||||
}
|
||||
|
||||
// Close closes the Garlic structure's sessions and listeners.
|
||||
func (g *Garlic) Close() error {
|
||||
log.WithField("name", g.getName()).Debug("Closing Garlic sessions")
|
||||
e1 := g.StreamSession.Close()
|
||||
var err error
|
||||
if e1 != nil {
|
||||
log.WithError(e1).Error("Failed to close stream session")
|
||||
err = fmt.Errorf("onramp Close: %v", e1)
|
||||
} else {
|
||||
log.Debug("Stream session closed successfully")
|
||||
}
|
||||
e2 := g.SAM.Close()
|
||||
if e2 != nil {
|
||||
log.WithError(e2).Error("Failed to close SAM session")
|
||||
err = fmt.Errorf("onramp Close: %v %v", e1, e2)
|
||||
} else {
|
||||
log.Debug("SAM session closed successfully")
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
log.Debug("All sessions closed successfully")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Keys returns the I2PKeys for the Garlic structure. If none
|
||||
// exist, they are created and stored.
|
||||
func (g *Garlic) Keys() (*i2pkeys.I2PKeys, error) {
|
||||
log.WithFields(logrus.Fields{
|
||||
"name": g.getName(),
|
||||
"address": g.getAddr(),
|
||||
}).Debug("Retrieving I2P keys")
|
||||
|
||||
keys, err := I2PKeys(g.getName(), g.getAddr())
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to get I2P keys")
|
||||
return &i2pkeys.I2PKeys{}, fmt.Errorf("onramp Keys: %v", err)
|
||||
}
|
||||
log.Debug("Successfully retrieved I2P keys")
|
||||
return &keys, nil
|
||||
}
|
||||
|
||||
func (g *Garlic) DeleteKeys() error {
|
||||
return DeleteGarlicKeys(g.getName())
|
||||
// return DeleteGarlicKeys(g.getName())
|
||||
log.WithField("name", g.getName()).Debug("Attempting to delete Garlic keys")
|
||||
err := DeleteGarlicKeys(g.getName())
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to delete Garlic keys")
|
||||
}
|
||||
log.Debug("Successfully deleted Garlic keys")
|
||||
return err
|
||||
}
|
||||
|
||||
// NewGarlic returns a new Garlic struct. It is immediately ready to use with
|
||||
// I2P streaming.
|
||||
func NewGarlic(tunName, samAddr string, options []string) (*Garlic, error) {
|
||||
log.WithFields(logrus.Fields{
|
||||
"tunnel_name": tunName,
|
||||
"sam_address": samAddr,
|
||||
"options": options,
|
||||
}).Debug("Creating new Garlic instance")
|
||||
|
||||
g := new(Garlic)
|
||||
g.name = tunName
|
||||
g.addr = samAddr
|
||||
g.opts = options
|
||||
var err error
|
||||
if g.SAM, err = g.samSession(); err != nil {
|
||||
log.WithError(err).Error("Failed to create SAM session")
|
||||
return nil, fmt.Errorf("onramp NewGarlic: %v", err)
|
||||
}
|
||||
if g.StreamSession, err = g.setupStreamSession(); err != nil {
|
||||
log.WithError(err).Error("Failed to setup stream session")
|
||||
return nil, fmt.Errorf("onramp NewGarlic: %v", err)
|
||||
}
|
||||
|
||||
log.Debug("Successfully created new Garlic instance")
|
||||
return g, nil
|
||||
}
|
||||
|
||||
@@ -324,49 +464,74 @@ func NewGarlic(tunName, samAddr string, options []string) (*Garlic, error) {
|
||||
// This is permanent and irreversible, and will change the onion service
|
||||
// address.
|
||||
func DeleteGarlicKeys(tunName string) error {
|
||||
log.WithField("tunnel_name", tunName).Debug("Attempting to delete Garlic keys")
|
||||
keystore, err := I2PKeystorePath()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to get keystore path")
|
||||
return fmt.Errorf("onramp DeleteGarlicKeys: discovery error %v", err)
|
||||
}
|
||||
keyspath := filepath.Join(keystore, tunName+".i2p.private")
|
||||
log.WithField("path", keyspath).Debug("Deleting key file")
|
||||
if err := os.Remove(keyspath); err != nil {
|
||||
log.WithError(err).WithField("path", keyspath).Error("Failed to delete key file")
|
||||
return fmt.Errorf("onramp DeleteGarlicKeys: %v", err)
|
||||
}
|
||||
log.Debug("Successfully deleted Garlic keys")
|
||||
return nil
|
||||
}
|
||||
|
||||
// I2PKeys returns the I2PKeys at the keystore directory for the given
|
||||
// tunnel name. If none exist, they are created and stored.
|
||||
func I2PKeys(tunName, samAddr string) (i2pkeys.I2PKeys, error) {
|
||||
log.WithFields(logrus.Fields{
|
||||
"tunnel_name": tunName,
|
||||
"sam_address": samAddr,
|
||||
}).Debug("Looking up I2P keys")
|
||||
|
||||
keystore, err := I2PKeystorePath()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to get keystore path")
|
||||
return i2pkeys.I2PKeys{}, fmt.Errorf("onramp I2PKeys: discovery error %v", err)
|
||||
}
|
||||
keyspath := filepath.Join(keystore, tunName+".i2p.private")
|
||||
log.WithField("path", keyspath).Debug("Checking for existing keys")
|
||||
info, err := os.Stat(keyspath)
|
||||
if info != nil {
|
||||
if info.Size() == 0 {
|
||||
log.WithField("path", keyspath).Debug("Keystore empty, will regenerate keys")
|
||||
log.Println("onramp I2PKeys: keystore empty, re-generating keys")
|
||||
} else {
|
||||
log.WithField("path", keyspath).Debug("Found existing keystore")
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
log.WithField("path", keyspath).Debug("Keys not found, generating new keys")
|
||||
sam, err := sam3.NewSAM(samAddr)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to create SAM connection")
|
||||
return i2pkeys.I2PKeys{}, fmt.Errorf("onramp I2PKeys: SAM error %v", err)
|
||||
}
|
||||
log.Debug("SAM connection established")
|
||||
keys, err := sam.NewKeys(tunName)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to generate new keys")
|
||||
return i2pkeys.I2PKeys{}, fmt.Errorf("onramp I2PKeys: keygen error %v", err)
|
||||
}
|
||||
log.Debug("New keys generated successfully")
|
||||
if err = i2pkeys.StoreKeys(keys, keyspath); err != nil {
|
||||
log.WithError(err).WithField("path", keyspath).Error("Failed to store generated keys")
|
||||
return i2pkeys.I2PKeys{}, fmt.Errorf("onramp I2PKeys: store error %v", err)
|
||||
}
|
||||
log.WithField("path", keyspath).Debug("Successfully stored new keys")
|
||||
return keys, nil
|
||||
} else {
|
||||
log.WithField("path", keyspath).Debug("Loading existing keys")
|
||||
keys, err := i2pkeys.LoadKeys(keyspath)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("path", keyspath).Error("Failed to load existing keys")
|
||||
return i2pkeys.I2PKeys{}, fmt.Errorf("onramp I2PKeys: load error %v", err)
|
||||
}
|
||||
log.Debug("Successfully loaded existing keys")
|
||||
return keys, nil
|
||||
}
|
||||
}
|
||||
@@ -376,18 +541,35 @@ var garlics map[string]*Garlic
|
||||
// CloseAllGarlic closes all garlics managed by the onramp package. It does not
|
||||
// affect objects instantiated by an app.
|
||||
func CloseAllGarlic() {
|
||||
log.WithField("count", len(garlics)).Debug("Closing all Garlic connections")
|
||||
for i, g := range garlics {
|
||||
log.WithFields(logrus.Fields{
|
||||
"index": i,
|
||||
"name": g.name,
|
||||
}).Debug("Closing Garlic connection")
|
||||
|
||||
log.Println("Closing garlic", g.name)
|
||||
CloseGarlic(i)
|
||||
}
|
||||
log.Debug("All Garlic connections closed")
|
||||
}
|
||||
|
||||
// CloseGarlic closes the Garlic at the given index. It does not affect Garlic
|
||||
// objects instantiated by an app.
|
||||
func CloseGarlic(tunName string) {
|
||||
log.WithField("tunnel_name", tunName).Debug("Attempting to close Garlic connection")
|
||||
g, ok := garlics[tunName]
|
||||
if ok {
|
||||
g.Close()
|
||||
log.Debug("Found Garlic connection, closing")
|
||||
// g.Close()
|
||||
err := g.Close()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Error closing Garlic connection")
|
||||
} else {
|
||||
log.Debug("Successfully closed Garlic connection")
|
||||
}
|
||||
} else {
|
||||
log.Debug("No Garlic connection found for tunnel name")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,11 +581,18 @@ var SAM_ADDR = "127.0.0.1:7656"
|
||||
// corresponding to a structure managed by the onramp library
|
||||
// and not instantiated by an app.
|
||||
func ListenGarlic(network, keys string) (net.Listener, error) {
|
||||
log.WithFields(logrus.Fields{
|
||||
"network": network,
|
||||
"keys": keys,
|
||||
"sam_addr": SAM_ADDR,
|
||||
}).Debug("Creating new Garlic listener")
|
||||
g, err := NewGarlic(keys, SAM_ADDR, OPT_DEFAULTS)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to create new Garlic")
|
||||
return nil, fmt.Errorf("onramp Listen: %v", err)
|
||||
}
|
||||
garlics[keys] = g
|
||||
log.Debug("Successfully created Garlic listener")
|
||||
return g.Listen()
|
||||
}
|
||||
|
||||
@@ -411,10 +600,26 @@ func ListenGarlic(network, keys string) (net.Listener, error) {
|
||||
// corresponding to a structure managed by the onramp library
|
||||
// and not instantiated by an app.
|
||||
func DialGarlic(network, addr string) (net.Conn, error) {
|
||||
log.WithFields(logrus.Fields{
|
||||
"network": network,
|
||||
"address": addr,
|
||||
"sam_addr": SAM_ADDR,
|
||||
}).Debug("Creating new Garlic connection")
|
||||
|
||||
g, err := NewGarlic(addr, SAM_ADDR, OPT_DEFAULTS)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to create new Garlic")
|
||||
return nil, fmt.Errorf("onramp Dial: %v", err)
|
||||
}
|
||||
garlics[addr] = g
|
||||
return g.Dial(network, addr)
|
||||
log.WithField("address", addr).Debug("Attempting to dial")
|
||||
conn, err := g.Dial(network, addr)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to dial connection")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debug("Successfully established Garlic connection")
|
||||
return conn, nil
|
||||
// return g.Dial(network, addr)
|
||||
}
|
||||
|
@@ -1,11 +1,15 @@
|
||||
package onramp
|
||||
|
||||
import "github.com/eyedeekay/sam3"
|
||||
import "github.com/go-i2p/sam3"
|
||||
|
||||
var OPT_DEFAULTS = sam3.Options_Default
|
||||
var OPT_WIDE = sam3.Options_Wide
|
||||
var (
|
||||
OPT_DEFAULTS = sam3.Options_Default
|
||||
OPT_WIDE = sam3.Options_Wide
|
||||
)
|
||||
|
||||
var OPT_HUGE = sam3.Options_Humongous
|
||||
var OPT_LARGE = sam3.Options_Large
|
||||
var OPT_MEDIUM = sam3.Options_Medium
|
||||
var OPT_SMALL = sam3.Options_Small
|
||||
var (
|
||||
OPT_HUGE = sam3.Options_Humongous
|
||||
OPT_LARGE = sam3.Options_Large
|
||||
OPT_MEDIUM = sam3.Options_Medium
|
||||
OPT_SMALL = sam3.Options_Small
|
||||
)
|
||||
|
@@ -6,8 +6,7 @@ package onramp
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"testing"
|
||||
@@ -17,7 +16,10 @@ import (
|
||||
func TestBareGarlic(t *testing.T) {
|
||||
fmt.Println("TestBareGarlic Countdown")
|
||||
Sleep(5)
|
||||
garlic := &Garlic{}
|
||||
garlic, err := NewGarlic("test123", "localhost:7656", OPT_WIDE)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer garlic.Close()
|
||||
listener, err := garlic.ListenTLS()
|
||||
if err != nil {
|
||||
@@ -29,9 +31,14 @@ func TestBareGarlic(t *testing.T) {
|
||||
fmt.Fprintf(w, "Hello, %q", r.URL.Path)
|
||||
})
|
||||
go Serve(listener)
|
||||
Sleep(15)
|
||||
garlic2, err := NewGarlic("test321", "localhost:7656", OPT_WIDE)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer garlic2.Close()
|
||||
Sleep(60)
|
||||
transport := http.Transport{
|
||||
Dial: garlic.Dial,
|
||||
Dial: garlic2.Dial,
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
@@ -45,7 +52,7 @@ func TestBareGarlic(t *testing.T) {
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
fmt.Println(resp.Status)
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
38
go.mod
38
go.mod
@@ -1,34 +1,20 @@
|
||||
module github.com/eyedeekay/onramp
|
||||
module github.com/go-i2p/onramp
|
||||
|
||||
go 1.18
|
||||
go 1.23.3
|
||||
|
||||
toolchain go1.24.1
|
||||
|
||||
require (
|
||||
github.com/cretz/bine v0.2.0
|
||||
github.com/eyedeekay/i2pkeys v0.33.0
|
||||
github.com/eyedeekay/sam3 v0.33.5
|
||||
github.com/libp2p/go-libp2p v0.28.1
|
||||
github.com/libp2p/go-yamux/v4 v4.0.0
|
||||
github.com/multiformats/go-multiaddr v0.9.0
|
||||
github.com/go-i2p/i2pkeys v0.33.10-0.20241113193422-e10de5e60708
|
||||
github.com/go-i2p/logger v0.0.0-20241123010126-3050657e5d0c
|
||||
github.com/go-i2p/sam3 v0.33.9
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
|
||||
github.com/ipfs/go-cid v0.4.1 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
|
||||
github.com/minio/sha256-simd v1.0.1 // indirect
|
||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||
github.com/multiformats/go-base32 v0.1.0 // indirect
|
||||
github.com/multiformats/go-base36 v0.2.0 // indirect
|
||||
github.com/multiformats/go-multibase v0.2.0 // indirect
|
||||
github.com/multiformats/go-multicodec v0.9.0 // indirect
|
||||
github.com/multiformats/go-multihash v0.2.2 // indirect
|
||||
github.com/multiformats/go-multistream v0.4.1 // indirect
|
||||
github.com/multiformats/go-varint v0.0.7 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
golang.org/x/crypto v0.7.0 // indirect
|
||||
golang.org/x/net v0.10.0 // indirect
|
||||
golang.org/x/sys v0.8.0 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
lukechampine.com/blake3 v1.2.1 // indirect
|
||||
github.com/stretchr/testify v1.8.4 // indirect
|
||||
golang.org/x/crypto v0.29.0 // indirect
|
||||
golang.org/x/net v0.31.0 // indirect
|
||||
golang.org/x/sys v0.27.0 // indirect
|
||||
)
|
||||
|
129
go.sum
129
go.sum
@@ -1,129 +1,40 @@
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
|
||||
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
|
||||
github.com/eyedeekay/goSam v0.32.31-0.20210122211817-f97683379f23/go.mod h1:UgJnih/LpotwKriwVPOEa6yPDM2NDdVrKfLtS5DOLPE=
|
||||
github.com/eyedeekay/i2pkeys v0.0.0-20220310052025-204d4ae6dcae/go.mod h1:W9KCm9lqZ+Ozwl3dwcgnpPXAML97+I8Jiht7o5A8YBM=
|
||||
github.com/eyedeekay/i2pkeys v0.33.0 h1:5SzUyWxNjV6AvYv/WaI8J4dSgAfv7/WEps6pDLe2YSs=
|
||||
github.com/eyedeekay/i2pkeys v0.33.0/go.mod h1:W9KCm9lqZ+Ozwl3dwcgnpPXAML97+I8Jiht7o5A8YBM=
|
||||
github.com/eyedeekay/sam3 v0.32.32/go.mod h1:qRA9KIIVxbrHlkj+ZB+OoxFGFgdKeGp1vSgPw26eOVU=
|
||||
github.com/eyedeekay/sam3 v0.33.5 h1:mY2MmEG4W35AOpG/G7DOdAhFZWRwFxlm+NmIoub1Xnw=
|
||||
github.com/eyedeekay/sam3 v0.33.5/go.mod h1:sPtlI4cRm7wD0UywOzLPvvdY1G++vBSK3n+jiIGqWlU=
|
||||
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY=
|
||||
github.com/getlantern/errors v1.0.1/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A=
|
||||
github.com/getlantern/go-socks5 v0.0.0-20171114193258-79d4dd3e2db5/go.mod h1:kGHRXch95rnGLHjER/GhhFiHvfnqNz7KqWD9kGfATHY=
|
||||
github.com/getlantern/golog v0.0.0-20201105130739-9586b8bde3a9/go.mod h1:ZyIjgH/1wTCl+B+7yH1DqrWp6MPJqESmwmEQ89ZfhvA=
|
||||
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7/go.mod h1:dD3CgOrwlzca8ed61CsZouQS5h5jIzkK9ZWrTcf0s+o=
|
||||
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55/go.mod h1:6mmzY2kW1TOOrVy+r41Za2MxXM+hhqTtY3oBKd2AgFA=
|
||||
github.com/getlantern/netx v0.0.0-20190110220209-9912de6f94fd/go.mod h1:wKdY0ikOgzrWSeB9UyBVKPRhjXQ+vTb+BPeJuypUuNE=
|
||||
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
|
||||
github.com/getlantern/ops v0.0.0-20200403153110-8476b16edcd6/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/renameio v1.0.0/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
|
||||
github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s=
|
||||
github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8=
|
||||
github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=
|
||||
github.com/libp2p/go-libp2p v0.28.1 h1:YurK+ZAI6cKfASLJBVFkpVBdl3wGhFi6fusOt725ii8=
|
||||
github.com/libp2p/go-libp2p v0.28.1/go.mod h1:s3Xabc9LSwOcnv9UD4nORnXKTsWkPMkIMB/JIGXVnzk=
|
||||
github.com/libp2p/go-yamux/v4 v4.0.0 h1:+Y80dV2Yx/kv7Y7JKu0LECyVdMXm1VUoko+VQ9rBfZQ=
|
||||
github.com/libp2p/go-yamux/v4 v4.0.0/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4=
|
||||
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
|
||||
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
|
||||
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
||||
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||
github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE=
|
||||
github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI=
|
||||
github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=
|
||||
github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4=
|
||||
github.com/multiformats/go-multiaddr v0.9.0 h1:3h4V1LHIk5w4hJHekMKWALPXErDfz/sggzwC/NcqbDQ=
|
||||
github.com/multiformats/go-multiaddr v0.9.0/go.mod h1:mI67Lb1EeTOYb8GQfL/7wpIZwc46ElrvzhYnoJOmTT0=
|
||||
github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g=
|
||||
github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk=
|
||||
github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg=
|
||||
github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k=
|
||||
github.com/multiformats/go-multihash v0.2.2 h1:Uu7LWs/PmWby1gkj1S1DXx3zyd3aVabA4FiMKn/2tAc=
|
||||
github.com/multiformats/go-multihash v0.2.2/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM=
|
||||
github.com/multiformats/go-multistream v0.4.1 h1:rFy0Iiyn3YT0asivDUIR05leAdwZq3de4741sbiSdfo=
|
||||
github.com/multiformats/go-multistream v0.4.1/go.mod h1:Mz5eykRVAjJWckE2U78c6xqdtyNUEhKSM0Lwar2p77Q=
|
||||
github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8=
|
||||
github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-i2p/i2pkeys v0.0.0-20241108200332-e4f5ccdff8c4/go.mod h1:m5TlHjPZrU5KbTd7Lr+I2rljyC6aJ88HdkeMQXV0U0E=
|
||||
github.com/go-i2p/i2pkeys v0.33.10-0.20241113193422-e10de5e60708 h1:Tiy9IBwi21maNpK74yCdHursJJMkyH7w87tX1nXGWzg=
|
||||
github.com/go-i2p/i2pkeys v0.33.10-0.20241113193422-e10de5e60708/go.mod h1:m5TlHjPZrU5KbTd7Lr+I2rljyC6aJ88HdkeMQXV0U0E=
|
||||
github.com/go-i2p/logger v0.0.0-20241123010126-3050657e5d0c h1:VTiECn3dFEmUlZjto+wOwJ7SSJTHPLyNprQMR5HzIMI=
|
||||
github.com/go-i2p/logger v0.0.0-20241123010126-3050657e5d0c/go.mod h1:te7Zj3g3oMeIl8uBXAgO62UKmZ6m6kHRNg1Mm+X8Hzk=
|
||||
github.com/go-i2p/sam3 v0.33.9 h1:3a+gunx75DFc6jxloUZTAVJbdP6736VU1dy2i7I9fKA=
|
||||
github.com/go-i2p/sam3 v0.33.9/go.mod h1:oDuV145l5XWKKafeE4igJHTDpPwA0Yloz9nyKKh92eo=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/riobard/go-x25519 v0.0.0-20190716001027-10cc4d8d0b33/go.mod h1:BjmVxzAnkLeoEbqHEerI4eSw6ua+RaIB0S4jMV21RAs=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
||||
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20201125231158-b5590deeca9b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
honnef.co/go/tools v0.0.1-2020.1.6/go.mod h1:pyyisuGw24ruLjrr1ddx39WE0y9OooInRzEYLhQB2YY=
|
||||
lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI=
|
||||
lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
161
onion.go
161
onion.go
@@ -7,12 +7,12 @@ import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/cretz/bine/tor"
|
||||
"github.com/cretz/bine/torutil/ed25519"
|
||||
)
|
||||
@@ -66,23 +66,29 @@ func (o *Onion) getDialConf() *tor.DialConf {
|
||||
|
||||
func (o *Onion) getTor() *tor.Tor {
|
||||
if torp == nil {
|
||||
log.Debug("Initializing new Tor instance")
|
||||
var err error
|
||||
torp, err = tor.Start(o.getContext(), o.getStartConf())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.WithError(err).Error("Failed to start Tor")
|
||||
panic(err) // return nil instead?
|
||||
}
|
||||
log.Debug("Tor instance started successfully")
|
||||
}
|
||||
return torp
|
||||
}
|
||||
|
||||
func (o *Onion) getDialer() *tor.Dialer {
|
||||
//if o.Dialer == nil {
|
||||
//var err error
|
||||
//o.Dialer, err
|
||||
// if o.Dialer == nil {
|
||||
// var err error
|
||||
// o.Dialer, err
|
||||
log.Debug("Creating new Tor dialer")
|
||||
dialer, err := o.getTor().Dialer(o.getContext(), o.getDialConf())
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to create Tor dialer")
|
||||
panic(err)
|
||||
}
|
||||
log.Debug("Tor dialer created successfully")
|
||||
//}
|
||||
//return o.Dialer
|
||||
return dialer
|
||||
@@ -106,25 +112,55 @@ func (o *Onion) NewListener(n, addr string) (net.Listener, error) {
|
||||
// address, and will automatically generate a keypair and store it.
|
||||
// the args are always ignored
|
||||
func (o *Onion) Listen(args ...string) (net.Listener, error) {
|
||||
return o.OldListen(args...)
|
||||
log.WithFields(logrus.Fields{
|
||||
"args": args,
|
||||
"name": o.getName(),
|
||||
}).Debug("Setting up Onion listener")
|
||||
listener, err := o.OldListen(args...)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to create Onion listener")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debug("Successfully created Onion listener")
|
||||
return listener, nil
|
||||
// return o.OldListen(args...)
|
||||
}
|
||||
|
||||
// OldListen returns a net.Listener which will listen on an onion
|
||||
// address, and will automatically generate a keypair and store it.
|
||||
// the args are always ignored
|
||||
func (o *Onion) OldListen(args ...string) (net.Listener, error) {
|
||||
return o.getTor().Listen(o.getContext(), o.getListenConf())
|
||||
log.WithField("name", o.getName()).Debug("Creating Tor listener")
|
||||
|
||||
listener, err := o.getTor().Listen(o.getContext(), o.getListenConf())
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to create Tor listener")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debug("Successfully created Tor listener")
|
||||
return listener, nil
|
||||
// return o.getTor().Listen(o.getContext(), o.getListenConf())
|
||||
}
|
||||
|
||||
// ListenTLS returns a net.Listener which will apply TLS encryption
|
||||
// to the onion listener, which will not be decrypted until it reaches
|
||||
// the browser
|
||||
func (o *Onion) ListenTLS(args ...string) (net.Listener, error) {
|
||||
log.WithField("args", args).Debug("Setting up TLS Onion listener")
|
||||
cert, err := o.TLSKeys()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to get TLS keys")
|
||||
return nil, fmt.Errorf("onramp ListenTLS: %v", err)
|
||||
}
|
||||
log.Debug("Creating base Tor listener")
|
||||
l, err := o.getTor().Listen(o.getContext(), o.getListenConf())
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to create base Tor listener")
|
||||
return nil, err
|
||||
}
|
||||
log.Debug("Wrapping Tor listener with TLS")
|
||||
return tls.NewListener(
|
||||
l,
|
||||
&tls.Config{
|
||||
@@ -135,23 +171,56 @@ func (o *Onion) ListenTLS(args ...string) (net.Listener, error) {
|
||||
|
||||
// Dial returns a net.Conn to the given onion address or clearnet address.
|
||||
func (o *Onion) Dial(net, addr string) (net.Conn, error) {
|
||||
return o.getDialer().DialContext(o.getContext(), net, addr)
|
||||
log.WithFields(logrus.Fields{
|
||||
"network": net,
|
||||
"address": addr,
|
||||
}).Debug("Attempting to dial via Tor")
|
||||
conn, err := o.getDialer().DialContext(o.getContext(), net, addr)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to establish Tor connection")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debug("Successfully established Tor connection")
|
||||
return conn, nil
|
||||
// return o.getDialer().DialContext(o.getContext(), net, addr)
|
||||
}
|
||||
|
||||
// Close closes the Onion Service and all associated resources.
|
||||
func (o *Onion) Close() error {
|
||||
return o.getTor().Close()
|
||||
log.WithField("name", o.getName()).Debug("Closing Onion service")
|
||||
|
||||
err := o.getTor().Close()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to close Tor instance")
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debug("Successfully closed Onion service")
|
||||
return nil
|
||||
// return o.getTor().Close()
|
||||
}
|
||||
|
||||
// Keys returns the keys for the Onion
|
||||
func (o *Onion) Keys() (ed25519.KeyPair, error) {
|
||||
return TorKeys(o.getName())
|
||||
log.WithField("name", o.getName()).Debug("Retrieving Onion keys")
|
||||
|
||||
keys, err := TorKeys(o.getName())
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to get Tor keys")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debug("Successfully retrieved Onion keys")
|
||||
return keys, nil
|
||||
// return TorKeys(o.getName())
|
||||
}
|
||||
|
||||
// DeleteKeys deletes the keys at the given key name in the key store.
|
||||
// This is permanent and irreversible, and will change the onion service
|
||||
// address.
|
||||
func (g *Onion) DeleteKeys() error {
|
||||
log.WithField("Onion keys", g.getName()).Debug("Deleting Onion keys")
|
||||
return DeleteOnionKeys(g.getName())
|
||||
}
|
||||
|
||||
@@ -166,36 +235,50 @@ func NewOnion(name string) (*Onion, error) {
|
||||
// name in the key store. If the key already exists, it will be
|
||||
// returned. If it does not exist, it will be generated.
|
||||
func TorKeys(keyName string) (ed25519.KeyPair, error) {
|
||||
log.WithField("key_name", keyName).Debug("Getting Tor keys")
|
||||
keystore, err := TorKeystorePath()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to get keystore path")
|
||||
return nil, fmt.Errorf("onramp OnionKeys: discovery error %v", err)
|
||||
}
|
||||
var keys ed25519.KeyPair
|
||||
keysPath := filepath.Join(keystore, keyName+".tor.private")
|
||||
log.WithField("path", keysPath).Debug("Checking for existing keys")
|
||||
if _, err := os.Stat(keysPath); os.IsNotExist(err) {
|
||||
log.Debug("Generating new Tor keys")
|
||||
tkeys, err := ed25519.GenerateKey(nil)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to generate onion service key, %s", err)
|
||||
log.WithError(err).Error("Failed to generate onion service key")
|
||||
log.Fatal("Unable to generate onion service key")
|
||||
}
|
||||
keys = tkeys
|
||||
|
||||
log.WithField("path", keysPath).Debug("Creating key file")
|
||||
f, err := os.Create(keysPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to create Tor keys file for writing, %s", err)
|
||||
log.WithError(err).Error("Failed to create Tor keys file")
|
||||
log.Fatal("Unable to create Tor keys file for writing")
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = f.Write(tkeys.PrivateKey())
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to write Tor keys to disk, %s", err)
|
||||
log.WithError(err).Error("Failed to write Tor keys to disk")
|
||||
log.Fatal("Unable to write Tor keys to disk")
|
||||
}
|
||||
log.Debug("Successfully generated and stored new keys")
|
||||
} else if err == nil {
|
||||
tkeys, err := ioutil.ReadFile(keysPath)
|
||||
log.Debug("Loading existing Tor keys")
|
||||
tkeys, err := os.ReadFile(keysPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to read Tor keys from disk")
|
||||
log.WithError(err).Error("Failed to read Tor keys from disk")
|
||||
log.Fatal("Unable to read Tor keys from disk")
|
||||
}
|
||||
k := ed25519.FromCryptoPrivateKey(tkeys)
|
||||
keys = k
|
||||
log.Debug("Successfully loaded existing keys")
|
||||
} else {
|
||||
log.Fatalf("Unable to set up Tor keys, %s", err)
|
||||
log.WithError(err).Error("Failed to set up Tor keys")
|
||||
log.Fatal("Unable to set up Tor keys")
|
||||
}
|
||||
return keys, nil
|
||||
}
|
||||
@@ -205,18 +288,34 @@ var onions map[string]*Onion
|
||||
// CloseAllOnion closes all onions managed by the onramp package. It does not
|
||||
// affect objects instantiated by an app.
|
||||
func CloseAllOnion() {
|
||||
log.WithField("count", len(onions)).Debug("Closing all Onion services")
|
||||
for i, g := range onions {
|
||||
log.Println("Closing onion", g.name)
|
||||
log.WithFields(logrus.Fields{
|
||||
"index": i,
|
||||
"name": g.name,
|
||||
}).Debug("Closing Onion service")
|
||||
CloseOnion(i)
|
||||
}
|
||||
|
||||
log.Debug("All Onion services closed")
|
||||
}
|
||||
|
||||
// CloseOnion closes the Onion at the given index. It does not affect Onion
|
||||
// objects instantiated by an app.
|
||||
func CloseOnion(tunName string) {
|
||||
log.WithField("tunnel_name", tunName).Debug("Attempting to close Onion service")
|
||||
|
||||
g, ok := onions[tunName]
|
||||
if ok {
|
||||
g.Close()
|
||||
log.WithField("name", g.name).Debug("Found Onion service, closing")
|
||||
err := g.Close()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to close Onion service")
|
||||
} else {
|
||||
log.Debug("Successfully closed Onion service")
|
||||
}
|
||||
} else {
|
||||
log.Debug("No Onion service found for tunnel name")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,12 +323,28 @@ func CloseOnion(tunName string) {
|
||||
// corresponding to a structure managed by the onramp library
|
||||
// and not instantiated by an app.
|
||||
func ListenOnion(network, keys string) (net.Listener, error) {
|
||||
log.WithFields(logrus.Fields{
|
||||
"network": network,
|
||||
"keys": keys,
|
||||
}).Debug("Creating new Onion listener")
|
||||
|
||||
g, err := NewOnion(keys)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to create new Onion")
|
||||
return nil, fmt.Errorf("onramp Listen: %v", err)
|
||||
}
|
||||
onions[keys] = g
|
||||
return g.Listen()
|
||||
log.Debug("Onion service registered, creating listener")
|
||||
|
||||
listener, err := g.Listen()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to create Onion listener")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debug("Successfully created Onion listener")
|
||||
return listener, nil
|
||||
// return g.Listen()
|
||||
}
|
||||
|
||||
// DialOnion returns a net.Conn for a onion structure's keys
|
||||
@@ -247,13 +362,19 @@ func DialOnion(network, addr string) (net.Conn, error) {
|
||||
// DeleteOnionKeys deletes the key file at the given path as determined by
|
||||
// keystore + tunName.
|
||||
func DeleteOnionKeys(tunName string) error {
|
||||
log.WithField("tunnel_name", tunName).Debug("Attempting to delete Onion keys")
|
||||
|
||||
keystore, err := TorKeystorePath()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to get keystore path")
|
||||
return fmt.Errorf("onramp DeleteOnionKeys: discovery error %v", err)
|
||||
}
|
||||
keyspath := filepath.Join(keystore, tunName+".i2p.private")
|
||||
log.WithError(err).Error("Failed to get keystore path")
|
||||
if err := os.Remove(keyspath); err != nil {
|
||||
log.WithError(err).WithField("path", keyspath).Error("Failed to delete key file")
|
||||
return fmt.Errorf("onramp DeleteOnionKeys: %v", err)
|
||||
}
|
||||
log.Debug("Successfully deleted Onion keys")
|
||||
return nil
|
||||
}
|
||||
|
@@ -6,8 +6,7 @@ package onramp
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"io"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
@@ -15,19 +14,22 @@ import (
|
||||
func TestBareOnion(t *testing.T) {
|
||||
fmt.Println("TestBareOnion Countdown")
|
||||
Sleep(5)
|
||||
onion := &Onion{}
|
||||
onion, err := NewOnion("test123")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer onion.Close()
|
||||
listener, err := onion.ListenTLS()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
log.Println("listener:", listener.Addr().String())
|
||||
//defer listener.Close()
|
||||
// defer listener.Close()
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "Hello, %q", r.URL.Path)
|
||||
})
|
||||
go Serve(listener)
|
||||
Sleep(15)
|
||||
Sleep(60)
|
||||
transport := http.Transport{
|
||||
Dial: onion.Dial,
|
||||
TLSClientConfig: &tls.Config{
|
||||
@@ -42,7 +44,7 @@ func TestBareOnion(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
fmt.Println("Status:", resp.Status)
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
34
proxy.go
34
proxy.go
@@ -2,9 +2,10 @@ package onramp
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type OnrampProxy struct {
|
||||
@@ -21,30 +22,59 @@ type OnrampProxy struct {
|
||||
// and an I2P or Onion address, and it will act as a tunnel to a
|
||||
// listening hidden service somewhere.
|
||||
func (p *OnrampProxy) Proxy(list net.Listener, raddr string) error {
|
||||
log.WithFields(logrus.Fields{
|
||||
"remote_address": raddr,
|
||||
"local_address": list.Addr().String(),
|
||||
}).Debug("Starting proxy service")
|
||||
|
||||
for {
|
||||
log.Debug("Waiting for incoming connection")
|
||||
conn, err := list.Accept()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to accept connection")
|
||||
return err
|
||||
}
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"local_addr": conn.LocalAddr().String(),
|
||||
"remote_addr": conn.RemoteAddr().String(),
|
||||
}).Debug("Accepted new connection, starting proxy routine")
|
||||
|
||||
go p.proxy(conn, raddr)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *OnrampProxy) proxy(conn net.Conn, raddr string) {
|
||||
log.WithFields(logrus.Fields{
|
||||
"remote_address": raddr,
|
||||
"local_addr": conn.LocalAddr().String(),
|
||||
"remote_addr": conn.RemoteAddr().String(),
|
||||
}).Debug("Setting up proxy connection")
|
||||
|
||||
var remote net.Conn
|
||||
var err error
|
||||
checkaddr := strings.Split(raddr, ":")[0]
|
||||
if strings.HasSuffix(checkaddr, ".i2p") {
|
||||
log.Debug("Detected I2P address, using Garlic connection")
|
||||
remote, err = p.Garlic.Dial("tcp", raddr)
|
||||
} else if strings.HasSuffix(checkaddr, ".onion") {
|
||||
log.Debug("Detected Onion address, using Tor connection")
|
||||
remote, err = p.Onion.Dial("tcp", raddr)
|
||||
} else {
|
||||
log.Debug("Using standard TCP connection")
|
||||
remote, err = net.Dial("tcp", raddr)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("cannot dial to remote: %v", err)
|
||||
log.WithError(err).Error("Failed to establish remote connection")
|
||||
log.Fatal("Cannot dial to remote")
|
||||
}
|
||||
defer remote.Close()
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"local_addr": remote.LocalAddr().String(),
|
||||
"remote_addr": remote.RemoteAddr().String(),
|
||||
}).Debug("Remote connection established, starting bidirectional copy")
|
||||
|
||||
go io.Copy(remote, conn)
|
||||
io.Copy(conn, remote)
|
||||
}
|
||||
|
56
tls.go
56
tls.go
@@ -17,6 +17,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/cretz/bine/torutil"
|
||||
)
|
||||
|
||||
@@ -24,11 +26,14 @@ import (
|
||||
// if no TLS keys exist, they will be generated. They will be valid for
|
||||
// the .b32.i2p domain.
|
||||
func (g *Garlic) TLSKeys() (tls.Certificate, error) {
|
||||
log.WithField("name", g.getName()).Debug("Getting TLS keys for Garlic service")
|
||||
keys, err := g.Keys()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to get I2P keys")
|
||||
return tls.Certificate{}, err
|
||||
}
|
||||
base32 := keys.Addr().Base32()
|
||||
log.WithField("base32", base32).Debug("Retrieving TLS certificate for base32 address")
|
||||
return TLSKeys(base32)
|
||||
}
|
||||
|
||||
@@ -36,31 +41,45 @@ func (g *Garlic) TLSKeys() (tls.Certificate, error) {
|
||||
// if no TLS keys exist, they will be generated. They will be valid for
|
||||
// the .onion domain.
|
||||
func (o *Onion) TLSKeys() (tls.Certificate, error) {
|
||||
log.WithField("name", o.getName()).Debug("Getting TLS keys for Onion service")
|
||||
keys, err := o.Keys()
|
||||
if err != nil {
|
||||
return tls.Certificate{}, err
|
||||
}
|
||||
onionService := torutil.OnionServiceIDFromPrivateKey(keys.PrivateKey)
|
||||
onionService := torutil.OnionServiceIDFromPrivateKey(keys)
|
||||
log.WithField("onion_service", onionService).Debug("Retrieving TLS certificate for onion service")
|
||||
return TLSKeys(onionService)
|
||||
}
|
||||
|
||||
// TLSKeys returns the TLS certificate and key for the given hostname.
|
||||
func TLSKeys(tlsHost string) (tls.Certificate, error) {
|
||||
log.WithField("host", tlsHost).Debug("Getting TLS certificate and key")
|
||||
tlsCert := tlsHost + ".crt"
|
||||
tlsKey := tlsHost + ".pem"
|
||||
if err := CreateTLSCertificate(tlsHost); nil != err {
|
||||
log.WithError(err).Error("Failed to create TLS certificate")
|
||||
return tls.Certificate{}, err
|
||||
}
|
||||
tlsKeystorePath, err := TLSKeystorePath()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to get TLS keystore path")
|
||||
return tls.Certificate{}, err
|
||||
}
|
||||
tlsCertPath := filepath.Join(tlsKeystorePath, tlsCert)
|
||||
tlsKeyPath := filepath.Join(tlsKeystorePath, tlsKey)
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"cert_path": tlsCertPath,
|
||||
"key_path": tlsKeyPath,
|
||||
}).Debug("Loading TLS certificate pair")
|
||||
|
||||
cert, err := tls.LoadX509KeyPair(tlsCertPath, tlsKeyPath)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to load TLS certificate pair")
|
||||
return cert, err
|
||||
}
|
||||
|
||||
log.Debug("Successfully loaded TLS certificate and key")
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
@@ -68,6 +87,7 @@ func TLSKeys(tlsHost string) (tls.Certificate, error) {
|
||||
// and stores it in the TLS keystore for the application. If the keys already
|
||||
// exist, generation is skipped.
|
||||
func CreateTLSCertificate(tlsHost string) error {
|
||||
log.WithField("host", tlsHost).Debug("Creating TLS certificate")
|
||||
tlsCertName := tlsHost + ".crt"
|
||||
tlsKeyName := tlsHost + ".pem"
|
||||
tlsKeystorePath, err := TLSKeystorePath()
|
||||
@@ -79,70 +99,102 @@ func CreateTLSCertificate(tlsHost string) error {
|
||||
_, certErr := os.Stat(tlsCert)
|
||||
_, keyErr := os.Stat(tlsKey)
|
||||
if certErr != nil || keyErr != nil {
|
||||
log.WithFields(logrus.Fields{
|
||||
"cert_exists": certErr == nil,
|
||||
"key_exists": keyErr == nil,
|
||||
"cert_path": tlsCert,
|
||||
"key_path": tlsKey,
|
||||
}).Debug("Certificate or key missing, generating new ones")
|
||||
if certErr != nil {
|
||||
log.WithField("path", tlsCert).Debug("TLS certificate not found")
|
||||
fmt.Printf("Unable to read TLS certificate '%s'\n", tlsCert)
|
||||
}
|
||||
if keyErr != nil {
|
||||
log.WithField("path", tlsKey).Debug("TLS key not found")
|
||||
fmt.Printf("Unable to read TLS key '%s'\n", tlsKey)
|
||||
}
|
||||
|
||||
if err := createTLSCertificate(tlsHost); nil != err {
|
||||
log.WithError(err).Error("Failed to create TLS certificate")
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
log.Debug("TLS certificate and key already exist")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createTLSCertificate(host string) error {
|
||||
log.WithField("host", host).Debug("Generating new TLS certificate")
|
||||
fmt.Println("Generating TLS keys. This may take a minute...")
|
||||
priv, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to generate private key")
|
||||
return err
|
||||
}
|
||||
|
||||
tlsCert, err := NewTLSCertificate(host, priv)
|
||||
if nil != err {
|
||||
log.WithError(err).Error("Failed to create new TLS certificate")
|
||||
return err
|
||||
}
|
||||
privStore, err := TLSKeystorePath()
|
||||
if nil != err {
|
||||
log.WithError(err).Error("Failed to get keystore path")
|
||||
return err
|
||||
}
|
||||
|
||||
certFile := filepath.Join(privStore, host+".crt")
|
||||
log.WithField("path", certFile).Debug("Saving TLS certificate")
|
||||
// save the TLS certificate
|
||||
certOut, err := os.Create(certFile)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("path", certFile).Error("Failed to create certificate file")
|
||||
return fmt.Errorf("failed to open %s for writing: %s", host+".crt", err)
|
||||
}
|
||||
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: tlsCert})
|
||||
certOut.Close()
|
||||
log.WithField("path", certFile).Debug("TLS certificate saved successfully")
|
||||
fmt.Printf("\tTLS certificate saved to: %s\n", host+".crt")
|
||||
|
||||
// save the TLS private key
|
||||
privFile := filepath.Join(privStore, host+".pem")
|
||||
log.WithField("path", privFile).Debug("Saving TLS private key")
|
||||
keyOut, err := os.OpenFile(privFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("path", privFile).Error("Failed to create private key file")
|
||||
return fmt.Errorf("failed to open %s for writing: %v", privFile, err)
|
||||
}
|
||||
secp384r1, err := asn1.Marshal(asn1.ObjectIdentifier{1, 3, 132, 0, 34}) // http://www.ietf.org/rfc/rfc5480.txt
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to marshal EC parameters")
|
||||
return err
|
||||
}
|
||||
pem.Encode(keyOut, &pem.Block{Type: "EC PARAMETERS", Bytes: secp384r1})
|
||||
ecder, err := x509.MarshalECPrivateKey(priv)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to marshal private key")
|
||||
return err
|
||||
}
|
||||
pem.Encode(keyOut, &pem.Block{Type: "EC PRIVATE KEY", Bytes: ecder})
|
||||
pem.Encode(keyOut, &pem.Block{Type: "CERTIFICATE", Bytes: tlsCert})
|
||||
|
||||
keyOut.Close()
|
||||
log.WithField("path", privFile).Debug("TLS private key saved successfully")
|
||||
fmt.Printf("\tTLS private key saved to: %s\n", privFile)
|
||||
|
||||
// CRL
|
||||
crlFile := filepath.Join(privStore, host+".crl")
|
||||
log.WithField("path", crlFile).Debug("Creating CRL")
|
||||
crlOut, err := os.OpenFile(crlFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("path", crlFile).Error("Failed to create CRL file")
|
||||
return fmt.Errorf("failed to open %s for writing: %s", crlFile, err)
|
||||
}
|
||||
crlcert, err := x509.ParseCertificate(tlsCert)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to parse certificate for CRL creation")
|
||||
return fmt.Errorf("Certificate with unknown critical extension was not parsed: %s", err)
|
||||
}
|
||||
|
||||
@@ -156,10 +208,12 @@ func createTLSCertificate(host string) error {
|
||||
|
||||
crlBytes, err := crlcert.CreateCRL(rand.Reader, priv, revokedCerts, now, now)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to create CRL")
|
||||
return fmt.Errorf("error creating CRL: %s", err)
|
||||
}
|
||||
_, err = x509.ParseDERCRL(crlBytes)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to validate generated CRL")
|
||||
return fmt.Errorf("error reparsing CRL: %s", err)
|
||||
}
|
||||
pem.Encode(crlOut, &pem.Block{Type: "X509 CRL", Bytes: crlBytes})
|
||||
|
Reference in New Issue
Block a user