16 Commits

Author SHA1 Message Date
idk
197aca0ece Never pass Sig_NONE again, if sig is unset, use the correct default sig 2023-01-16 04:18:11 +00:00
idk
fff37dbffa Never pass Sig_NONE again, if sig is unset, use the correct default sig 2023-01-16 04:17:51 +00:00
idk
dfd7cd886f update index.html 2022-08-28 13:40:47 -04:00
idk
c998e57a89 update index.html 2022-08-28 13:38:27 -04:00
idk
41317685c5 update index.html 2022-08-28 13:36:46 -04:00
idk
56cca2e537 bump version 2022-08-08 18:09:36 -04:00
idk
9ca67baa32 don't let the primary session thing be a guess, detect first, then attempt one and cache the result 2022-08-08 17:43:43 -04:00
idk
9083650fd0 switch to github-release 2022-08-05 11:57:22 -04:00
idk
34eafde2e2 bump version 2022-08-05 11:56:51 -04:00
idk
1780908fb8 add logging detail 2022-08-05 01:48:06 -04:00
idk
615604699a update index.html 2022-08-04 18:07:18 -04:00
idk
fb14beb87f update index.html 2022-07-31 18:03:00 -04:00
idk
6fb498e01e update index.html 2022-07-31 17:40:28 -04:00
idk
c80e6e7d95 update index.html 2022-07-31 17:39:38 -04:00
idk
ee9930813d fixes primary tunnel test 2022-06-01 18:25:24 -04:00
idk
3d289d99c6 fix some primary sessions, grab some defaults from environment 2022-05-31 23:59:37 -04:00
18 changed files with 706 additions and 115 deletions

0
.nojekyll Normal file
View File

View File

@@ -1,16 +1,16 @@
USER_GH=eyedeekay
VERSION=0.33.2
VERSION=0.33.5
packagename=sam3
echo:
@echo "type make version to do release $(VERSION)"
version:
gothub 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)"
del:
gothub delete -s $(GITHUB_TOKEN) -u $(USER_GH) -r $(packagename) -t v$(VERSION)
github-release delete -s $(GITHUB_TOKEN) -u $(USER_GH) -r $(packagename) -t v$(VERSION)
tar:
tar --exclude .git \
@@ -26,4 +26,4 @@ fmt:
find . -name '*.go' -exec gofmt -w -s {} \;
upload-linux:
gothub upload -R -u $(USER_GH) -r "$(packagename)" -t $(VERSION) -l `sha256sum ` -n "$(packagename)" -f "$(packagename)"
github-release upload -R -u $(USER_GH) -r "$(packagename)" -t $(VERSION) -l `sha256sum ` -n "$(packagename)" -f "$(packagename)"

View File

