How do TLS connections in EventMachine work? - ssl

I have a custom Protobuf-based protocol that I've implemented as an EventMachine protocol and I'd like to use it over a secure connection between the server and clients. Each time I send a message from a client to the server, I prepend the message with a 4-byte integer representing the size of the Protobuf serialized string to be sent such that the server knows how many bytes to read off the wire before parsing the data back into a Protobuf message.
I'm calling start_tls in the post_init callback method in both the client and server protocol handlers, with the one in the server handler being passed the server's private key and certificate. There seems to be no errors happening at this stage, based on log messages I'm printing out.
Where I get into trouble is when I begin parsing data in the receive_data callback in the server's handler code... I read 4 bytes of data off the wire and unpack it to an integer, but the integer that gets unpacked is not the same integer I send from the client (i.e. I'm sending 17, but receiving 134222349).
Note that this does not happen when I don't use TLS... everything works fine if I remove the start_tls calls in both the client and server code.
Is it the case that SSL/TLS data gets passed to the receive_data callback when TLS is used? If so, how do I know when data from the client begins? I can't seem to find any example code that discusses this use case...

OK, so via a cross-post to the EventMachine Google Group I figured out what my problem was here. Essentially, I was trying to send data from the client to the server before the TLS handshake was done because I wasn't waiting until the ssl_handshake_completed callback was called.
Here's the code I got to work, just in case anyone comes across this post in the future. :)
Handler code for the server-side:
require 'eventmachine'
class ServerHandler < EM::Connection
def post_init
start_tls :private_key_file => 'server.key', :cert_chain_file => 'server.crt', :verify_peer => false
end
def receive_data(data)
puts "Received data in server: #{data}"
send_data(data)
end
end
Handler code for the client-side:
require 'eventmachine'
class ClientHandler < EM::Connection
def connection_completed
start_tls
end
def receive_data(data)
puts "Received data in client: #{data}"
end
def ssl_handshake_completed
send_data('Hello World! - 12345')
end
end
Code to start server:
EventMachine.run do
puts 'Starting server...'
EventMachine.start_server('127.0.0.1', 45123, ServerHandler)
end
Code to start client:
EventMachine.run do
puts 'Starting client...'
EventMachine.connect('127.0.0.1', 45123, ClientHandler)
end

Related

SOLVED Handle multiple AMQP Pattern inside one Rails Service using Bunny gem

SOLVED PROBLEM.
Just create 2 different queues, like rpc.queue and pubsub.queue. Then you can use multiple messaging pattern in one service without any problem.
I create one rails service using Bunny and ConnectionPool Gem. This service "in my mind (cause not yet implemented)" handle multiple RMQ pattern such as Direct Messaging and RPC. These patterns initialized with different object of Connection Class and defined inside initalizer folder.
Initializer looks like this:
# RMQ Initializer for RabbitMQ Connection
class RMQ
include Lontara::RMQ
# NOTE: Call 2 Server caused errors
def self.start(url:, queue:, rpc_exchange:, pubsub_exchange:)
# Then start the consumer and subscriber
Server::RPCConsumer.new(Connection.new(url:), queue:, exchange: rpc_exchange).consume
Server::Subscriber.new(Connection.new(url:), queue:, exchange: pubsub_exchange).subscribe
end
end
RMQ.start(
url: ENV.fetch('RABBITMQ_URL', 'amqp://guest:guest#rmqserver:5672'),
queue: ENV.fetch('RABBITMQ_QUEUE_VOUCHER', 'lontara-dev.voucher'),
rpc_exchange: ENV.fetch('RABBITMQ_EXCHANGE_RPC', 'lontara-dev.rpc'),
pubsub_exchange: ENV.fetch('RABBITMQ_EXCHANGE_PUBSUB', 'lontara-dev.pubsub')
)
and Connection class:
module Lontara
module RMQ
# Class Connection initializing the connection to RabbitMQ.
class Connection
def initialize(url: ENV['RABBITMQ_URL'])
#connection = Bunny.new(url)
connection.start
#channel = channel_pool.with(&:create_channel)
yield self if block_given?
end
def close
channel.close
connection.close
end
attr_reader :connection, :channel
private
def channel_pool
#channel_pool ||= ConnectionPool.new { #connection }
end
end
end
end
The problem goes whenever these 2 Server:: (RPC and Subscriber) activated. Impacted only when use RPC as messaging, the problem is RPC Publisher does not get response from Consumer.
These steps (when RPC produce error) are:
Run Rails server
Open new terminal, and open rails console in same project
Create Request to Consumer using RPCPublisher
Publisher get response. Then send request again... On this step not get response.
Job is pending, i push ctrl+c to terminate job. Send request again, and get response...
Try again like step 4, and error...
But, if Server::Publisher not initialized on initializer, nothing error happened.
I assumed this error happened cause of thread... But i don't really get helped from other articles on internet.
My expectation is so simple:
RPC Connection requested for Get related (because RPC can reply this request) or other action requires response. And Pub/Sub (Direct) request for Create, Update, Delete since this type didn't need it.
Your answer really help me... Thankyou !

