Demonstrations of how to use SSLtcl Peter Antman SSLtcl main program is a loadable module SSLtcl.so which can be loaded by tclsh or wish at any time. It then gives the programmer access to an extended version of tcl sockets. If you already know how to use the socket command then read the manual page and get on with it. If you feel unsure how to use sockets under tcl then have a look in demo. There you find six example programs that utilize sockets with SSL. They show different ways of configuring the socket under tcl. The tcl socket command is among the most easiest ways to create applications that can communicate over tcp/ip. Its most fundamental aspect is that it either creates a client socket that directly tries to connect to a server. Or it creates a server socket that listens on the specified port and then creates additional sockets for each client that connect so that is can service more than one client at a time. To get tcl sockets to work in this nice way you have to have it in an event loop and you have to configure the channel the socket is wrapped in so that the program can take back command when it has started serving a client. The scripts in the demo directory contain different solutions to this problem. The scripts tstCli.tcl and tstSrv.tcl is two simple scripts that utilize sockets and SSL in an easy way; but with the negative sideffect that the server only will service one client at time. Lets take a look at tstCli.tcl 9 set shouldbe [exec cat tstCli.tcl] 10 load ../SSLtcl.so SSLtcl 11 ... 16 set s [socket -ssl -verify 1 -ciphers RC4-MD5 -cert demoCA/newcert.pem -key \ 17 demoCA/newkey.pem -CAfile demoCA/cacert.pem localhost 443] 18 19 puts $s ''GET /'' 20 puts ''Req complete'' 21 flush $s 22 set got '''' 23 while { [gets $s l ] >= 0 } { 24 if { $got == '''' } { 25 set got $l 26 } else { 27 set got ''$got\n$l'' 28 } 29 # puts ''RECV($l)'' 30 } 31 close $s 32 puts ''Client done'' 33 if { $got == $shouldbe } { 34 puts ''Client successful!!!'' 35 } else { 36 puts ''Bummer!!! Client did not rec what it should have!'' 37 exit -1; 38 } 39 As you see it loads in SSLtcl.so in line 10. SSLtcl.so is not as is common under C based applications loaded from the best found location. Therefore you have to specify the full path. For example load /usr/local/lib/SSLtcl.so SSLtcl tstCli.tcl is a string fall through application. On line 16 it opens a socket and sets certain characteristics of it. Here we make it a client socket which uses SSL. All possible options is used in this example. The application will have its own certificate and will also have a file which contain certificate from Certificate Authorities it trusts. It also tells that it will only tolerate use of a special cipher. The socket command returns a channel identifier which we now can use to read from and write to. On line 19-21 the application writes to the socket and flushed the channel so that it really sends something. Because we are using SSL the communication with the server will take place over a cryptated tunnel. We then go in a loop and wait for something to read on the channel on lone 23-29. When we get something we immediately closes the cannel, as on line 31. This could be the basis of a webbrowser fetching webpages. That is, for a connectionless protocol. The server tstSrv.tcl is not that useful for real life problems, because it blocks while servicing clients. But is fairly easy to understand. 9 set data [exec cat tstCli.tcl] 10 11 # This is the callback procedure that has to be part of the socket call 12 proc getit { s ip port args } { 13 global data 14 puts $args 15 puts ''Server socket ($s $ip $port)'' 16 set req [gets $s]; 17 puts ''RECV($req)'' 18 if { $req == ''GET /'' } { 19 puts $s $data 20 } else { 21 puts ''UNKNOWN request'' 22 puts $s ''UNKNOWN request'' 23 } 24 close $s 25 } 26 27 # load the module, with full path to it 28 load ../SSLtcl.so SSLtcl 29 set s [socket -server getit -ssl -Verify 1 -cert \ 30 demoCA/newcert.pem -key demoCA/newkey.pem \ 31 -CAfile demoCA/cacert.pem 443] 32 puts $s 33 set tst 0 34 puts ''Server waiting connection on $s'' 35 36 # Go into the eventloop 37 vwait tst The main different between the client socket and a server socket is that the application have to enter an event loop. A server program is therefore not as straight a client program. Every server socket has to have a command to call back when a request arrives. At line 29 the channel is created. As we see we have to tell tcl that wee want a server socket with the switch -server. This option have a necessary argument, namely a callback command. This is created on line 12-25. When a client connects this client will be handed over to the callback command, in this case a procedure. The procedure is rather straightforward. It read from the channel with gets (line 16). Observe that this is not the same channel as that returned by the socket command, but is a new channel given as a argument to the procedure. It then writes back to the channel with puts $channelid something. At line 36 we went into an event loop. Nothing would have happen on the socket channel before we did go into that loop because tcl would not know how to handle the callback but would be busy doing other things. A connections is closed by closing the connection socket. The server socket is closed first when you close the channel you got back from the socket command. To do something more useful one would like at least the server to be able to respond to more than one client at a time. tstSrvReal is an example of such a client. It is basically build on two callback. One for the server socket to call, and one for every connections socket to call. To get a callback from every socket when it need attention wee have to configure it for that. This is done with the command filevent. At line 36 you can see how it is used in this example. 9 # server writing back to client 10 proc getit_fork {s l} { 11 puts $s $l 12 flush $s 13 } 14 15 # the callback from filevent 16 proc getit_hand {s} { 17 puts filevent 18 set l [gets $s] ;# get the client packet 19 puts ''got $l'' 20 if {[eof $s]} { ;# client gone or finished 21 close $s ;# release the servers client channel 22 } elseif {$l == ''Q''} { 23 close $s 24 exit 25 } elseif {$l == ''q''} { 26 close $s 27 return 28 } else { 29 getit_fork $s $l 30 } 31 } 32 33 # The connection callback, called from ssl 34 proc getit { s ip port args } { 35 puts ''in getit'' 36 fileevent $s readable [list getit_hand $s] 37 38 39 fconfigure $s -buffering line -blocking 0 40 return 41 42 } 43 44 # load the module 45 load ../SSLtcl.so SSLtcl 46 47 # create socket, with ssl 48 set s [socket -server getit -ssl -Verify 1 -cert \ 49 demoCA/newcert.pem -key demoCA/newkey.pem \ 50 -CAfile /home/peter/ssl/demoCA/cacert.pem 443] 51 puts ''Server waiting connection on $s'' 52 53 # Go into the eventloop 54 vwait events The main part of the program is rather short. On line 45 to 54 we load the module, creates the socket and goes into the event loop. But in the callback procedure we give to the -server option we behave differently. There we instead gives a new callback, configures the new channel and returns so that the main program can continue on. The filevent command is used to get tcl to call a procedure when there is something happening on the cannel. In our case we want is to call the procedure getit_hand when the channel is readable. And we send the new channel as an argument. We also configure the cannel. Especially useful is to configure it for non blocking mode. If we do not do that the server will not be able to service new client until the first connected has at least send something, which means that a client could easily freeze the whole server. In the new callback we read in what's in the socket and examine its connect. Useful is to check for End Of File, which is done on line 20 so that we know if the client has disappeared. The stsCliReal.tcl is based on the same principles that tstSrvReal, that is, we implement an event loop and a callback even in the client. This is particularly good for connections that will be durable. 9 load ../SSLtcl.so SSLtcl 10 11 # I'm repeating this a few times to check for 12 # problems that may accumulate like file descriptor not being 13 # closed. 14 proc read_sock {sock} { 15 set l [gets $sock] 16 if {[eof $sock]} { 17 close $sock 18 set eventLoop ''done'' 19 } 20 puts stdout ''ServerReply:$l'' 21 } 22 23 # Read a line of text from stdin and send it to the echoserver socket, 24 # on eof stdin closedown the echoserver client socket connection 25 # this implements sending a message to the Server. 26 proc read_stdin {wsock} { 27 global eventLoop 28 set l [gets stdin] 29 puts ''l: $l'' 30 if {[eof stdin]} { 31 close $wsock ;# close the socket client connection 32 set eventLoop ''done'' ;# terminate the vwait (eventloop) 33 } elseif {$l == ''Q''} { 34 puts $wsock $l 35 close $wsock 36 exit 37 } elseif {$l == ''q''} { 38 puts $wsock $l 39 close $wsock 40 exit 41 } else { 42 puts $wsock $l ;# send the data to the server 43 } 44 } 45 46 # Create the socket and ssl enables it 47 set s [socket -ssl -verify 1 -ciphers RC4-MD5 -cert demoCA/newcert.pem -key \ 48 demoCA/newkey.pem -CAfile demoCA/cacert.pem localhost 443] 49 50 puts $s 51 fileevent $s readable [list read_sock $s] 52 fconfigure $s -buffering line 53 fileevent stdin readable [list read_stdin $s] 54 vwait eventLoop 55 puts ''Finished'' In this we create the socket and then a callback for that socket with filevent on lines 47 and 51. We also creates a callback for standardin. This means that we can type something on the screen at it will be sent to the server. If something is on standardin we read it and sends it to the server. If something is on the socket, we reads it and prints it to the terminal. On line 54 we go into an eventloop by waiting on an event on the variable eventLoop. The two scripts tstSrvRealNoSSL.tcl and tstCliRealNoSSL.tcl is exactly the same as the above scripts, except that they do not configure the socket for SSL (but it uses the socket command provided by SSLtcl.)