@@ -10,6 +10,8 @@ before upgrading your sam3 dependencies. You can probably do this by running:
find . -name '*.go' -exec sed -i 's|github.com/eyedeekay/sam3/i2pkeys|github.com/eyedeekay/i2pkeys|g' {} \;
```
STATUS: This project is maintained. I will respond to issues, pull requests, and feature requests within a few days.
# README #
go library for the I2P [SAMv3.0](https://geti2p.net/en/docs/api/samv3) bridge, used to build anonymous/pseudonymous end-to-end encrypted sockets.

View File

@@ -33,7 +33,7 @@ func (s *SAM) NewDatagramSession(id string, keys i2pkeys.I2PKeys, options []stri
if udpPort == 0 {
udpPort = 7655
}
lhost, _, err := net.SplitHostPort(s.conn.LocalAddr().String())
lhost, _, err := SplitHostPort(s.conn.LocalAddr().String())
if err != nil {
s.Close()
return nil, err
@@ -46,7 +46,7 @@ func (s *SAM) NewDatagramSession(id string, keys i2pkeys.I2PKeys, options []stri
if err != nil {
return nil, err
}
rhost, _, err := net.SplitHostPort(s.conn.RemoteAddr().String())
rhost, _, err := SplitHostPort(s.conn.RemoteAddr().String())
if err != nil {
s.Close()
return nil, err

13
emit.go
View File

@@ -3,6 +3,7 @@ package sam3
import (
"fmt"
"log"
"net"
"strings"
)
@@ -107,3 +108,15 @@ func IgnorePortError(err error) error {
}
return err
}
func SplitHostPort(hostport string) (string, string, error) {
host, port, err := net.SplitHostPort(hostport)
if err != nil {
if IgnorePortError(err) == nil {
log.Println("host: " + hostport)
host = hostport
port = "0"
}
}
return host, port, nil
}

View File

@@ -11,7 +11,7 @@ import (
)
func NetListener(name, samaddr, keyspath string) (net.Listener, error) {
return I2PListener(name, samaddr, keyspath)
return I2PListener(name, sam3.SAMDefaultAddr(samaddr), keyspath)
}
// I2PListener is a convenience function which takes a SAM tunnel name, a SAM address and a filename.
@@ -19,7 +19,7 @@ func NetListener(name, samaddr, keyspath string) (net.Listener, error) {
// exist, keys will be generated and stored in that file.
func I2PListener(name, samaddr, keyspath string) (*sam3.StreamListener, error) {
log.Printf("Starting and registering I2P service, please wait a couple of minutes...")
listener, err := I2PStreamSession(name, samaddr, keyspath)
listener, err := I2PStreamSession(name, sam3.SAMDefaultAddr(samaddr), keyspath)
if keyspath != "" {
err = ioutil.WriteFile(keyspath+".i2p.public.txt", []byte(listener.Keys().Addr().Base32()), 0644)
@@ -36,9 +36,9 @@ func I2PListener(name, samaddr, keyspath string) (*sam3.StreamListener, error) {
// of the user.
func I2PStreamSession(name, samaddr, keyspath string) (*sam3.StreamSession, error) {
log.Printf("Starting and registering I2P session...")
sam, err := sam3.NewSAM(samaddr)
sam, err := sam3.NewSAM(sam3.SAMDefaultAddr(samaddr))
if err != nil {
log.Fatalf("error connecting to SAM to %s: %s", samaddr, err)
log.Fatalf("error connecting to SAM to %s: %s", sam3.SAMDefaultAddr(samaddr), err)
}
keys, err := GenerateOrLoadKeys(keyspath, sam)
if err != nil {
@@ -52,9 +52,9 @@ func I2PStreamSession(name, samaddr, keyspath string) (*sam3.StreamSession, erro
// It also takes care of setting a persisitent key on behalf of the user.
func I2PDatagramSession(name, samaddr, keyspath string) (*sam3.DatagramSession, error) {
log.Printf("Starting and registering I2P session...")
sam, err := sam3.NewSAM(samaddr)
sam, err := sam3.NewSAM(sam3.SAMDefaultAddr(samaddr))
if err != nil {
log.Fatalf("error connecting to SAM to %s: %s", samaddr, err)
log.Fatalf("error connecting to SAM to %s: %s", sam3.SAMDefaultAddr(samaddr), err)
}
keys, err := GenerateOrLoadKeys(keyspath, sam)
if err != nil {
@@ -68,9 +68,9 @@ func I2PDatagramSession(name, samaddr, keyspath string) (*sam3.DatagramSession,
// It also takes care of setting a persisitent key on behalf of the user.
func I2PPrimarySession(name, samaddr, keyspath string) (*sam3.PrimarySession, error) {
log.Printf("Starting and registering I2P session...")
sam, err := sam3.NewSAM(samaddr)
sam, err := sam3.NewSAM(sam3.SAMDefaultAddr(samaddr))
if err != nil {
log.Fatalf("error connecting to SAM to %s: %s", samaddr, err)
log.Fatalf("error connecting to SAM to %s: %s", sam3.SAMDefaultAddr(samaddr), err)
}
keys, err := GenerateOrLoadKeys(keyspath, sam)
if err != nil {
@@ -80,9 +80,15 @@ func I2PPrimarySession(name, samaddr, keyspath string) (*sam3.PrimarySession, er
return gram, err
}
// GenerateOrLoadKeys is a convenience function which takes a filename and a SAM session.
// if the SAM session is nil, a new one will be created with the defaults.
// The keyspath must be the path to a place to store I2P keys. The keyspath will be suffixed with
// .i2p.private for the private keys, and public.txt for the b32 addresses.
// If the keyspath.i2p.private file does not exist, keys will be generated and stored in that file.
// if the keyspath.i2p.private does exist, keys will be loaded from that location and returned
func GenerateOrLoadKeys(keyspath string, sam *sam3.SAM) (keys *i2pkeys.I2PKeys, err error) {
if sam == nil {
sam, err = sam3.NewSAM("127.0.0.1:7657")
sam, err = sam3.NewSAM(sam3.SAMDefaultAddr("127.0.0.1:7656"))
if err != nil {
return nil, err
}
@@ -112,6 +118,8 @@ func GenerateOrLoadKeys(keyspath string, sam *sam3.SAM) (keys *i2pkeys.I2PKeys,
return keys, nil
}
// GenerateKeys is a shorter version of GenerateOrLoadKeys which generates keys and stores them in a file.
// it always uses a new default SAM session.
func GenerateKeys(keyspath string) (keys *i2pkeys.I2PKeys, err error) {
return GenerateOrLoadKeys(keyspath, nil)
}

BIN
i2plogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

331
index.html Normal file
View File

@@ -0,0 +1,331 @@
<html>
<head>
<title>
README
</title>
<meta name="author" content="eyedeekay" />
<meta name="description" content="sam3" />
<meta name="keywords" content="master" />
<link rel="stylesheet" type="text/css" href="style.css" />
<link rel="stylesheet" type="text/css" href="showhider.css" />
</head>
<body>
<div id="navbar">
<a href="#shownav">
Show navigation
</a>
<div id="shownav">
<div id="hidenav">
<ul>
<li>
<a href="index.html">
index
</a>
</li>
</ul>
<br>
<a href="#hidenav">
Hide Navigation
</a>
</div>
</div>
</div>
<h1>
<a href="#readme" rel="nofollow">
<span></span>
</a>
README
</h1>
<h2>
<a href="#important" rel="nofollow">
<span></span>
</a>
!!IMPORTANT!!
</h2>
<p>
In the next version, I&#39;ll be moving the
<code>
i2pkeys
</code>
directory to it&#39;s own repository
so I can avoid import cycle headaches. Please migrate to the new
<code>
i2pkeys
</code>
repository
before upgrading your sam3 dependencies. You can probably do this by running:
</p>
<div>
<pre>find . -name &#39;*.go&#39; -exec sed -i &#39;s|github.com/eyedeekay/sam3/i2pkeys|github.com/eyedeekay/i2pkeys|g&#39; {} \;
</pre>
</div>
<p>
STATUS: This project is maintained. I will respond to issues, pull requests, and feature requests within a few days.
</p>
<h1>
<a href="#readme" rel="nofollow">
<span></span>
</a>
README
</h1>
<p>
go library for the I2P
<a href="https://geti2p.net/en/docs/api/samv3" rel="nofollow">
SAMv3.0
</a>
bridge, used to build anonymous/pseudonymous end-to-end encrypted sockets.
</p>
<p>
This library is much better than ccondom (that use BOB), much more stable and much easier to maintain.
</p>
<h2>
<a href="#support-todo" rel="nofollow">
<span></span>
</a>
Support/TODO
</h2>
<p>
<strong>
What works:
</strong>
</p>
<ul>
<li>
Utils
<ul>
<li>
Resolving domain names to I2P destinations
</li>
<li>
.b32.i2p hashes
</li>
<li>
Generating keys/i2p destinations
</li>
</ul>
</li>
<li>
Streaming
<ul>
<li>
DialI2P() - Connecting to stuff in I2P
</li>
<li>
Listen()/Accept() - Handling incomming connections
</li>
<li>
Implements net.Conn and net.Listener
</li>
</ul>
</li>
<li>
Datagrams
<ul>
<li>
Implements net.PacketConn
</li>
</ul>
</li>
<li>
Raw datagrams
<ul>
<li>
Like datagrams, but without addresses
</li>
</ul>
</li>
</ul>
<p>
<strong>
Does not work:
</strong>
</p>
<ul>
<li>
Stream Forwarding
</li>
<li>
Probably needs some real-world testing
</li>
</ul>
<h2>
<a href="#documentation" rel="nofollow">
<span></span>
</a>
Documentation
</h2>
<ul>
<li>
Latest version-documentation:
<ul>
<li>
set your GOPATH
</li>
<li>
Enter
<code>
godoc -http=:8081
</code>
into your terminal and hit enter.
</li>
<li>
Goto
<a href="http://localhost:8081" rel="nofollow">
http://localhost:8081
</a>
, click packages, and navigate to sam3
</li>
</ul>
</li>
</ul>
<h2>
<a href="#examples" rel="nofollow">
<span></span>
</a>
Examples
</h2>
<div>
<pre>package main
import (
&#34;github.com/eyedeekay/sam3&#34;
&#34;github.com/eyedeekay/sam3/i2pkeys&#34;
&#34;fmt&#34;
)
const yoursam = &#34;127.0.0.1:7656&#34; // sam bridge
func client(server i2pkeys.I2PAddr) {
sam, _ := sam3.NewSAM(yoursam)
keys, _ := sam.NewKeys()
stream, _ := sam.NewStreamSession(&#34;clientTun&#34;, keys, sam3.Options_Small)
fmt.Println(&#34;Client: Connecting to &#34; + server.Base32())
conn, _ := stream.DialI2P(server)
conn.Write([]byte(&#34;Hello world!&#34;))
return
}
func main() {
sam, _ := NewSAM(yoursam)
keys, _ := sam.NewKeys()
stream, _ := sam.NewStreamSession(&#34;serverTun&#34;, keys, sam3.Options_Medium)
listener, _ := stream.Listen()
go client(keys.Addr())
conn, _ := listener.Accept()
buf := make([]byte, 4096)
n, _ := conn.Read(buf)
fmt.Println(&#34;Server received: &#34; + string(buf[:n]))
}
</pre>
</div>
<p>
The above will write to the terminal:
</p>
<div>
<pre>Client: Connecting to zjnvfh4hs3et5vtz35ogwzrws26zvwkcad5uo5esecvg4qpk5b4a.b32.i2p
Server received: Hello world!
</pre>
</div>
<p>
Error handling was omitted in the above code for readability.
</p>
<h2>
<a href="#testing" rel="nofollow">
<span></span>
</a>
Testing
</h2>
<ul>
<li>
<code>
go test -tags=nettest
</code>
runs the whole suite (takes 90+ sec to perform!)
</li>
<li>
<code>
go test -short
</code>
runs the shorter variant, does not connect to anything
</li>
</ul>
<h2>
<a href="#license" rel="nofollow">
<span></span>
</a>
License
</h2>
<p>
Public domain.
</p>
<h2>
<a href="#author" rel="nofollow">
<span></span>
</a>
Author
</h2>
<ul>
<li>
Kalle Vedin
<code>
kalle.vedin@fripost.org
</code>
</li>
<li>
Unknown Name (majestrate)
</li>
<li>
idk
</li>
<li>
qiwenmin
</li>
</ul>
<div>
<a href="#show">
Show license
</a>
<div id="show">
<div id="hide">
<pre><code>This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
</code></pre>
<a href="#hide">
Hide license
</a>
</div>
</div>
</div>
<div>
<iframe src="https://snowflake.torproject.org/embed.html" width="320" height="240" frameborder="0" scrolling="no"></iframe>
</div>
<div>
<a href="https://geti2p.net/">
<img src="i2plogo.png"></img>
I2P
</a>
</div>
</body>
</html>

View File

@@ -77,9 +77,11 @@ func (ss *PrimarySession) Keys() i2pkeys.I2PKeys {
func (sam *PrimarySession) Dial(network, addr string) (net.Conn, error) {
if network == "udp" || network == "udp4" || network == "udp6" {
//return sam.DialUDPI2P(network, network+addr[0:4], addr)
return sam.DialUDPI2P(network, network+addr[0:4], addr)
}
if network == "tcp" || network == "tcp4" || network == "tcp6" {
//return sam.DialTCPI2P(network, network+addr[0:4], addr)
return sam.DialTCPI2P(network, network+addr[0:4], addr)
}
return nil, fmt.Errorf("Error: Must specify a valid network type")
@@ -87,52 +89,60 @@ func (sam *PrimarySession) Dial(network, addr string) (net.Conn, error) {
// DialTCP implements x/dialer
func (sam *PrimarySession) DialTCP(network string, laddr, raddr net.Addr) (net.Conn, error) {
_, ok := sam.stsess[network+raddr.String()[0:4]]
ts, ok := sam.stsess[network+raddr.String()[0:4]]
var err error
if !ok {
stsess, err := sam.NewUniqueStreamSubSession(network + raddr.String()[0:4])
ts, err = sam.NewUniqueStreamSubSession(network + raddr.String()[0:4])
if err != nil {
return nil, err
}
sam.stsess[network+raddr.String()[0:4]] = stsess
sam.stsess[network+raddr.String()[0:4]] = ts
ts, _ = sam.stsess[network+raddr.String()[0:4]]
}
return sam.stsess[network+raddr.String()[0:4]].Dial(network, raddr.String())
return ts.Dial(network, raddr.String())
}
func (sam *PrimarySession) DialTCPI2P(network string, laddr, raddr string) (net.Conn, error) {
_, ok := sam.stsess[network+raddr[0:4]]
ts, ok := sam.stsess[network+raddr[0:4]]
var err error
if !ok {
stsess, err := sam.NewUniqueStreamSubSession(network + laddr)
ts, err = sam.NewUniqueStreamSubSession(network + laddr)
if err != nil {
return nil, err
}
sam.stsess[network+raddr[0:4]] = stsess
sam.stsess[network+raddr[0:4]] = ts
ts, _ = sam.stsess[network+raddr[0:4]]
}
return sam.stsess[network+raddr[0:4]].Dial(network, raddr)
return ts.Dial(network, raddr)
}
// DialUDP implements x/dialer
func (sam *PrimarySession) DialUDP(network string, laddr, raddr net.Addr) (net.PacketConn, error) {
_, ok := sam.dgsess[network+raddr.String()[0:4]]
ds, ok := sam.dgsess[network+raddr.String()[0:4]]
var err error
if !ok {
dgsess, err := sam.NewDatagramSubSession(network+raddr.String()[0:4], 0)
ds, err = sam.NewDatagramSubSession(network+raddr.String()[0:4], 0)
if err != nil {
return nil, err
}
sam.dgsess[network+raddr.String()[0:4]] = dgsess
sam.dgsess[network+raddr.String()[0:4]] = ds
ds, _ = sam.dgsess[network+raddr.String()[0:4]]
}
return sam.dgsess[network+raddr.String()[0:4]].Dial(network, raddr.String())
return ds.Dial(network, raddr.String())
}
func (sam *PrimarySession) DialUDPI2P(network, laddr, raddr string) (*DatagramSession, error) {
_, ok := sam.dgsess[network+raddr[0:4]]
ds, ok := sam.dgsess[network+raddr[0:4]]
var err error
if !ok {
dgsess, err := sam.NewDatagramSubSession(network+laddr, 0)
ds, err = sam.NewDatagramSubSession(network+laddr, 0)
if err != nil {
return nil, err
}
sam.dgsess[network+raddr[0:4]] = dgsess
sam.dgsess[network+raddr[0:4]] = ds
ds, _ = sam.dgsess[network+raddr[0:4]]
}
return sam.dgsess[network+raddr[0:4]].Dial(network, raddr)
return ds.Dial(network, raddr)
}
func (s *PrimarySession) Lookup(name string) (a net.Addr, err error) {
@@ -161,7 +171,11 @@ func (sam *PrimarySession) ResolveUDPAddr(network, dest string) (net.Addr, error
// Creates a new PrimarySession with the I2CP- and streaminglib options as
// specified. See the I2P documentation for a full list of options.
func (sam *SAM) NewPrimarySession(id string, keys i2pkeys.I2PKeys, options []string) (*PrimarySession, error) {
conn, err := sam.newGenericSession(PrimarySessionSwitch, id, keys, options, []string{})
return sam.newPrimarySession(PrimarySessionSwitch, id, keys, options)
}
func (sam *SAM) newPrimarySession(primarySessionSwitch string, id string, keys i2pkeys.I2PKeys, options []string) (*PrimarySession, error) {
conn, err := sam.newGenericSession(primarySessionSwitch, id, keys, options, []string{})
if err != nil {
return nil, err
}
@@ -205,13 +219,13 @@ func (sam *PrimarySession) newGenericSubSessionWithSignatureAndPorts(style, id,
conn := sam.conn
fp := ""
tp := ""
if from != "0" {
if from != "0" && from != "" {
fp = " FROM_PORT=" + from
}
if to != "0" {
if to != "0" && to != "" {
tp = " TO_PORT=" + to
}
scmsg := []byte("SESSION ADD STYLE=" + style + fp + tp + " ID=" + id + " " + strings.Join(extras, " ") + "\n")
scmsg := []byte("SESSION ADD STYLE=" + style + " ID=" + id + fp + tp + " " + strings.Join(extras, " ") + "\n")
for m, i := 0, 0; m != len(scmsg); i++ {
if i == 15 {
conn.Close()
@@ -231,6 +245,7 @@ func (sam *PrimarySession) newGenericSubSessionWithSignatureAndPorts(style, id,
return nil, err
}
text := string(buf[:n])
//log.Println("SAM:", text)
if strings.HasPrefix(text, session_ADDOK) {
//if sam.keys.String() != text[len(session_ADDOK):len(text)-1] {
//conn.Close()
@@ -245,7 +260,7 @@ func (sam *PrimarySession) newGenericSubSessionWithSignatureAndPorts(style, id,
return nil, errors.New("Duplicate destination")
} else if text == session_INVALID_KEY {
conn.Close()
return nil, errors.New("Invalid key")
return nil, errors.New("Invalid key - Primary Session")
} else if strings.HasPrefix(text, session_I2P_ERROR) {
conn.Close()
return nil, errors.New("I2P error " + text[len(session_I2P_ERROR):])
@@ -272,7 +287,7 @@ func (sam *PrimarySession) NewUniqueStreamSubSession(id string) (*StreamSession,
if err != nil {
return nil, err
}
return &StreamSession{sam.Config.I2PConfig.Sam(), id, conn, sam.keys, time.Duration(600 * time.Second), time.Now(), Sig_NONE, randport(), "0"}, nil
return &StreamSession{sam.Config.I2PConfig.Sam(), id, conn, sam.keys, time.Duration(600 * time.Second), time.Now(), Sig_NONE, randport(), randport()}, nil
}
// Creates a new StreamSession with the I2CP- and streaminglib options as
@@ -304,7 +319,7 @@ func (s *PrimarySession) NewDatagramSubSession(id string, udpPort int) (*Datagra
if udpPort == 0 {
udpPort = 7655
}
lhost, _, err := net.SplitHostPort(s.conn.LocalAddr().String())
lhost, _, err := SplitHostPort(s.conn.LocalAddr().String())
if err != nil {
s.Close()
return nil, err
@@ -317,7 +332,7 @@ func (s *PrimarySession) NewDatagramSubSession(id string, udpPort int) (*Datagra
if err != nil {
return nil, err
}
rhost, _, err := net.SplitHostPort(s.conn.RemoteAddr().String())
rhost, _, err := SplitHostPort(s.conn.RemoteAddr().String())
if err != nil {
s.Close()
return nil, err
@@ -347,7 +362,7 @@ func (s *PrimarySession) NewRawSubSession(id string, udpPort int) (*RawSession,
if udpPort == 0 {
udpPort = 7655
}
lhost, _, err := net.SplitHostPort(s.conn.LocalAddr().String())
lhost, _, err := SplitHostPort(s.conn.LocalAddr().String())
if err != nil {
s.Close()
return nil, err
@@ -360,7 +375,7 @@ func (s *PrimarySession) NewRawSubSession(id string, udpPort int) (*RawSession,
if err != nil {
return nil, err
}
rhost, _, err := net.SplitHostPort(s.conn.RemoteAddr().String())
rhost, _, err := SplitHostPort(s.conn.RemoteAddr().String())
if err != nil {
s.Close()
return nil, err

View File

@@ -2,12 +2,12 @@ package sam3
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"strings"
"testing"
"time"
"github.com/eyedeekay/i2pkeys"
)
func Test_PrimaryStreamingDial(t *testing.T) {
@@ -108,17 +108,20 @@ func Test_PrimaryStreamingServerClient(t *testing.T) {
if !(<-w) {
return
}
/*sam2, err := NewSAM(yoursam)
if err != nil {
c <- false
return
}
defer sam2.Close()
keys, err := sam2.NewKeys()
if err != nil {
c <- false
return
}*/
/*
sam2, err := NewSAM(yoursam)
if err != nil {
c <- false
return
}
defer sam2.Close()
keys, err := sam2.NewKeys()
if err != nil {
c <- false
return
}
*/
fmt.Println("\tClient: Creating tunnel")
ss2, err := sam.NewStreamSubSession("primaryExampleClientTun")
if err != nil {
@@ -220,73 +223,86 @@ func ExamplePrimaryStreamListener() {
const samBridge = "127.0.0.1:7656"
earlysam, err := NewSAM(yoursam)
var ss *StreamSession
go func() {
earlysam, err := NewSAM(yoursam)
if err != nil {
log.Fatal(err.Error())
return
}
defer earlysam.Close()
keys, err := earlysam.NewKeys()
if err != nil {
log.Fatal(err.Error())
return
}
sam, err := earlysam.NewPrimarySession("PrimaryListenerTunnel", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil {
log.Fatal(err.Error())
return
}
defer sam.Close()
ss, err = sam.NewStreamSubSession("PrimaryListenerServerTunnel2")
if err != nil {
fmt.Println(err.Error())
return
}
defer ss.Close()
l, err := ss.Listen()
if err != nil {
fmt.Println(err.Error())
return
}
defer l.Close()
//fmt.Println("Serving on primary listener", l.Addr().String())
if err := http.Serve(l, &exitHandler{}); err != nil {
fmt.Println(err.Error())
}
}()
time.Sleep(time.Second * 10)
latesam, err := NewSAM(yoursam)
if err != nil {
log.Fatal(err.Error())
return
}
defer earlysam.Close()
keys, err := earlysam.NewKeys()
defer latesam.Close()
keys2, err := latesam.NewKeys()
if err != nil {
log.Fatal(err.Error())
return
}
sam, err := earlysam.NewPrimarySession("PrimaryListenerTunnel", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil {
log.Fatal(err.Error())
return
}
defer sam.Close()
quit := make(chan bool)
// Client connecting to the server
go func(server i2pkeys.I2PAddr) {
cs, err := sam.NewUniqueStreamSubSession("PrimaryListenerTunnel")
if err != nil {
fmt.Println(err.Error())
quit <- false
return
}
defer cs.Close()
conn, err := cs.DialI2P(server)
if err != nil {
fmt.Println(err.Error())
quit <- false
return
}
buf := make([]byte, 256)
n, err := conn.Read(buf)
if err != nil {
fmt.Println(err.Error())
quit <- false
return
}
fmt.Println(string(buf[:n]))
quit <- true
}(keys.Addr()) // end of client
ss, err := sam.NewUniqueStreamSubSession("PrimaryListenerTunnel")
sc, err := latesam.NewStreamSession("PrimaryListenerClientTunnel2", keys2, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil {
fmt.Println(err.Error())
return
}
defer ss.Close()
l, err := ss.Listen()
defer sc.Close()
client := http.Client{
Transport: &http.Transport{
Dial: sc.Dial,
},
}
//resp, err := client.Get("http://" + "idk.i2p") //ss.Addr().Base32())
resp, err := client.Get("http://" + ss.Addr().Base32())
if err != nil {
fmt.Println(err.Error())
return
}
conn, err := l.Accept()
defer resp.Body.Close()
r, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err.Error())
return
}
conn.Write([]byte("Hello world!"))
<-quit // waits for client to die, for example only
fmt.Println("Got response: " + string(r))
// Output:
//Hello world!
// Got response: Hello world!
}
type exitHandler struct {
}
func (e *exitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello world!"))
}

4
raw.go
View File

@@ -34,7 +34,7 @@ func (s *SAM) NewRawSession(id string, keys i2pkeys.I2PKeys, options []string, u
if udpPort == 0 {
udpPort = 7655
}
lhost, _, err := net.SplitHostPort(s.conn.LocalAddr().String())
lhost, _, err := SplitHostPort(s.conn.LocalAddr().String())
if err != nil {
s.Close()
return nil, err
@@ -47,7 +47,7 @@ func (s *SAM) NewRawSession(id string, keys i2pkeys.I2PKeys, options []string, u
if err != nil {
return nil, err
}
rhost, _, err := net.SplitHostPort(s.conn.RemoteAddr().String())
rhost, _, err := SplitHostPort(s.conn.RemoteAddr().String())
if err != nil {
s.Close()
return nil, err

View File

@@ -55,7 +55,7 @@ func (sam *SAMResolver) Resolve(name string) (i2pkeys.I2PAddr, error) {
if text == "RESULT=OK" {
continue
} else if text == "RESULT=INVALID_KEY" {
errStr += "Invalid key."
errStr += "Invalid key - resolver."
} else if text == "RESULT=KEY_NOT_FOUND" {
errStr += "Unable to resolve " + name
} else if text == "NAME="+name {

View File

@@ -12,9 +12,7 @@ import (
"strings"
"github.com/eyedeekay/i2pkeys"
)
import (
. "github.com/eyedeekay/i2pkeys"
)
@@ -37,7 +35,7 @@ const (
)
const (
Sig_NONE = ""
Sig_NONE = "SIGNATURE_TYPE=EdDSA_SHA512_Ed25519"
Sig_DSA_SHA1 = "SIGNATURE_TYPE=DSA_SHA1"
Sig_ECDSA_SHA256_P256 = "SIGNATURE_TYPE=ECDSA_SHA256_P256"
Sig_ECDSA_SHA384_P384 = "SIGNATURE_TYPE=ECDSA_SHA384_P384"
@@ -259,7 +257,7 @@ func (sam *SAM) newGenericSessionWithSignatureAndPorts(style, id, from, to strin
return nil, errors.New("Duplicate destination")
} else if text == session_INVALID_KEY {
conn.Close()
return nil, errors.New("Invalid key")
return nil, errors.New("Invalid key - SAM session")
} else if strings.HasPrefix(text, session_I2P_ERROR) {
conn.Close()
return nil, errors.New("I2P error " + text[len(session_I2P_ERROR):])

10
showhider.css Normal file
View File

@@ -0,0 +1,10 @@
/* edgar showhider CSS file */
#show {display:none; }
#hide {display:block; }
#show:target {display: block; }
#hide:target {display: none; }
#shownav {display:none; }
#hidenav {display:block; }
#shownav:target {display: block; }
#hidenav:target {display: none; }

View File

@@ -108,7 +108,7 @@ func (s *StreamSession) Lookup(name string) (i2pkeys.I2PAddr, error) {
sam, err := NewSAM(s.samAddr)
if err == nil {
addr, err := sam.Lookup(name)
sam.Close()
defer sam.Close()
return addr, err
}
return i2pkeys.I2PAddr(""), err
@@ -161,6 +161,7 @@ func minNonzeroTime(a, b time.Time) time.Time {
// - now+Timeout
// - d.Deadline
// - the context's deadline
//
// Or zero, if none of Timeout, Deadline, or context's deadline is set.
func (s *StreamSession) deadline(ctx context.Context, now time.Time) (earliest time.Time) {
if s.Timeout != 0 { // including negative, for historical reasons
@@ -177,15 +178,19 @@ func (s *StreamSession) Dial(n, addr string) (c net.Conn, err error) {
var i2paddr i2pkeys.I2PAddr
var host string
host, _, err = net.SplitHostPort(addr)
host, _, err = SplitHostPort(addr)
//log.Println("Dialing:", host)
if err = IgnorePortError(err); err == nil {
// check for name
if strings.HasSuffix(host, ".b32.i2p") || strings.HasSuffix(host, ".i2p") {
// name lookup
i2paddr, err = s.Lookup(host)
//log.Println("Lookup:", i2paddr, err)
} else {
// probably a destination
i2paddr = i2pkeys.I2PAddr(host)
i2paddr, err = i2pkeys.NewI2PAddrFromBytes([]byte(host))
//i2paddr = i2pkeys.I2PAddr(host)
//log.Println("Destination:", i2paddr, err)
}
if err == nil {
return s.DialI2P(i2paddr)
@@ -230,7 +235,7 @@ func (s *StreamSession) DialI2P(addr i2pkeys.I2PAddr) (*SAMConn, error) {
return nil, errors.New("I2P internal error")
case "RESULT=INVALID_KEY":
conn.Close()
return nil, errors.New("Invalid key")
return nil, errors.New("Invalid key - Stream Session")
case "RESULT=INVALID_ID":
conn.Close()
return nil, errors.New("Invalid tunnel ID")

View File

@@ -86,7 +86,6 @@ func (l *StreamListener) AcceptI2P() (*SAMConn, error) {
if strings.HasPrefix(line, "STREAM STATUS RESULT=OK") {
// we gud read destination line
destline, err := rd.ReadString(10)
log.Println(destline)
if err == nil {
dest := ExtractDest(destline)
l.session.from = ExtractPairString(destline, "FROM_PORT")

157
style.css Normal file
View File

@@ -0,0 +1,157 @@
/* edgar default CSS file */
body {
font-family: "Roboto";
font-family: monospace;
text-align: justify;
background-color: #373636;
color: whitesmoke;
font-size: 1.15em;
}
ul {
width: 55%;
display: block;
}
ol {
width: 55%;
display: block;
}
li {
margin-top: 1%;
}
p {
max-width: 90%;
margin-top: 1%;
margin-left: 3%;
margin-right: 3%;
}
img {
float: left;
top: 5%;
left: 5%;
max-width: 60%;
display: inline;
}
.inline {
display: inline;
}
.link-button:focus {
outline: none;
}
.link-button:active {
color: red;
}
code {
font-family: monospace;
border-radius: 5%;
padding: 1%;
border-color: darkgray;
font-size: .9em;
}
a {
color: #C6D9FE;
padding: 1%;
}
ul li {
color: #C6D9FE;
}
iframe {
background: aliceblue;
border-radius: 15%;
margin: 2%;
}
.container {
width: 36vw;
height: 64vh;
display: inline-block;
margin: 0;
padding: 0;
}
.editor-toolbar a {
display: inline-block;
text-align: center;
text-decoration: none !important;
color: whitesmoke !important;
}
#feed {
width: 60vw;
height: unset !important;
margin: 0;
padding: 0;
float: right;
background-color: #373636;
color: whitesmoke;
border: #C6D9FE solid 1px;
}
.thread-post,
.thread {
color: whitesmoke !important;
background-color: #373636;
border: 1px solid darkgray;
font-size: inherit;
padding-top: 1%;
padding-bottom: 1%;
}
.thread-post {
margin-left: 4%;
}
input {
text-align: center;
color: whitesmoke !important;
background-color: #373636;
border: 1px solid darkgray;
font: normal normal normal 14px/1 FontAwesome;
font-size: inherit;
padding-top: 1%;
padding-bottom: 1%;
}
.thread-hash {
text-align: right;
color: whitesmoke !important;
background-color: #373636;
border: 1px solid darkgray;
font-size: inherit;
padding-top: 1%;
padding-bottom: 1%;
}
.post-body {
text-align: left;
color: whitesmoke !important;
font-size: inherit;
padding-top: 1%;
padding-bottom: 1%;
}
#show {display:none; }
#hide {display:block; }
#show:target {display: block; }
#hide:target {display: none; }
#shownav {display:none; }
#hidenav {display:block; }
#shownav:target {display: block; }
#hidenav:target {display: none; }
#navbar {
float: right;
width: 10%;
}

View File

@@ -1,6 +1,10 @@
package sam3
import "net/http"
import (
"net"
"net/http"
"os"
)
// Examples and suggestions for options when creating sessions.
var (
@@ -61,10 +65,43 @@ func PrimarySessionString() string {
if err != nil {
return "MASTER"
}
// at this point we're probably running on Java I2P and thus probably
// have a PRIMARY session. Just to be sure, try to make one, check
// for errors, then immediately close it.
testSam, err := NewSAM(SAMDefaultAddr(""))
if err != nil {
return "MASTER"
}
newKeys, err := testSam.NewKeys()
if err != nil {
return "MASTER"
}
primarySession, err := testSam.newPrimarySession("PRIMARY", "primaryTestTunnel", newKeys, Options_Small)
if err != nil {
return "MASTER"
}
primarySession.Close()
return "PRIMARY"
}
return "MASTER"
}
var PrimarySessionSwitch string = PrimarySessionString()
func getEnv(key, fallback string) string {
value, ok := os.LookupEnv(key)
if !ok {
return fallback
}
return value
}
var SAM_HOST = getEnv("sam_host", "127.0.0.1")
var SAM_PORT = getEnv("sam_port", "7656")
func SAMDefaultAddr(fallforward string) string {
if fallforward == "" {
return net.JoinHostPort(SAM_HOST, SAM_PORT)
}
return fallforward
}