TAGS :Viewed: 8 - Published at: a few seconds ago

[ Python SSL Socket: receiving and sending from both server and client ]

OK, So I'm trying to get back and forth communication between server and client over an SSL socket connection. What I thought would be the best way to do this would have been to implement 2 threads in each, each thread acting as a server and a client respectively.But when I implement this code (obviously using opposite corresponding ports in other server/client):

#secserv.py

import socket
from OpenSSL import SSL
import threading
import time

class SecureIn(threading.Thread):
  context = SSL.Context(SSL.SSLv23_METHOD)
  context.use_privatekey_file('key')
  context.use_certificate_file('cert')

  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  s = SSL.Connection(context, s)
  s.bind(('', 5570))
  def run(self):
    while True:
      self.s.listen(5)
      (connection, address) = self.s.accept()
      print repr(connection.recv(5570))


class SecureOut(threading.Thread):
  time.sleep(6)
  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  s.connect(('localhost', 12345))
  sslSocket = socket.ssl(s)
  print repr(sslSocket.server())
  print repr(sslSocket.issuer())
  def run(self):
    sslSocket.write('Hello secure socket\n')

  s.close()


si = SecureIn()
si.start()
time.sleep(6)
so = SecureOut()
so.start()

I get this error:

Traceback (most recent call last):
  File "secserv.py", line 25, in <module>
    class SecureOut(threading.Thread):
  File "secserv.py", line 28, in SecureOut
    s.connect(('localhost', 12345))
  File "/usr/lib/python2.7/socket.py", line 224, in meth
    return getattr(self._sock,name)(*args)
socket.error: [Errno 111] Connection refused

Alternatively I have tried getting a standalone server to send even a broadcast message to all clients. I've searched high and low, but I can't seem to find a working way to do it with SSL socket, only regular sockets. When I try either s.write() or s.sendall() I get this error:

Traceback (most recent call last):
  File "secserv.py", line 19, in <module>
    s.write('hello client\n')
OpenSSL.SSL.Error: [('SSL routines', 'SSL_write', 'uninitialized')]

from this code:

import socket
from OpenSSL import SSL
context = SSL.Context(SSL.SSLv23_METHOD)
context.use_privatekey_file('key')
context.use_certificate_file('cert') 
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s = SSL.Connection(context, s)
s.bind(('', 12345))
while True:
    s.listen(5) 
    (connection, address) = s.accept()
    print repr(connection.recv(12345))
    #This could also be s.sendall()
    s.write('hello client\n')

Please help me StackOverFlow, you're my only hope. I know this should be easy, but my brain is so strained at this point, I just can't think anymore.

Also, I'm fairly new to python, so there is a large possibility that there's something about the way it operates/loads classes/etc, that I just don't get.

EDIT: OK, I know this code is bad. It isn't a product that's going to market, it will never be run live, this is just me trying to get a concept working, and that concept is: get server and client sending messages to each other over a python ssl connection.

I know this is terrible code, but I just need to know how to get the server to send a message back, because whenever I try it I get the error at the end.

Answer 1


Your design seems to be misguided at many levels.

First, you do not need two sockets to send data in both directions; sockets are bidirectional.

And you probably do not want to make a connection from the server back to the client—that might work in localhost testing, but once you deploy to the internet, most of the clients will be behind NAT routers, and won't have public addresses that you can connect to.

Meanwhile, your logic is very odd. You start the connection in one direction, then sleep 6 seconds, then start the connection in the other direction. Why would you want to do that?

If the server calls its SecureOut before, or even around the same time, as the client calls its SecureIn, it will try to connect before there's anyone listening, which will get exactly the error you're seeing.

Also, you've got a very weird run loop for each thread:

def run(self):
    while True:
        self.s.listen(5)
        (connection, address) = self.s.accept()
        print repr(connection.recv(5570))

This only accepts one connection at a time, does a single read from it, then just leaks the connection, never talking to it again, and goes to get the next client. (I'm also not sure if it is actually valid to call listen(5) again on a socket that's already listening, or what it does.) And of course there's no way it can ever finish. (That's not quite true—if, e.g., a client connects and then goes away before you call recv, you'll probably get an exception, which you don't catch, so your program will just exit…) Is that what you want?

Also, why recv(5570)? That number is the max number of bytes to read, not the port number.

And why print the repr of a string?

Meanwhile, do you realize that recv, no matter what you pass for the buffer length, is never guaranteed to get an entire message (or to get only one message instead of two, if you had a client that ever sent more than one)? It will almost certainly work that work on localhost, and with such a small message it will likely work most of the time on the internet, but not all the time.

You also seem to be confused about the difference between class variables and instance variables, because you're setting a bunch of class variables in both SecureIn and SecureOut. This means that if you could ever have two instances of the class, they'd share the same SSL.Context, etc. Even if you don't need to have two simultaneous instances in real life, for testing you'll almost certainly want to create a new instance, and you'll want that to create a new socket, etc., not keep using the old one. So instead of this:

class SecureIn(threading.Thread):
  context = SSL.Context(SSL.SSLv23_METHOD)
  context.use_privatekey_file('key')
  context.use_certificate_file('cert')
  …

Do this:

class SecureIn(threading.Thread):
  def __init__(self):
    self.context = SSL.Context(SSL.SSLv23_METHOD)
    self.context.use_privatekey_file('key')
    self.context.use_certificate_file('cert')
    …

(Or you could put that in the run method, of course.)

Basically, this isn't likely to do anything useful, with or without SSL. I would suggest you take a step back, go over some tutorials, and write something that works with plain-old TCP sockets, and then worry about how to SSL-ize it.