From ce26df96ac11467d47d18fa50dc59a07b4513619 Mon Sep 17 00:00:00 2001 From: Henry Date: Mon, 10 Feb 2014 21:01:55 +0100 Subject: [PATCH] Trying to implement Dial for usage with http.Transport --- client.go | 3 +-- dial.go | 42 ++++++++++++++++++++++++++++++++ naming.go | 70 +++++++++++++++++++++++++---------------------------- sessions.go | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++ stream.go | 52 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 194 insertions(+), 39 deletions(-) create mode 100644 dial.go create mode 100644 sessions.go create mode 100644 stream.go diff --git a/client.go b/client.go index da9368c..e6b50fe 100644 --- a/client.go +++ b/client.go @@ -3,12 +3,11 @@ package goSam import ( "bufio" "fmt" - "io" "net" ) type Client struct { - samConn io.ReadWriteCloser + samConn net.Conn fromSam *bufio.Reader toSam *bufio.Writer diff --git a/dial.go b/dial.go new file mode 100644 index 0000000..0acc7c5 --- /dev/null +++ b/dial.go @@ -0,0 +1,42 @@ +package goSam + +import ( + "fmt" + "net" + "strings" +) + +// implements the net.Dial function to be used as http.Transport +func (c *Client) Dial(network, addr string) (net.Conn, error) { + addr = addr[:strings.Index(addr, ":")] + addr, err := c.Lookup(addr) + if err != nil { + return nil, err + } + + fmt.Println("Dial Lookup:", addr) + + id, _, err := c.createStreamSession("") + if err != nil { + return nil, err + } + + newC, err := NewDefaultClient() + if err != nil { + return nil, err + } + + if newC.Hello() != nil { + return nil, err + } + + fmt.Println("newC Hello OK") + + if newC.StreamConnect(id, addr) != nil { + return nil, err + } + + fmt.Println("StreamConnect OK") + + return newC.samConn, nil +} diff --git a/naming.go b/naming.go index a271d75..3fd5d06 100644 --- a/naming.go +++ b/naming.go @@ -4,26 +4,25 @@ import ( "fmt" ) -type Result int - const ( - ResultOk Result = iota //Operation completed successfully - ResultCantReachPeer //The peer exists, but cannot be reached - ResultDuplicatedDest //The specified Destination is already in use - ResultI2PError //A generic I2P error (e.g. I2CP disconnection, etc.) - ResultInvalidKey //The specified key is not valid (bad format, etc.) - ResultKeyNotFound //The naming system can't resolve the given name - ResultPeerNotFound //The peer cannot be found on the network - ResultTimeout // Timeout while waiting for an event (e.g. peer answer) + ResultOk = "OK" //Operation completed successfully + ResultCantReachPeer = "CANT_REACH_PEER" //The peer exists, but cannot be reached + ResultDuplicatedId = "DUPLICATED_ID" //If the nickname is already associated with a session : + ResultDuplicatedDest = "DUPLICATED_DEST" //The specified Destination is already in use + ResultI2PError = "I2P_ERROR" //A generic I2P error (e.g. I2CP disconnection, etc.) + ResultInvalidKey = "INVALID_KEY" //The specified key is not valid (bad format, etc.) + ResultKeyNotFound = "KEY_NOT_FOUND" //The naming system can't resolve the given name + ResultPeerNotFound = "PEER_NOT_FOUND" //The peer cannot be found on the network + ResultTimeout = "TIMEOUT" // Timeout while waiting for an event (e.g. peer answer) ) type ReplyError struct { - Result Result + Result string Reply *Reply } func (r ReplyError) Error() string { - return fmt.Sprintf("ReplyError: Result:%d - Reply:%+v", r.Reply) + return fmt.Sprintf("ReplyError: Result:%s - Reply:%+v", r.Result, r.Reply) } func (c *Client) Lookup(name string) (addr string, err error) { @@ -40,36 +39,33 @@ func (c *Client) Lookup(name string) (addr string, err error) { line string r *Reply ) - for { - line, err = c.fromSam.ReadString('\n') - if err != nil { - return - } - r, err = parseReply(line) - if err != nil { - break - } + line, err = c.fromSam.ReadString('\n') + if err != nil { + return + } - if r.Topic != "NAMING" || r.Type != "REPLY" { - err = fmt.Errorf("Unknown Reply: %+v\n", r) - break - } + r, err = parseReply(line) + if err != nil { + return + } - switch r.Pairs["RESULT"] { - case "OK": - addr = r.Pairs["VALUE"] - return - case "KEY_NOT_FOUND": - err = ReplyError{ResultKeyNotFound, r} - } + if r.Topic != "NAMING" || r.Type != "REPLY" { + err = fmt.Errorf("Unknown Reply: %+v\n", r) + return + } - if r.Pairs["NAME"] != name { - err = fmt.Errorf("i2p Replyied with: %+v\n", r) - break - } + switch r.Pairs["RESULT"] { + case "OK": + addr = r.Pairs["VALUE"] + return + case "KEY_NOT_FOUND": + err = ReplyError{ResultKeyNotFound, r} + return + } - break + if r.Pairs["NAME"] != name { + err = fmt.Errorf("i2p Replyied with: %+v\n", r) } return diff --git a/sessions.go b/sessions.go new file mode 100644 index 0000000..ba59a54 --- /dev/null +++ b/sessions.go @@ -0,0 +1,66 @@ +package goSam + +import ( + "fmt" + "math" + "math/rand" +) + +func (c *Client) createStreamSession(dest string) (id int32, newDest string, err error) { + if dest == "" { + dest = "TRANSIENT" + } + + id = rand.Int31n(math.MaxInt32) + createCmd := fmt.Sprintf("SESSION CREATE STYLE=STREAM ID=%d DESTINATION=%s\n", id, dest) + _, err = c.toSam.WriteString(createCmd) + if err != nil { + return + } + + if err = c.toSam.Flush(); err != nil { + return + } + + var ( + line string + r *Reply + ) + + line, err = c.fromSam.ReadString('\n') + if err != nil { + return + } + fmt.Println("createStreamSession line:", line) + + r, err = parseReply(line) + if err != nil { + return + } + + if r.Topic != "SESSION" || r.Type != "STATUS" { + err = fmt.Errorf("Unknown Reply: %+v\n", r) + return + } + + switch r.Pairs["RESULT"] { + case ResultOk: + fmt.Println("createStreamSession created") + newDest = r.Pairs["DESTINATION"] + return + case ResultDuplicatedId: + err = ReplyError{ResultDuplicatedId, r} + return + case ResultDuplicatedDest: + err = ReplyError{ResultDuplicatedDest, r} + return + case ResultInvalidKey: + err = ReplyError{ResultInvalidKey, r} + return + case ResultI2PError: + err = ReplyError{ResultKeyNotFound, r} + return + } + + return +} diff --git a/stream.go b/stream.go new file mode 100644 index 0000000..82e7d94 --- /dev/null +++ b/stream.go @@ -0,0 +1,52 @@ +package goSam + +import ( + "fmt" +) + +func (c *Client) StreamConnect(id int32, dest string) error { + connectCmd := fmt.Sprintf("STREAM CONNECT ID=%d DESTINATION=%s\n", id, dest) + _, err := c.toSam.WriteString(connectCmd) + if err != nil { + return err + } + + if err = c.toSam.Flush(); err != nil { + return err + } + + var ( + line string + r *Reply + ) + + line, err = c.fromSam.ReadString('\n') + if err != nil { + return err + } + + r, err = parseReply(line) + if err != nil { + return err + } + + if r.Topic != "STREAM" || r.Type != "STATUS" { + return fmt.Errorf("Unknown Reply: %+v\n", r) + } + + switch r.Pairs["RESULT"] { + case ResultOk: + fmt.Println("StreamConnect OK") + return nil + case ResultDuplicatedId: + return ReplyError{ResultDuplicatedId, r} + case ResultDuplicatedDest: + return ReplyError{ResultDuplicatedDest, r} + case ResultInvalidKey: + return ReplyError{ResultInvalidKey, r} + case ResultI2PError: + return ReplyError{ResultKeyNotFound, r} + } + + return nil +}