%%%------------------------------------------------------------------- %% @doc Minimal UDP worker. %%%------------------------------------------------------------------- -module(vpn_udp). -behaviour(gen_server). -export([start_link/1, start_link/2, stop/1, send/4]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]). start_link(Port) -> start_link(Port, undefined). start_link(Port, OwnerPid) -> gen_server:start_link(?MODULE, {Port, OwnerPid}, []). stop(Pid) -> gen_server:stop(Pid). send(Pid, Host, Port, Packet) when is_binary(Packet) -> gen_server:call(Pid, {send, Host, Port, Packet}). init({Port, OwnerPid}) -> case gen_udp:open(Port, [binary, {active, true}]) of {ok, Socket} -> {ok, #{socket => Socket, port => Port, owner => OwnerPid}}; {error, Reason} -> {stop, Reason} end. handle_call({send, Host, Port, Packet}, _From, State = #{socket := Socket}) -> Reply = gen_udp:send(Socket, Host, Port, Packet), {reply, Reply, State}; handle_call(_Request, _From, State) -> {reply, {error, not_implemented}, State}. handle_cast(_Request, State) -> {noreply, State}. handle_info({udp, Socket, Ip, Port, Packet}, State = #{socket := Socket}) -> logger:info("vpn_udp received packet from ~p:~p: ~p bytes", [Ip, Port, byte_size(Packet)]), maybe_send_packet(Ip, Port, Packet, State), {noreply, State}; handle_info({udp, _OtherSocket, _Ip, _Port, _Packet}, State) -> {noreply, State}; handle_info(_Message, State) -> {noreply, State}. terminate(_Reason, #{socket := Socket}) -> gen_udp:close(Socket), ok; terminate(_Reason, _State) -> ok. maybe_send_packet(_Ip, _Port, _Packet, #{owner := undefined}) -> ok; maybe_send_packet(Ip, Port, Packet, #{owner := OwnerPid}) -> OwnerPid ! {vpn_udp_packet, self(), Ip, Port, Packet}, ok.