LSP4J : Language Server method call never ends

I have created a Java-based LSP client, but none of the method calls are completed & it waits indefinitely.
Socket socket = new Socket("localhost", 6008);
Launcher<LanguageServer> createClientLauncher = LSPLauncher.createClientLauncher (languageClient,
socket.getInputStream(), socket.getOutputStream());
LanguageServer server = createClientLauncher.getRemoteProxy();
createClientLauncher.startListening();
InitializeResult result = server.initialize(new InitializeParams()).get();
System.out.println("end");
initialize method never returns. The Language Server is working fine when tested with the VSCode instance.
Seems like requests are not reaching the server as nothing is printed in trace logs of server.

is there a way to setup timeout in grpc server side?

Unable to timeout a grpc connection from server side. It is possible that client establishes a connection but kept on hold/sleep which is resulting in grpc server connection to hang. Is there a way at server side to disconnect the connection after a certain time or set the timeout?
We tried disconnecting the connection from client side but unable to do so from server side. In this link Problem with gRPC setup. Getting an intermittent RPC unavailable error, Angad says that it is possible but unable to define those parameters in python.
My code snippet:
def serve():
server = grpc.server(thread_pool=futures.ThreadPoolExecutor(max_workers=2), maximum_concurrent_rpcs=None, options=(('grpc.so_reuseport', 1),('grpc.GRPC_ARG_KEEPALIVE_TIME_MS', 1000)))
stt_pb2_grpc.add_ListenerServicer_to_server(Listener(), server)
server.add_insecure_port("localhost:50051")
print("Server starting in port "+str(50051))
server.start()
try:
while True:
time.sleep(60 * 60 * 24)
except KeyboardInterrupt:
server.stop(0)
if __name__ == '__main__':
serve()
I expect the connection should be timed out from grpc server side too in python.
In short, you may find context.abort(...) useful, see API reference. Timeout a server handler is not supported by the underlying C-Core API of gRPC Python. So, you have to implement your own timeout mechanism in Python.
You can try out some solution from other StackOverflow questions.
Or use a simple-but-big-overhead extra threads to abort the connection after certain length of time. It might look like this:
_DEFAULT_TIME_LIMIT_S = 5
class FooServer(FooServicer):
def RPCWithTimeLimit(self, request, context):
rpc_ended = threading.Condition()
work_finished = threading.Event()
def wrapper(...):
YOUR_ACTUAL_WORK(...)
work_finished.set()
rpc_ended.notify_all()
def timer():
time.sleep(_DEFAULT_TIME_LIMIT_S)
rpc_ended.notify_all()
work_thread = threading.Thread(target=wrapper, ...)
work_thread.daemon = True
work_thread.start()
timer_thread = threading.Thread(target=timer)
timer_thread.daemon = True
timer_thread.start()
rpc_ended.wait()
if work_finished.is_set():
return NORMAL_RESPONSE
else:
context.abort(grpc.StatusCode.DEADLINE_EXCEEDED, 'RPC Time Out!')

Twisted big files transfer

