RAW sessions works :]

This commit is contained in:
Kalle Vedin
2014-06-29 21:01:46 +02:00
parent f5afde6b21
commit ccc37f2a1e
4 changed files with 267 additions and 22 deletions

View File

@ -9,7 +9,7 @@ This library is much better than ccondom (that use BOB), much more stable and mu
**What works:**
* Utils
* Resolving URLs to I2P destinations
* Resolving domain names to I2P destinations
* .b32.i2p hashes
* Generating keys/i2p destinations
* Streaming
@ -18,20 +18,66 @@ This library is much better than ccondom (that use BOB), much more stable and mu
* Implements net.Conn and net.Listener
* Datagrams
* Implements net.PacketConn
* Raw datagrams
* Like datagrams, but without addresses
**Does not work:**
* Raw packets
* Everything works! :D
* Probably needs some real-world testing
## Documentation ##
* Enter `godoc -http=:8081` into your terminal and hit enter.
* Goto http://localhost:8081, click packages, and navigate to sam3
## Examples ##
´´´go
package main
import (
"bitbucket.org/kallevedin/sam3"
"fmt"
)
const yoursam = "127.0.0.1:7656" // sam bridge
func client(server I2PAddr) {
sam, _ := NewSAM(yoursam)
keys, _ := sam.NewKeys()
stream, _ := sam2.NewStreamSession("clientTun", keys, Options_Small)
fmt.Println("Client: Connecting to " + server.Base32())
conn, _ := stream.DialI2P(server)
conn.Write([]byte("Hello world!"))
return
}
func main() {
sam, _ := NewSAM(yoursam)
keys, _ := sam.NewKeys()
go client(keys.Addr())
stream, _ := sam.NewStreamSession("serverTun", keys, Options_Medium)
listener, _ := stream.Listen()
conn, _ := listener.Accept()
buf := make([]byte, 4096)
n,_ := conn.Read(buf)
fmt.Println("Server received: " + string(buf[:n]))
}
´´´
The above will write to the terminal:
```text
Client: Connecting to zjnvfh4hs3et5vtz35ogwzrws26zvwkcad5uo5esecvg4qpk5b4a.b32.i2p
Server received: Hello world!
```
Error handling was omitted in the above code for readability.
## Testing ##
* `go test` runs the whole suite
* `go test -short` runs the shorter variant
* `go test` runs the whole suite (takes 90+ sec to perform!)
* `go test -short` runs the shorter variant, does not connect to anything
## License ##

View File

