osmo_map/src/map_codec.erl

167 lines
6.1 KiB
Erlang

% GSM MAP codec wrapper functions
% (C) 2011 by Harald Welte <laforge@gnumonks.org>
%
% All Rights Reserved
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU Affero General Public License as
% published by the Free Software Foundation; either version 3 of the
% License, or (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU Affero General Public License
% along with this program. If not, see <http://www.gnu.org/licenses/>.
%
% Additional Permission under GNU AGPL version 3 section 7:
%
% If you modify this Program, or any covered work, by linking or
% combining it with runtime libraries of Erlang/OTP as released by
% Ericsson on http://www.erlang.org (or a modified version of these
% libraries), containing parts covered by the terms of the Erlang Public
% License (http://www.erlang.org/EPLICENSE), the licensors of this
% Program grant you additional permission to convey the resulting work
% without the need to license the runtime libraries of Erlang/OTP under
% the GNU Affero General Public License. Corresponding Source for a
% non-source form of such a combination shall include the source code
% for the parts of the runtime libraries of Erlang/OTP used as well as
% that of the covered work.
-module(map_codec).
-author('Harald Welte <laforge@gnumonks.org>').
-include("map.hrl").
-include_lib("osmo_ss7/include/isup.hrl").
-export([parse_tcap_msg/1, encode_tcap_msg/1]).
-export([parse_addr_string/1, encode_addr_string/1]).
-export([parse_map_addr/1, encode_map_tbcd/1]).
-define(MAP_ADDR_NAT_UNKNOWN, 0).
-define(MAP_ADDR_NAT_INTERNATIONAL, 1).
-define(MAP_ADDR_NAT_NATIONAL, 2).
-define(MAP_ADDR_NAT_NETWORK_SPEC, 3).
-define(MAP_ADDR_NAT_SUBSCRIBER, 4).
-define(MAP_ADDR_NAT_RES, 5).
-define(MAP_ADDR_NAT_ABBREVIATED, 6).
-define(MAP_ADDR_NAT_RES_FOR_EXT, 7).
% convert from MAP -> ISUP 'nature of address'
nature_map2isup(NatureMap) ->
case NatureMap of
?MAP_ADDR_NAT_INTERNATIONAL -> ?ISUP_ADDR_NAT_INTERNATIONAL;
?MAP_ADDR_NAT_NATIONAL -> ?ISUP_ADDR_NAT_NATIONAL;
?MAP_ADDR_NAT_SUBSCRIBER -> ?ISUP_ADDR_NAT_SUBSCRIBER;
_ -> NatureMap
end.
% convert from ISUP -> MAP 'nature of address'
nature_isup2map(NatureIsup) ->
case NatureIsup of
?ISUP_ADDR_NAT_INTERNATIONAL -> ?MAP_ADDR_NAT_INTERNATIONAL;
?ISUP_ADDR_NAT_NATIONAL -> ?MAP_ADDR_NAT_NATIONAL;
?ISUP_ADDR_NAT_SUBSCRIBER -> ?MAP_ADDR_NAT_SUBSCRIBER;
_ -> NatureIsup
end.
% Parse a TBCD-STRING
parse_map_tbcd(<<>>, DigitList) ->
DigitList;
parse_map_tbcd(BcdBin, DigitList) ->
<<Second:4, First:4, Remain/binary>> = BcdBin,
NewDigits = [First, Second],
parse_map_tbcd(Remain, DigitList ++ NewDigits).
parse_map_tbcd(ListBcd) when is_list(ListBcd) ->
BinBcd = list_to_binary(ListBcd),
parse_map_tbcd(BinBcd);
parse_map_tbcd(BinBcd) when is_binary(BinBcd) ->
parse_map_tbcd(BinBcd, []).
% like parse_map_tbcd, but remove any trailing 0xF
parse_map_addr(Bcd) ->
DigitList = parse_map_tbcd(Bcd),
LastDigit = lists:last(DigitList),
if
LastDigit == 15 ->
lists:sublist(DigitList, length(DigitList)-1);
true ->
DigitList
end.
encode_map_tbcd(BcdInt) when is_integer(BcdInt) ->
BcdList = osmo_util:int2digit_list(BcdInt),
encode_map_tbcd(BcdList);
encode_map_tbcd(BcdList) when is_list(BcdList) ->
encode_map_tbcd(BcdList, <<>>).
encode_map_tbcd([], Bin) ->
Bin;
encode_map_tbcd([First,Second|BcdList], Bin) ->
encode_map_tbcd(BcdList, <<Bin/binary, Second:4, First:4>>);
encode_map_tbcd([Last], Bin) ->
encode_map_tbcd([], <<Bin/binary, 15:4, Last:4>>).
encode_map_addr(Bcd) ->
encode_map_tbcd(Bcd).
parse_addr_string(AddrList) when is_list(AddrList) ->
parse_addr_string(list_to_binary(AddrList));
parse_addr_string(AddrBin) when is_binary(AddrBin) ->
<<1:1, NatureMap:3, Numplan:4, Remain/binary>> = AddrBin,
PhoneNum = parse_map_addr(Remain),
NatureIsup = nature_map2isup(NatureMap),
#party_number{nature_of_addr_ind = NatureIsup,
numbering_plan = Numplan,
phone_number = PhoneNum}.
encode_addr_string(#party_number{nature_of_addr_ind = NatureIsup,
numbering_plan = Numplan,
phone_number = PhoneNum}) ->
NatureMap = nature_isup2map(NatureIsup),
PhoneBin = encode_map_addr(PhoneNum),
Bin = <<1:1, NatureMap:3, Numplan:4, PhoneBin/binary>>,
binary_to_list(Bin).
parse_tcap_msg(MsgBin) when is_binary(MsgBin) ->
Msg = binary_to_list(MsgBin),
parse_tcap_msg(Msg);
parse_tcap_msg(Msg) when is_list(Msg) ->
case asn1rt:decode('map', 'MapSpecificPDUs', Msg) of
{ok, {Type, TcapMsgDec}} ->
fixup_dialogue({Type, TcapMsgDec});
Error ->
Error
end.
% Extract the dialoguePortion and feed it through external_1990ify/1
fixup_dialogue({'begin', Beg = #'MapSpecificPDUs_begin'{dialoguePortion=Dia}}) ->
{'begin', Beg#'MapSpecificPDUs_begin'{dialoguePortion = external_1990ify(Dia)}};
fixup_dialogue({'end', Beg = #'MapSpecificPDUs_end'{dialoguePortion=Dia}}) ->
{'end', Beg#'MapSpecificPDUs_end'{dialoguePortion = external_1990ify(Dia)}};
fixup_dialogue({'continue', Beg = #'MapSpecificPDUs_continue'{dialoguePortion=Dia}}) ->
{'continue', Beg#'MapSpecificPDUs_continue'{dialoguePortion = external_1990ify(Dia)}};
fixup_dialogue({'unidirectional', Beg = #'MapSpecificPDUs_unidirectional'{dialoguePortion=Dia}}) ->
{'unidirectional', Beg#'MapSpecificPDUs_unidirectional'{dialoguePortion = external_1990ify(Dia)}};
fixup_dialogue(Default) ->
Default.
% Take the EXTERNAL date type and convert from 1994-style to 1990 with 'single-ASN1-type'
external_1990ify({'EXTERNAL', {syntax, DirRef}, IndirRef, Data}) when is_list(Data); is_binary(Data) ->
#'EXTERNAL'{'direct-reference' = DirRef,
'indirect-reference' = IndirRef,
encoding = {'single-ASN1-type', Data}};
external_1990ify(Default) ->
Default.
encode_tcap_msg({Type, TcapMsgDec}) ->
case asn1rt:encode('map', 'MapSpecificPDUs', {Type, TcapMsgDec}) of
{ok, List} ->
list_to_binary(List);
Error ->
Error
end.