TLS echo client issue need your help

107 views
Skip to first unread message

Jon Zhu

unread,
Aug 9, 2011, 3:23:16 PM8/9/11
to golan...@googlegroups.com
Hi,

Can you help to figure out what's wrong with the following TLS test code? It's just a echo server, the expected result is to get echo string from server, but client always gets nothing. 


client.go:
===============================================================
package main

import ( "fmt" ; "crypto/tls"; "os" )


func main() {
        conn , err := tls.Dial("tcp", "127.0.0.1:8000", nil)
        if err != nil {
                fmt.Println("Fatal error ", err.String())
                os.Exit(1)
        }
        defer conn.Close()
        state := conn.ConnectionState()

        fmt.Println("handshaked: ", state.HandshakeComplete)
        fmt.Println("NegotiatedProtocolIsMutual: ", state.NegotiatedProtocolIsMutual)
        fmt.Println("local address: ", conn.LocalAddr())
        conn.Write([]byte("Hello"))
        var retbuf []byte
//        conn.SetReadTimeout(1000000)
        n, error := conn.Read(retbuf)
        fmt.Println(n, " bytes read from socket", error)
        fmt.Println(string(retbuf))
}
===============================================================

server.go
===============================================================
package main

import ("fmt"; "os"; "crypto/tls"; "net"; "crypto/rand"; "time")

func main() {

        cert, err := tls.LoadX509KeyPair("/etc/ssl/certs/ssl-cert-snakeoil.pem", "/etc/ssl/private/ssl-cert-snakeoil.key")
        //cert, err := tls.LoadX509KeyPair("jan.newmarch.name.pem", "private.pem")
        checkError(err)
        config := tls.Config {Certificates: []tls.Certificate {cert}}

        now := time.Seconds()
        config.Time = func() int64 { return now }
        config.Rand = rand.Reader

        service := "0.0.0.0:8000"

        listener, err := tls.Listen("tcp", service, &config)
        checkError(err)
        fmt.Println("Listening")
        for {
                conn, err := listener.Accept()
                if err != nil {
                        fmt.Println(err.String())
                        continue
                }
                fmt.Println("Accepted")
                //tlsConn := tls.Server(conn, &config)
                go handleClient(conn)
        }
}

func handleClient(conn net.Conn) {
        defer conn.Close()

        var buf [512]byte
        for {
                fmt.Println("Trying to read")
                n, err := conn.Read(buf[0:])
                if err != nil {
                        return
                }
                fmt.Println(string(buf[0:100]))
                _, err2 := conn.Write(buf[0:n])
                if err2 != nil {
                        return
                }
                fmt.Println("Echo done")
//                time.Sleep(5000000);
        }
}

func checkError(err os.Error) {
        if err != nil {
                fmt.Println("Fatal error ", err.String())
                os.Exit(1)
        }
}

======================================================

Use the following openssl test tool can always get correct result. 
openssl s_client -tls1 -connect localhost:8000

Thanks,
-Jon.



Kyle Lemons

unread,
Aug 9, 2011, 4:15:37 PM8/9/11
to Jon Zhu, golan...@googlegroups.com
Just a quick note: you're not checking all of your errors, notably conn.Write in the client.  If (as you seem to indicate) the echo server works fine, I would take a closer look at this in the client.  You also just say "the client always gets nothing" but this could mean that it doesn't receive a packet or that it reads zero-length and then exits.  Sample output of both programs would be helpful.  (For this sort of thing, I like using gist.github.com, where I can post the files and the output as separate files and not use up email realestate.)
~K

Jon Zhu

unread,
Aug 9, 2011, 4:24:16 PM8/9/11
to Kyle Lemons, golan...@googlegroups.com
Here you go: 

server output:
root@ip-10-77-38-236:/home/ubuntu/go/jon/tls# ./server
Listening
Accepted
Trying to read
Hello
Echo done
Trying to read

client output: 
root@ip-10-77-38-236:/home/ubuntu/go/jon/tls# ./client
handshaked:  true
NegotiatedProtocolIsMutual:  true
local address:  127.0.0.1:53980
0  bytes read from socket <nil>

client read call returns nil error, means the read is successful but length is zero, any suggestion for TLS read code? 

Thanks,
-Jon.

Jon Zhu

unread,
Aug 9, 2011, 4:28:08 PM8/9/11
to Kyle Lemons, golan...@googlegroups.com
Posted on gist: 
https://gist.github.com/1135104

Thanks for any suggestions!

-Jon.

Tobias Matt

unread,
Aug 9, 2011, 4:43:09 PM8/9/11
to golang-nuts
> >>>         var retbuf []byte
> >>> //        conn.SetReadTimeout(1000000)
> >>>         n, error := conn.Read(retbuf)

retbuf has a size of 0. It can't read anything.

Kyle Lemons

unread,
Aug 9, 2011, 4:53:04 PM8/9/11
to Tobias Matt, golang-nuts
retbuf has a size of 0. It can't read anything.

Yep.


I made it somewhat more idiomatic too.

Jon Zhu

unread,
Aug 9, 2011, 4:59:40 PM8/9/11
to Tobias Matt, golang-nuts
Thanks, Tobias & Tobias. That's the problem. 

I used to use a fix length buffer but it won't compile. 
 var retbuf [256]byte

root@ip-10-77-38-236:/home/ubuntu/go/jon/tls# mk client
building client
client.go:21: cannot use retbuf (type [256]uint8) as type []uint8 in function argument
client.go:23: cannot convert retbuf (type [256]uint8) to type string
cannot open file: client.8

The compiler error leaded me to use var retbuf []byte, then comes the zero size buffer problem. 

After you pointing it out I used the following code, it works. 
retbuf := make([]byte, 256)


This part of Go language looks little bit strange to traditional C programmer. 

-Jon.

Paul Borman

unread,
Aug 9, 2011, 5:03:15 PM8/9/11
to Jon Zhu, Tobias Matt, golang-nuts
You can also just use rebuf[:].  But yes, this is something that takes a C program by surprise initially.

Paul Borman

unread,
Aug 9, 2011, 5:03:31 PM8/9/11
to Jon Zhu, Tobias Matt, golang-nuts
s/program/programmer :-)

Kyle Lemons

unread,
Aug 9, 2011, 5:06:11 PM8/9/11
to Jon Zhu, golang-nuts
You can also just use rebuf[:].  But yes, this is something that takes a C program by surprise initially.

You actually did this correctly in the server code (if somewhat verbosely, you don't need [0:], both start and end are implied if you omit them).  If that was copied from somewhere, I'll fall back to my suggestion to anyone learning a language: always write everything from scratch when you're learning; copy/paste is your enemy.  You'll make a ton of mistakes, but you'll learn from them.

~K

Jon Zhu

unread,
Aug 9, 2011, 5:12:30 PM8/9/11
to Paul Borman, Tobias Matt, golang-nuts
Cool. Thanks, all.
Reply all
Reply to author
Forward
0 new messages