How to get Ephemeral port for UDP in Erlang? - erlang

I'm trying to find a way to open an ephemeral port (https://en.wikipedia.org/wiki/Ephemeral_port basic one without having to provide a specific port number, and from the ephemeral range of ports). This is for client requests via UDP protocol. I know how to open a specific port for UDP communications via:
{ok,Socket} = gen_udp:open(8000).
But prefer not to have to know an available port number ahead of time and just have the system provide one if this is even possible.
Any suggestion or thought of how best to do this in Erlang?

Do it the same way you would in C: specify port 0.
1> {ok, S1} = gen_udp:open(0).
{ok,#Port<0.541>}
2> {ok, S2} = gen_udp:open(0).
{ok,#Port<0.547>}
3> {ok, S3} = gen_udp:open(0).
{ok,#Port<0.548>}
4> inet:port(S1).
{ok,55398}
5> inet:port(S2).
{ok,44963}
6> inet:port(S3).
{ok,58993}

Related

How can I send messages to an existing Unix socket from Erlang?

I see that gen_udp has support for Unix sockets, and this example shows creating an using one in Erlang.
I want to send messages to an existing Unix socket (to control mpv via its JSON IPC interface). I see there was a self-answered question on the Erlang mailing list about this, but the answer doesn't make sense to me, as Sock2 is used without previous assignment.
I see in the gen_udp docs this option:
{fd, integer() >= 0}
If a socket has somehow been opened without using gen_udp,
use this option to pass the file descriptor for it.
But when I try to open the socket as a file with file:open/2, I get {error,eopnotsupp}.
How can I send messages to an existing Unix socket?
Answer for my case
This will not be a canonical and thorough answer, because I'm not super familiar with sockets. However, I emailed Joe from the mailing list link above, and he said:
As far as I understand, the unix socket type to erlang module mapping
is as follows:
SOCK_STREAM -> gen_tcp
SOCK_DGRAM -> gen_udp
SOCK_SEQPACKET -> gen_sctp
He suggested using gen_tcp:connect in my case, and it worked! Apparently, mpv created a SOCK_STREAM socket.
So, having started mpv like:
mpv /Users/me/playlist.m3u --input-ipc-server=/tmp/mpv.sock --idle yes --no-audio-display
... so that it expects commands on the socket /tmp/mpv.sock, I could send it a "play a different playlist" command like this in erl:
{ok, Port} = gen_tcp:connect({local, "/tmp/mpv.sock"}, 0, [local]).
Msg = "{ \"command\": [\"loadlist\", \"/Users/me/playlist2.m3u\", \"replace\"] }\n".
gen_tcp:send(Port, Msg).

How To Use Erlang ssl:close/2

I have an SSL server, and I want to downgrade this after receiving the first ssl:recv to a raw gen_tcp. Assuming this can be used to do that I can not find an example on how to use this. And I am not so good at using the Erlang/OTP documentation yet http://erlang.org/doc/man/ssl.html#close-2
I am a bit confused with NewController::pid() from the documentation:
How = timeout() | {NewController::pid(), timeout()}
NewController::pid() here refers to the process you want to set as the "controlling process" for the downgraded TCP socket. gen_tcp functions on the socket will only work if called from that process. You'll want to send self() here unless you want to use the downgraded TCP socket from another process.
The only example I could find of ssl:close/2 being used with a tuple as the second argument is this test. Here's a simplified version of that code to get you started:
% Assuming `SSLSocket` is the SSL socket.
{ok, TCPSocket} = ssl:close(SSLSocket, {self(), 10000}),
% You can use `TCPSocket` with `gen_tcp` now.
gen_tcp:send(TCPSocket, "foo"),

can't add multicast group

Trying to add multicast group for ipv6, but it returns error. don't understand the problem. with ipv4 it works fine
(test_client#127.0.0.1)1> {ok, S} = gen_udp:open(3333, [binary, {active, false}, {ip, {65342,0,0,0,0,0,34048,9029}}, inet6, {multicast_loop, false}]).
{ok,#Port<0.1587>}
(test_client#127.0.0.1)4> inet:setopts(S, [{add_membership, {{65342,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}}}]).
{error,einval}
unfortunately this topic in erlang docs is badly documented
also have tried with addrreses like ff3c: , ff32:
UPDATE
i've looked into Erlang/OTP 18.2 source code, there is using function prim_inet:is_sockopt_val(add_membership, {{65280,0,0,0,0,0,34048,9029}, {0,0,0,0,0,0,0,0}})
and it always return false, because in prim_inet:type_value_2/2 we have type ip, value {_,_,_,_,_,_,_,_} and it waits only for ipv4 {_,_,_,_}.
on the one hand i know why can't add membership with ipv6 when open socket, but on other hand what to do is opened question
It doesn't look like Erlang's driver has implemented IPV6_ADD_MEMBERSHIP, but it does have raw support so you could construct it yourself. A problem with this approach is that you are hard coding things usually defined in header files, so your solution wont be very portable.
-module(unssmraw).
-export([test/0]).
test() ->
Port = 57100,
Mad = <<65340:16,0:16,0:16,0:16,0:16,0:16,34048:16,9029:16>>,
Ifindx = <<3:64/native>>,
Ip6 = 41,
Ip6am = 20,
{ok, Sock} = gen_udp:open(Port, [{reuseaddr,true}, inet6, binary]),
R3 = inet:setopts(Sock, [{raw, Ip6, Ip6am, <<Mad/binary, Ifindx/binary>> }]),
io:format("ssm ok? ~w ~n", [R3]),
receive
{udp, S, A, Pr, Pk} -> io:format("watcher sees: Socket ~p Address ~p Port ~p Packet ~p ~n", [S, A, Pr, Pk]) end.
Example test sender:
echo hi | socat - UDP6-SENDTO:\"ff3c::8500:2345\":57100
Example run:
$ erl
Erlang/OTP 19 [erts-8.0.1] [source-761e467] [64-bit] [smp:2:2] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.0.1 (abort with ^G)
1> unssmraw:test().
ssm ok? ok
watcher sees: Socket #Port<0.453> Address {65152,0,0,0,47734,16383,65066,
19977} Port 43511 Packet <<"hi\n">>
ok
Notes on my hardcoded values
How to find the interface index used in Ifindx:
As of OTP 22 net:if_name2index is available to call. A language neutral description is here. I used a 64-bit since that is the size of an int on my system and it is an int in mreq in my in6.h.)
Ip6's value is from in.h
Ip6am is IPV6_ADD_MEMBERSHIP from in6.h.

Multiple gen_esme sessions

Trust you all are doing well.
I am trying to make multiple sessions to SMSC using OSERL application.
Since to make a SMPP client you need to inherit gen_esme behaviour.
I was wondering whether it is possible to make multiple connections towards SMSC without writing multiple gen_esme modules?
There are two main strategies for starting multiple processes using the same gen_esme based module:
gen_esme:start_link/4 - named or reference based server
gen_esme:start_link/3 - pid based server
I'm going to be referencing the sample_esme file found under the examples for oserl.
Named Server
Most of the examples from oserl show usage of gen_esme:start_link/4 which in turn is calling gen_server:start_link/4. The ServerName variable for gen_server:start_link/4 has a typespec of {local, Name::atom()} | {global, GlobalName::term()} | {via, Module::atom(), ViaName::term()}.
That means if we change the sample_esme:start_link/0,1,2 functions to look like this:
start_link() ->
start_link(?MODULE).
start_link(SrvName) ->
start_link(SrvName, true).
start_link(SrvName, Silent) ->
Opts = [{rps, 1}, {queue_file, "./sample_esme.dqueue"}],
gen_esme:start_link({local, SrvName}, ?MODULE, [Silent], Opts).
We can start multiple servers using:
sample_esme:start_link(). %% SrvName = 'sample_esme'
sample_esme:start_link(my_client1). %% SrvName = 'my_client1'
sample_esme:start_link(my_client2). %% SrvName = 'my_client2'
To make our sample_esme module work properly with this named server strategy, most of its calling functions will need to be modified. Let's use sample_esme:rps/0,1 as an example:
rps() ->
rps(?MODULE).
rps(SrvName) ->
gen_esme:rps(SrvName).
Now we can call the gen_esme:rps/1 function on any of our running servers:
sample_esme:rps(). %% calls 'sample_esme'
sample_esme:rps(my_client1). %% 'my_client1'
sample_esme:rps(my_client2). %% 'my_client2'
This is similar to how projects like pooler reference members of pools it creates.
pid Server
This is essentially the same as the Named Server strategy, but we're just going to pass the pid of the server around instead of a registered atom.
That means if we change the sample_esme:start_link/0,1 functions to look like this:
start_link() ->
start_link(true).
start_link(Silent) ->
Opts = [{rps, 1}, {queue_file, "./sample_esme.dqueue"}],
gen_esme:start_link(?MODULE, [Silent], Opts).
Notice that all we did was drop the {local, SrvName} argument so it won't register the SrvName atom with the server's pid.
That means we need to capture the pid of each created server:
{ok, Pid0} = sample_esme:start_link().
{ok, Pid1} = sample_esme:start_link().
{ok, Pid2} = sample_esme:start_link().
Using the same sample_esme:rps/0,1 example from Named Server, we will need to remove sample_esme:rps/0 and add a sample_esme:rps/1 function which takes a pid:
rps(SrvPid) ->
gen_esme:rps(SrvPid).
Now we can call the gen_esme:rps/1 function on any of our running servers:
sample_esme:rps(Pid0).
sample_esme:rps(Pid1).
sample_esme:rps(Pid2).
This is similar to how projects like poolboy reference members of pools it creates.
Recommendations
If you are simply trying to pool connections, I would recommend using a library like pooler or poolboy.
If you have a finite number of specifically named connections that you want to reference by name, I would recommend just having a supervisor with a child spec like the following for each connection:
{my_client1,
{sample_esme, start_link, [my_client1]},
permanent, 5000, worker, [sample_esme]}

How to write a simple webserver in Erlang?

Using the default Erlang installation what is the minimum code needed to produce a "Hello world" producing web server?
Taking "produce" literally, here is a pretty small one. It doesn't even read the request (but does fork on every request, so it's not as minimal possible).
-module(hello).
-export([start/1]).
start(Port) ->
spawn(fun () -> {ok, Sock} = gen_tcp:listen(Port, [{active, false}]),
loop(Sock) end).
loop(Sock) ->
{ok, Conn} = gen_tcp:accept(Sock),
Handler = spawn(fun () -> handle(Conn) end),
gen_tcp:controlling_process(Conn, Handler),
loop(Sock).
handle(Conn) ->
gen_tcp:send(Conn, response("Hello World")),
gen_tcp:close(Conn).
response(Str) ->
B = iolist_to_binary(Str),
iolist_to_binary(
io_lib:fwrite(
"HTTP/1.0 200 OK\nContent-Type: text/html\nContent-Length: ~p\n\n~s",
[size(B), B])).
For a web server using only the built in libraries check out inets http_server.
When in need of some more power but still with simplicity you should check out the mochiweb library. You can google for loads of example code.
Do you actually want to write a web server in Erlang, or do you want an Erlang web server so that you can create dynamic web content using Erlang?
If the latter, try YAWS. If the former, have a look at the YAWS source code for inspiration
Another way, similar to the gen_tcp example above but with less code and already offered as a suggestion, is using the inets library.
%%%
%%% A simple "Hello, world" server in the Erlang.
%%%
-module(hello_erlang).
-export([
main/1,
run_server/0,
start/0
]).
main(_) ->
start(),
receive
stop -> ok
end.
run_server() ->
ok = inets:start(),
{ok, _} = inets:start(httpd, [
{port, 0},
{server_name, "hello_erlang"},
{server_root, "/tmp"},
{document_root, "/tmp"},
{bind_address, "localhost"}
]).
start() -> run_server().
Keep in mind, this exposes your /tmp directory.
To run, simply:
$ escript ./hello_erlang.erl
For a very easy to use webserver for building restful apps or such check out the gen_webserver behaviour: http://github.com/martinjlogan/gen_web_server.
Just one fix for Felix's answer and it addresses the issues Martin is seeing. Before closing a socket, all data being sent from the client should be received (using for example do_recv from gen_tcp description).
Otherwise there's a race condition for the browser/proxy sending the HTTP request being quick enough to send the http request before the socket is closed.

Resources