From cd7742c42a4585bd306b20694607616b0302eb3e Mon Sep 17 00:00:00 2001 From: Tomasz Kazimierz Motyl Date: Mon, 4 Oct 2021 00:23:16 -0700 Subject: [PATCH] Fixing IPv6 address parse IPv6 host name resolution and inet family :inet or :inet6 based on peer's socket address to the client's socket --- src/eradius_client.erl | 36 +++++++++++++++++++++-------- src/eradius_client_socket.erl | 21 +++++++++-------- test/eradius_client_SUITE.erl | 4 ++-- test/eradius_client_socket_test.erl | 8 +++---- 4 files changed, 44 insertions(+), 25 deletions(-) diff --git a/src/eradius_client.erl b/src/eradius_client.erl index 0a2d3792..b0fcb143 100644 --- a/src/eradius_client.erl +++ b/src/eradius_client.erl @@ -36,8 +36,8 @@ Req#radius_request.cmd == 'coareq' orelse Req#radius_request.cmd == 'discreq')). --type nas_address() :: {string() | binary() | inet:ip_address(), - eradius_server:port_number(), +-type nas_address() :: {string() | binary() | inet:ip_address(), + eradius_server:port_number(), eradius_lib:secret()}. -type options() :: [{retries, pos_integer()} | {timeout, timeout()} | @@ -63,10 +63,10 @@ send_request(NAS, Request) -> % If no answer is received within the specified timeout, the request will be sent again. -spec send_request(nas_address(), #radius_request{}, options()) -> {ok, binary(), eradius_lib:authenticator()} | {error, 'timeout' | 'socket_down'}. -send_request({Host, Port, Secret}, Request, Options) +send_request({Host, Port, Secret}, Request, Options) when ?GOOD_CMD(Request) andalso is_binary(Host) -> send_request({erlang:binary_to_list(Host), Port, Secret}, Request, Options); -send_request({Host, Port, Secret}, Request, Options) +send_request({Host, Port, Secret}, Request, Options) when ?GOOD_CMD(Request) andalso is_list(Host) -> IP = get_ip(Host), send_request({IP, Port, Secret}, Request, Options); @@ -335,10 +335,19 @@ init([]) -> Else -> Else end. +%% @private +inet_family_based_on_peer(_PeerSocket = {{_, _, _, _}, _port}) -> + [inet]; +inet_family_based_on_peer(_PeerSocket = {{_, _, _, _, _, _, _, _}, _port}) -> + [inet6]; +inet_family_based_on_peer(_PeerSocket) -> + []. + %% @private handle_call({wanna_send, Peer = {_PeerName, PeerSocket}, _MetricsInfo}, _From, State) -> {PortIdx, ReqId, NewIdCounters} = next_port_and_req_id(PeerSocket, State#state.no_ports, State#state.idcounters), - {SocketProcess, NewSockets} = find_socket_process(PortIdx, State#state.sockets, State#state.socket_ip, State#state.sup), + InetFamily = inet_family_based_on_peer(PeerSocket), + {SocketProcess, NewSockets} = find_socket_process(PortIdx, State#state.sockets, State#state.socket_ip, InetFamily, State#state.sup), IsCreated = lists:member(Peer, State#state.clients), NewState = case IsCreated of false -> @@ -512,11 +521,11 @@ next_port_and_req_id(Peer, NumberOfPorts, Counters) -> NewCounters = Counters#{Peer => {NextPortIdx, NextReqId}}, {NextPortIdx, NextReqId, NewCounters}. -find_socket_process(PortIdx, Sockets, SocketIP, Sup) -> +find_socket_process(PortIdx, Sockets, SocketIP, Options, Sup) -> case array:get(PortIdx, Sockets) of undefined -> Res = supervisor:start_child(Sup, {PortIdx, - {eradius_client_socket, start, [SocketIP, self(), PortIdx]}, + {eradius_client_socket, start, [SocketIP, self(), PortIdx, Options]}, transient, brutal_kill, worker, [eradius_client_socket]}), Pid = case Res of {ok, P} -> P; @@ -538,7 +547,7 @@ parse_ip(Address) when is_list(Address) -> inet_parse:address(Address); parse_ip(T = {_, _, _, _}) -> {ok, T}; -parse_ip(T = {_, _, _, _, _, _}) -> +parse_ip(T = {_, _, _, _, _, _, _, _}) -> {ok, T}. init_server_status_metrics() -> @@ -689,5 +698,14 @@ get_ip(Host) -> {ok, #hostent{h_addrtype = inet, h_addr_list = [_ | _] = IPs}} -> Index = rand:uniform(length(IPs)), lists:nth(Index, IPs); - _ -> error(badarg) + _ -> + case inet:gethostbyname(Host, inet6) of + {ok, #hostent{h_addrtype = inet6, h_addr_list = [IP]}} -> + IP; + {ok, #hostent{h_addrtype = inet6, h_addr_list = [_ | _] = IPs}} -> + Index = rand:uniform(length(IPs)), + lists:nth(Index, IPs); + _Err -> + error(badarg) + end end. diff --git a/src/eradius_client_socket.erl b/src/eradius_client_socket.erl index d03ff2b3..e5de0ac5 100644 --- a/src/eradius_client_socket.erl +++ b/src/eradius_client_socket.erl @@ -2,22 +2,23 @@ -behaviour(gen_server). --export([start/3]). +-export([start/4]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -record(state, {client, socket, pending, mode, counter}). -start(SocketIP, Client, PortIdx) -> - gen_server:start_link(?MODULE, [SocketIP, Client, PortIdx], []). +start(SocketIP, Client, PortIdx, Options) -> + gen_server:start_link(?MODULE, [SocketIP, Client, PortIdx, Options], []). -init([SocketIP, Client, PortIdx]) -> +init([SocketIP, Client, PortIdx, Options]) -> Client ! {PortIdx, self()}, - case SocketIP of - undefined -> - ExtraOptions = []; - SocketIP when is_tuple(SocketIP) -> - ExtraOptions = [{ip, SocketIP}] - end, + ExtraOptions = + case SocketIP of + undefined -> + Options; + SocketIP when is_tuple(SocketIP) -> + [{ip, SocketIP} | Options] + end, RecBuf = application:get_env(eradius, recbuf, 8192), SndBuf = application:get_env(eradius, sndbuf, 131072), {ok, Socket} = gen_udp:open(0, [{active, once}, binary , {recbuf, RecBuf}, {sndbuf, SndBuf} | ExtraOptions]), diff --git a/test/eradius_client_SUITE.erl b/test/eradius_client_SUITE.erl index ded4ea01..98d57407 100644 --- a/test/eradius_client_SUITE.erl +++ b/test/eradius_client_SUITE.erl @@ -148,7 +148,7 @@ split2(N, List1, [L|List2]) -> split2(N-1, [L|List1], List2). meckStart() -> ok = meck:new(eradius_client_socket), - ok = meck:expect(eradius_client_socket, start, fun(X, Y, Z) -> eradius_client_socket_test:start(X, Y, Z) end), + ok = meck:expect(eradius_client_socket, start, fun(X, Y, Z, O) -> eradius_client_socket_test:start(X, Y, Z, O) end), ok = meck:expect(eradius_client_socket, init, fun(X) -> eradius_client_socket_test:init(X) end), ok = meck:expect(eradius_client_socket, handle_call, fun(X, Y, Z) -> eradius_client_socket_test:handle_call(X, Y, Z) end), ok = meck:expect(eradius_client_socket, handle_cast, fun(X, Y) -> eradius_client_socket_test:handle_cast(X, Y) end), @@ -165,7 +165,7 @@ parse_ip(Address) when is_list(Address) -> inet_parse:address(Address); parse_ip(T = {_, _, _, _}) -> {ok, T}; -parse_ip(T = {_, _, _, _, _, _}) -> +parse_ip(T = {_, _, _, _, _, _, _, _}) -> {ok, T}. %% CHECK diff --git a/test/eradius_client_socket_test.erl b/test/eradius_client_socket_test.erl index ee465c59..538c5d3c 100644 --- a/test/eradius_client_socket_test.erl +++ b/test/eradius_client_socket_test.erl @@ -22,15 +22,15 @@ -behaviour(gen_server). --export([start/3]). +-export([start/4]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -record(state, {client, socket, pending, mode, counter}). -start(SocketIP, Client, PortIdx) -> - gen_server:start_link(?MODULE, [SocketIP, Client, PortIdx], []). +start(SocketIP, Client, PortIdx, Options) -> + gen_server:start_link(?MODULE, [SocketIP, Client, PortIdx, Options], []). -init([_SocketIP, Client, PortIdx]) -> +init([_SocketIP, Client, PortIdx, Options]) -> Client ! {PortIdx, self()}, eradius_client_SUITE:addSocket(), {ok, #state{pending = maps:new(), mode = active, counter = 0}}.