@ -16,9 +16,9 @@ type DatagramSession struct {
samAddr string // address to the sam bridge (ipv4:port)
id string // tunnel name
conn net.Conn // connection to sam bridge
udpconn *net.UDPConn // used to deliver datagrams
udpconn *net.UDPConn // used to deliver datagrams
keys I2PKeys // i2p destination keys
rUDPAddr *net.UDPAddr // the SAM bridge UDP-port
rUDPAddr *net.UDPAddr // the SAM bridge UDP-port
}
// Creates a new datagram session. udpPort is the UDP port SAM is listening on,
@ -66,9 +66,17 @@ func (s *DatagramSession) ReadFrom(b []byte) (n int, addr I2PAddr, err error) {
// extra bytes to read the remote address of incomming datagram
buf := make([]byte, len(b) + 4096)
n, _, err = s.udpconn.ReadFrom(buf)
if err != nil {
return 0, I2PAddr(""), err
for {
// very basic protection: only accept incomming UDP messages from the IP of the SAM bridge
var saddr *net.UDPAddr
n, saddr, err = s.udpconn.ReadFromUDP(buf)
if err != nil {
return 0, I2PAddr(""), err
}
if bytes.Equal(saddr.IP, s.rUDPAddr.IP) {
continue
}
break
}
i := bytes.IndexByte(buf, byte('\n'))
if i > 4096 || i > n {
@ -79,12 +87,13 @@ func (s *DatagramSession) ReadFrom(b []byte) (n int, addr I2PAddr, err error) {
return 0, I2PAddr(""), errors.New("Could not parse incomming message remote address: " + err.Error())
}
// shift out the incomming address to contain only the data received
copy(b, buf[i+1:i+1+len(b)])
if ( n - i+1 ) > len(b) {
copy(b, buf[i+1:i+1+len(b)])
return n-(i+1), raddr, errors.New("Datagram did not fit into your buffer.")
} else {
copy(b, buf[i+1:n])
return n-(i+1), raddr, nil
}
return n-(i+1), raddr, nil
}
// Sends one signed datagram to the destination specified. At the time of

120
raw.go Normal file
View File

@ -0,0 +1,120 @@
package sam3
import (
"bytes"
"errors"
"net"
"strconv"
"time"
)
// The RawSession provides no authentication of senders, and there is no sender
// address attached to datagrams, so all communication is anonymous. The
// messages send are however still endpoint-to-endpoint encrypted. You
// need to figure out a way to identify and authenticate clients yourself, iff
// that is needed. Raw datagrams may be at most 32 kB in size. There is no
// overhead of authentication, which is the reason to use this..
type RawSession struct {
samAddr string // address to the sam bridge (ipv4:port)
id string // tunnel name
conn net.Conn // connection to sam bridge
udpconn *net.UDPConn // used to deliver datagrams
keys I2PKeys // i2p destination keys
rUDPAddr *net.UDPAddr // the SAM bridge UDP-port
}
// Creates a new raw session. udpPort is the UDP port SAM is listening on,
// and if you set it to zero, it will use SAMs standard UDP port.
func (s *SAM) NewRawSession(id string, keys I2PKeys, options []string, udpPort int) (*RawSession, error) {
if udpPort > 65335 || udpPort < 0 {
return nil, errors.New("udpPort needs to be in the intervall 0-65335")
}
if udpPort == 0 {
udpPort = 7655
}
lhost, _, err := net.SplitHostPort(s.conn.LocalAddr().String())
if err != nil {
s.Close()
return nil, err
}
lUDPAddr, err := net.ResolveUDPAddr("udp4", lhost + ":0")
if err != nil {
return nil, err
}
udpconn, err := net.ListenUDP("udp4", lUDPAddr)
if err != nil {
return nil, err
}
rhost, _, err := net.SplitHostPort(s.conn.RemoteAddr().String())
if err != nil {
s.Close()
return nil, err
}
rUDPAddr, err := net.ResolveUDPAddr("udp4", rhost + ":" + strconv.Itoa(udpPort))
if err != nil {
return nil, err
}
_, lport, err := net.SplitHostPort(udpconn.LocalAddr().String())
conn, err := s.newGenericSession("RAW", id, keys, options, []string{"PORT=" + lport})
if err != nil {
return nil, err
}
return &RawSession{s.address, id, conn, udpconn, keys, rUDPAddr}, nil
}
// Reads one raw datagram sent to the destination of the DatagramSession. Returns
// the number of bytes read. Who sent the raw message can not be determined at
// this layer - you need to do it (in a secure way!).
func (s *RawSession) Read(b []byte) (n int, err error) {
for {
// very basic protection: only accept incomming UDP messages from the IP of the SAM bridge
var saddr *net.UDPAddr
n, saddr, err = s.udpconn.ReadFromUDP(b)
if err != nil {
return 0, err
}
if bytes.Equal(saddr.IP, s.rUDPAddr.IP) {
continue
}
break
}
return n, nil
}
// Sends one raw datagram to the destination specified. At the time of writing,
// maximum size is 32 kilobyte, but this may change in the future.
func (s *RawSession) WriteTo(b []byte, addr I2PAddr) (n int, err error) {
header := []byte("3.0 " + s.id + " " + addr.String() + "\n")
msg := append(header, b...)
n, err = s.udpconn.WriteToUDP(msg, s.rUDPAddr)
return n, err
}
// Closes the RawSession.
func (s *RawSession) Close() error {
err := s.conn.Close()
err2 := s.udpconn.Close()
if err != nil {
return err
}
return err2
}
// Returns the local I2P destination of the RawSession.
func (s *RawSession) LocalAddr() I2PAddr {
return s.keys.Addr()
}
func (s *RawSession) SetDeadline(t time.Time) error {
return s.udpconn.SetDeadline(t)
}
func (s *RawSession) SetReadDeadline(t time.Time) error {
return s.udpconn.SetReadDeadline(t)
}
func (s *RawSession) SetWriteDeadline(t time.Time) error {
return s.udpconn.SetWriteDeadline(t)
}

View File

@ -4,7 +4,6 @@ package sam3
import (
"fmt"
"runtime"
"strings"
"testing"
"time"
@ -159,9 +158,6 @@ func Test_StreamingServerClient(t *testing.T) {
return
}
ncpu := runtime.NumCPU()
runtime.GOMAXPROCS(ncpu + 1)
fmt.Println("Test_StreamingServerClient")
sam, err := NewSAM(yoursam)
if err != nil {
@ -242,12 +238,9 @@ func Test_StreamingServerClient(t *testing.T) {
func Test_DatagramServerClient(t *testing.T) {
// if testing.Short() {
// return
// }
ncpu := runtime.NumCPU()
runtime.GOMAXPROCS(ncpu + 1)
if testing.Short() {
return
}
fmt.Println("Test_DatagramServerClient")
sam, err := NewSAM(yoursam)
@ -322,3 +315,80 @@ func Test_DatagramServerClient(t *testing.T) {
// fmt.Println("\tServer: Senders address was: " + saddr.Base32())
}
func Test_RawServerClient(t *testing.T) {
if testing.Short() {
return
}
fmt.Println("Test_RawServerClient")
sam, err := NewSAM(yoursam)
if err != nil {
t.Fail()
return
}
defer sam.Close()
keys, err := sam.NewKeys()
if err != nil {
t.Fail()
return
}
fmt.Println("\tServer: Creating tunnel")
rs, err := sam.NewDatagramSession("RAWserverTun", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"}, 0)
if err != nil {
fmt.Println("Server: Failed to create tunnel: " + err.Error())
t.Fail()
return
}
c, w := make(chan bool), make(chan bool)
go func(c, w chan(bool)) {
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")
rs2, err := sam2.NewDatagramSession("RAWclientTun", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"}, 0)
if err != nil {
c <- false
return
}
defer rs2.Close()
fmt.Println("\tClient: Tries to send raw datagram to server")
for {
select {
default :
_, err = rs2.WriteTo([]byte("Hello raw-world! <3 <3 <3 <3 <3 <3"), rs.LocalAddr())
if err != nil {
fmt.Println("\tClient: Failed to send raw datagram: " + err.Error())
c <- false
return
}
time.Sleep(5 * time.Second)
case <-w :
fmt.Println("\tClient: Sent raw datagram, quitting.")
return
}
}
c <- true
}(c, w)
buf := make([]byte, 512)
fmt.Println("\tServer: Read() waiting...")
n, _, err := rs.ReadFrom(buf)
w <- true
if err != nil {
fmt.Println("\tServer: Failed to Read(): " + err.Error())
t.Fail()
return
}
fmt.Println("\tServer: Received datagram: " + string(buf[:n]))
// fmt.Println("\tServer: Senders address was: " + saddr.Base32())
}