how to ensure message sending with ssl - erlang

I am implementing as exercise a gen_server which behaves as interface towards an ssl authentication server. The ssl server severes the connection if a packet received is wrong (e.g. wrong username and password). The connection must be persistent.
In my gen_server, I open the ssl connection towards the server with an handle_cast/2:
handle_cast(connect, State) ->
......
case ssl:connect(Address, Port, Options, Timeout) of
{ok, NewSocket} ->
{noreply, State#state{socket=NewSocket}};
{error, Reason} ->
gen_server:cast(?SERVER, connect),
{noreply, State#state{socket=undefined}};
and then I wait for other other messages in handle_cast/2 which can be sent for example using:
gen_server:cast(Pid, {authenticate, User, Password}).
Whenever I receive such a cast message I spawn a new function which recovers the SSL socket from the server state using a gen_server:call/3 and sends the authentication message to the SSL server. If the sending part returns an error I try to reconnect, otherwise I read for a while on the socket, to be sure that the socket does not go down, and if it does I reconnect.
send_auth(_, _, 0) ->
{error, max_num_reached};
send_auth(User, Password, Num) ->
Socket = gen_server:call(?SERVER, socket),
%% also a check that socket is not 'undefined'
case ssl:send(Socket, AuthMessage) of
ok ->
case ssl:recv(Socket, 0, 2000) of
{error, timeout} ->
ok;
_ ->
gen_server:cast(?SERVER, connect),
send_auth(User, Password, Num-1)
end,
{error, closed} ->
gen_server:cast(?SERVER, connect),
send_auth(User, Password, Num-1)
end.
I made many tests, but every time, if one message (not the last) is wrong, none of the following messages is actually delivered.
How can I grant that all valid authentication messages are delivered to the authentication server? Moreover how can be sure that the server will connect only if it is not already trying to do so? Otherwise that would be like DOS attack!

As I can understand there's only one authentication server in the system so why can't you connect to it right in the gen_server's init? If the connection is somewhat reliable, you can just kill gen_server if there are any issues with connection and let supervisor re-launch it. It probably will solve your DOS concern.
When you spawn additional processes with send_auth you can just pass socket connection in arguments, instead of doing gen_server call.
To guarantee the delivery you'll add some kind of acknowledgement into your protocol. Auth server should reply to send_auth with something confirming that it got it. And send_auth should retry until it receives that acknowledgement or have some other fallback behavior in case it never does.

Related

Erlang: difference between registering process and assigning Pid to variable

Does it make a difference if I register a newly spawned process using register(atom, spawn..) or if I do Pid = spawn..?
To take an example, I just did this with an old program from the Programming Erlang book:
Let's first make a simple server loop:
-module(geometry_server).
-export([loop/0]).
loop() ->
receive
{Client, {square, S} = Tuple} ->
io:format("Server: Area of square of Side ~p is ~p and Client was ~p~n", [S, S*S, Client]),
Client ! {self(), Tuple, S*S},
loop()
end.
Now a client:
-module(geometry_client).
-export([client/2, start_server/0]).
client(Pid_server, Geom_tuple) ->
Pid_server ! {self(), Geom_tuple},
receive
{Pid_server, Geom_tuple, Area} -> io:format("Client: Area of ~p is ~p and server was ~p~n", [Geom_tuple, Area, Pid_server])
after 1000 ->
io:format("~p~n",["received nothing from server"] )
end.
start_server() -> spawn(geometry_server, loop, []).
After compiling both, I do
register(q, Q = geometry_client:start_server()).
Then I call them and get results as follows:
5> geometry_client:client(Q, {square,2}).
Server: Area of square of Side 2 is 4 and Client was <0.60.0>
Client: Area of {square,2} is 4 and server was <0.77.0>
ok
6> geometry_client:client(q, {square,2}).
Server: Area of square of Side 2 is 4 and Client was <0.60.0>
"received nothing from server"
ok
Why does the client not receive anything from the server when I use the registered atom?? The server obviously received the message from the client.
I can confirm that the server sent a message, because after the above if I do
7> geometry_client:client(whereis(q), {square,2}).
Client: Area of {square,2} is 4 and server was <0.77.0>
Server: Area of square of Side 2 is 4 and Client was <0.60.0>
ok
12>
So I conclude that the mailbox already has the message from the server from the previous command, which is why the Client output gets printed before the Server message has been received and printed...
What am I missing?? Why is there a problem receiving the message when I use the registered atom?
The receive in your client/2 function waits for a message matching {Pid_server, Geom_tuple, Area}. When you pass q as the argument, Pid_server is q, but the message the server sends back to the client is a tuple with the first element always being the actual PID of the server, not its name, which means your receive ends up in the after block.
There are many ways to solve this. You can modify client/2 to use whereis/1 to get the PID of the registered process and use that in receive if Pid_server is an atom.
The best way would be to use references here (see make_ref/0). You create a reference when sending a message to the server, and the server sends it back in the response. This way you're guaranteed that you're receiving the response for the request you just sent, since every reference returned by make_ref/0 is guaranteed to be unique.
In client/2, do:
client(Pid_server, Geom_tuple) ->
Ref = make_ref(),
Pid_server ! {Ref, self(), Geom_tuple},
receive
{Ref, Geom_tuple, Area} -> io:format("Client: Area of ~p is ~p and server was ~p~n", [Geom_tuple, Area, Pid_server])
after 1000 ->
io:format("~p~n",["received nothing from server"] )
end.
And in the server:
loop() ->
receive
{Ref, Client, {square, S} = Tuple} ->
io:format("Server: Area of square of Side ~p is ~p and Client was ~p~n", [S, S*S, Client]),
Client ! {Ref, Tuple, S*S},
loop()
end.

Erlang TCP sockets get closed

To learn Erlang I am trying to implement a tiny web server based on gen_tcp. Unfortunately, my code seems to trigger some wired behaviour. To demonstrate the problem I have attached a minimised version of my implementation which is sufficient to reproduce the problem. It is just delivering a static 200 OK, no matter what the HTTP request was.
The problem arises when I try to run ab (Apache HTTP server benchmarking) against my web server (using loopback interface). Without any concurrent requests (-c) everything is running just fine. However, if I use -c 8 or -c 16, the call to gen_tcp:accept/1 seems to fail on some sockets as I see a number of request: closed lines in the shell.
What makes the whole story even weirder is, that I see different behaviours on different operating systems:
OS X+Erlang/OTP 18: ab reports "Connection reset by peer" almost immediately after starting.
Debian+Erlang R15B01: All but two of the HTTP requests seem to run through. But then, ab hangs for a few seconds and reports "The timeout specified has expired, Total of 4998 requests completed", when i run ab with -n 5000. Similarly, 14998 is reported when I run 15000 tests.
This one does not seem to be the problem. I am honestly quite lost and therefore appreciate any help! :) Thanks!
server(Port) ->
Opt = [list, {active, false}, {reuseaddr, true}],
case gen_tcp:listen(Port, Opt) of
{ok, Listen} ->
handler(Listen),
gen_tcp:close(Listen),
ok;
{error, Error} ->
io:format("init: ~w~n", [Error])
end.
handler(Listen) ->
case gen_tcp:accept(Listen) of
{ok, Client} ->
request(Client),
handler(Listen);
{error, Error} ->
io:format("request: ~w~n", [Error])
end.
request(Client) ->
Recv = gen_tcp:recv(Client, 0),
case Recv of
{ok, _} ->
Response = reply(),
gen_tcp:send(Client, Response);
{error, Error} ->
io:format("request: ~w~n", [Error])
end,
gen_tcp:close(Client).
reply() ->
"HTTP/1.0 200 OK\r\n" ++
"Content-Length: 7\r\n\r\n"
"static\n".
When you increase the number of concurrent requests sent with ab -c N it will immediately open multiple TCP sockets to the server.
By default a socket opened with gen_tcp:listen/2 will support only five outstanding connection requests. Increase the number of connection requests outstanding with the {backlog, N} option to gen_tcp:listen/2.
I tested your code on OS X with ab and saw this resolve the prolem with "Connection reset by peer".

Erlang : Tcp Server

Hi I am a new bee in Erlang but managed to create a simple TCP server which accepts client in passive mode and displays message.
I spawn a new process every time new client connects to the server. Is there a way I could send message to the client using the process which gets spawned when client connects.
Here is code.
-module(test).
-export([startserver/0]).
startserver()->
{ok, ListenSocket}=gen_tcp:listen(1235,[binary,{active, false}]),
connect(ListenSocket).
connect(ListenSocket)->
{ok, UserSocket}=gen_tcp:accept(ListenSocket),
Pid=spawn(? MODULE, user,[UserSocket]),
gen_tcp:controlling_process(UserSocket, Pid),
connect(ListenSocket).
user(UserSocket)->
case gen_tcp:recv(UserSocket, 0) of.
{ok, Binary}->% Send basic message.
{error, closed}->% operation on close.
end.
Can I have some thing like if I do.
Pid!{"Some Message"}. And the message is send to the socket associated with the process with non blocking io,
You could try this tutorial for writing a TCP server using OTP principles: http://learnyousomeerlang.com/buckets-of-sockets#sockserv-revisited
If you use a gen_server instead of your connect loop, you can store the Pids in the state. Then you can use gen_server:cast/2 to send a message to one of the Pids.
The function you want to send a message to the client from the controlling process is gen_tcp:send(Socket, Message), so for example if you wanted to send a one off message on connection you could do this:
user(UserSocket)->
gen_tcp:send(UserSocket, "hello"),
case gen_tcp:recv(UserSocket, 0) of
{ok, Binary}->% Send basic message.
gen_tcp:send(UserSocket, "basic message"),
{error, closed}->% operation on close.
gen_tcp:send(UserSocket, "this socket is closing now"),
end.

problems while sending messages using ssl:send

I am writing some code which sends data over ssl sockets.
The sending part is inside a gen_server:call/3 as:
handle_call({send, Data}, _From, #state{socket=Socket} = State) ->
Reply = case ssl:send(Socket, Data) of
ok ->
ok;
{error, Error} ->
{error, Error}
end,
{reply, Reply, State}.
the problem is that if i kill the application which behaves as server at the other side of the connection, the result of the call is 'ok' but the Data is not sent. Does that mean that the socket is viewed as alive untile {ssl_closed, S} is received by the process?
It was my mistake, data is actually sent but never recovered by peer.

How to enable active sockets in a Mochiweb application?

Does anyone know how to enable active instead of passive sockets in a Mochiweb application. Specifically, I am trying to adapt http://www.metabrew.com/article/a-million-user-comet-application-with-mochiweb-part-2 so that when a client disconnects, it will immediately "logout".
I have tried setting:
start(Options) ->
{DocRoot, Options1} = get_option(docroot, Options),
Loop = fun (Req) ->
Socket = Req:get(socket),
inet:setopts(Socket, [{active, once}]),
?MODULE:loop(Req, DocRoot)
end,
but that seems to not be working. I still only get updates in my receive after I am sent a new message.
Thoughts? Thanks!
I solved this for my Erlang comet app, parts of which I show in this blog post. Basically, you don't want the socket to be in active mode all the time; you just want it in active mode after you've read the client's request and before you return a response.
Here's a sample request handler:
comet(Req) ->
Body = Req:recv_body(),
io:format("~nBody: ~p~n", [Body]),
Socket = Req:get(socket),
inet:setopts(Socket, [{active, once}]),
Response = connection:handle_json(Body),
inet:setopts(Socket, [{active, false}]),
io:format("~nSending Response: ~s~n", [Response]),
Req:ok({"application/json", [], Response}).
The io:format call is just console logging for my benefit. The important part is that I set {active, once} on the socket after reading the body from the request and just before calling the function which holds the request and returns data. I also turn active mode back off; the socket may be reused in certain HTTP modes.

Resources