29 Commits

Author SHA1 Message Date
idk
48e87d68a9 Merge pull request #2 from urgentquest/main
Utililize go i2p logger (revised)
2025-05-05 18:02:21 -04:00
Call me Phil
3f93dec9db Utililize go i2p logger (#1)
- Drop log.go in favor of using go-i2p/logger
- Fix an occurrence of ioutil deprecation. Bump go version to a recent one
2025-03-30 23:14:21 +00:00
eyedeekay
daeaa91183 update modules 2024-11-16 16:25:24 -05:00
eyedeekay
ed7e6f56ae update release process 2024-11-16 16:23:33 -05:00
eyedeekay
982a496406 update module 2024-11-16 15:51:42 -05:00
eyedeekay
d7abd8af30 don't log in GetJoinedWD for now because it is called prior to 2024-11-16 15:50:58 -05:00
eyedeekay
0d05b3c3f7 update examples 2024-11-14 10:43:17 -05:00
eyedeekay
00aafc2ede fix the broken tests 2024-11-13 14:55:42 -05:00
eyedeekay
8920b7c847 update version 2024-11-13 14:53:54 -05:00
eyedeekay
2791849c67 update modules 2024-11-13 14:52:44 -05:00
eyedeekay
0d9a9ee10a Fix up import paths 2024-11-09 11:54:29 -05:00
eyedeekay
867026628f setup auto-assign workflow 2024-11-08 15:01:15 -05:00
eyedeekay
56eed1e88f Merge branch 'main' of github.com:eyedeekay/onramp 2024-11-08 12:55:30 -05:00
eyedeekay
c4e23394dc change module path 2024-11-08 12:54:30 -05:00
idk
f9fc572938 Merge pull request #1 from hkh4n/logging
Logging
2024-11-05 00:03:47 +00:00
Haris Khan
e184eda3c1 replaced deprecated function 2024-11-04 15:20:23 -05:00
Haris Khan
a0a35dd81c added logging to tls.go 2024-11-04 15:17:48 -05:00
Haris Khan
7ef5793e5a added logging to proxy.go 2024-11-04 15:11:49 -05:00
Haris Khan
c2b8660d5e added logging to onion.go 2024-11-04 15:08:35 -05:00
Haris Khan
680d5c000b added logging to garlic.go 2024-11-04 14:52:43 -05:00
Haris Khan
c2b56d35e2 updated README.md to reflect new logging paradigm 2024-11-04 14:44:07 -05:00
Haris Khan
2a53e8347c added logging to common.go 2024-11-04 11:05:47 -05:00
Haris Khan
dcb997a327 added logging paradigm to log.go 2024-11-04 11:05:39 -05:00
eyedeekay
d6a5a60d6f Fix broken test, fix onion TLS listener which broke 2024-09-17 20:10:29 -04:00
eyedeekay
c60c135814 Update sam3 and i2pkeys to 0.33.8 2024-09-17 19:41:29 -04:00
eyedeekay
8f08410320 add credit for contributions to release info 2024-09-17 19:35:32 -04:00
eyedeekay
cdad2747a7 bump version to 0.33.7 2024-01-09 14:48:35 -05:00
eyedeekay
394c9f9fb8 Update sam3 version to v0.33.7 2024-01-09 14:48:08 -05:00
eyedeekay
f32a695cff stub out libp2p interface 2023-08-06 22:36:36 -04:00
14 changed files with 647 additions and 208 deletions

20
.github/workflows/auto-assign.yml vendored Normal file
View 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

View File

@@ -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() {

View File

@@ -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)

View File

@@ -1,6 +1,8 @@
onramp
======
[![Go Report Card](https://goreportcard.com/badge/github.com/go-i2p/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.

View File

@@ -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
View File

@@ -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)
}

View File

@@ -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
)

View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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
View File

@@ -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})