I write client-server application like this:
client(c#) <-> server (twisted; ftp proxy and additional functional) <-> ftp server
Server has two classes: my own class-protocol inherited from LineReceiever protocol and FTPClient from twisted.protocols.ftp.
But when client sends or gets big files (10 Gb - 20 Gb) server catches MemoryError. I don't use any buffers in my code. It happens when after call transport.write(data) data appends to inner buffer of reactor's writers (correct me if I wrong).
What should I use to avoid this problem? Or should I change approach to the problem?
I found out that for big streams, I should use IConsumer and IProducer interfaces. But finally it will invoke transfer.write method and effect will be the same. Or am I wrong?
UPD:
Here is logic of file download/upload (from ftp through Twisted server to client on Windows):
Client sends some headers to Twisted server and after that begins send of file. Twisted server receive headers and after that (if it needs) invoke setRawMode(), open ftp connection and recieves/sends bytes from/to client and after all close connections. Here is a part of code that uploads files:
FTPManager class
def _ftpCWDSuccees(self, protocol, fileName):
self._ftpClientAsync.retrieveFile(fileName, FileReceiver(protocol))
class FileReceiver(Protocol):
def __init__(self, proto):
self.__proto = proto
def dataReceived(self, data):
self.__proto.transport.write(data)
def connectionLost(self, why = connectionDone):
self.__proto.connectionLost(why)
main proxy-server class:
class SSDMProtocol(LineReceiver)
...
After SSDMProtocol object (call obSSDMProtocol) parse headers it invoke method that open ftp connection (FTPClient from twisted.protocols.ftp) and set object of FTPManager field _ftpClientAsync and call _ftpCWDSuccees(self, protocol, fileName) with protocol = obSSDMProtocol and when file's bytes recieved invokes dataReceived(self, data) of FileReceiver object.
And when self.__proto.transport.write(data) invoked, data appends to inner buffer faster than sending back to client, therefore memory runs out. May be I can stop reading when the buffer reaches a certain size and resume reading after buffer will be all send to client? or something like that?
If you're passing a 20 gigabyte (gigabit?) string to transport.write, you're going to need at least 20 gigabytes (gigabits?) of memory - probably more like 40 or 60 due to the extra copying necessary when dealing with strings in Python.
Even if you never pass a single string to transport.write that is 20 gigabytes (gigabits?), if you repeatedly call transport.write with short strings at a rate faster than your network can handle, the send buffer will eventually grow too large to fit in memory and you'll encounter a MemoryError.
The solution to both of these problems is the producer/consumer system. The advantage that using IProducer and IConsumer gives you is that you'll never have a 20 gigabyte (gigabit?) string and you'll never fill up a send buffer with too many shorter strings. The network will be throttled so that bytes are not read faster than your application can deal with them and forget about them. Your strings will end up on the order of 16kB - 64kB, which should easily fit in memory.
You just need to adjust your use of FileReceiver to include registration of the incoming connection as a producer for the outgoing connection:
class FileReceiver(Protocol):
def __init__(self, outgoing):
self._outgoing = outgoing
def connectionMade(self):
self._outgoing.transport.registerProducer(self.transport, streaming=True)
def dataReceived(self, data):
self._outgoing.transport.write(data)
Now whenever self._outgoing.transport's send buffer fills up, it will tell self.transport to pause. Once the send buffer empties out, it will tell self.transport to resume. self.transport nows how to undertake these actions at the TCP level so that data coming into your server will also be slowed down.

on a non blocking socket, implementing renegotiation using openssl 0.9.8m and beyond

openssl has added secure renegotiation in 0.9.8m and later versions. How do I handle this case?
in a server, using a non blocking socket, say an SSL_accept is done. Now the server will check to see if the socket becomes readable. If it, then there are two possibilies, either the socket became readable because the client sent some request OR the client renegotiatied the connection. SSL_MODE_AUTO_RETRY is not applicable as I understnad on non blocking sockets.
If the client sent request, SSL_Read needs to be invoked. If the client renegotiated, SSL_accept has to be called.
If I call SSL_read when client did renegotiation, it returns with SSL_ERROR_SSL. So how do I determine if I should do SSL_accept or SSL_read. Calling SSL_read again returns with same error.
Thanks for any inputs
I am guessing you are using C. You will have to check the error code and maintain a state of session at your end. If the case is either success or needsRead or needsWrite you will have to call the respective function. Initially set the status as needsAccept. the accept function will automatically based on session decide if read or write is needed.
int retCode = SSL_get_error(ssl, retNum);
switch (retCode) {
case SSL_ERROR_NONE:
return "success";
case SSL_ERROR_WANT_READ:
LOG_DEBUG("SSL_ERROR_WANT_READ");
return "needsRead";
case SSL_ERROR_WANT_WRITE:
return "needsWrite";
case SSL_ERROR_ZERO_RETURN:
case SSL_ERROR_SSL:
case SSL_ERROR_SYSCALL:
return "socket should be closed";
}