From 8f69052ae8c27115f0cd1756e6949139759aa751 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Wed, 24 Jul 2013 11:05:34 +0200 Subject: Fix UTF8String encode To accept any nested list of codepoints and binaries. A list containing a binary was previously misinterpreted and the documentation was incomplete. Also, rework codec suite slightly to be able to specify values for which decode o encode is the identity map, for which encode should succeed, and for which encode should fail. --- lib/diameter/doc/src/diameter_dict.xml | 4 +- lib/diameter/src/base/diameter_types.erl | 18 ++-- lib/diameter/test/diameter_codec_test.erl | 147 ++++++++++++++++++------------ 3 files changed, 100 insertions(+), 69 deletions(-) (limited to 'lib') diff --git a/lib/diameter/doc/src/diameter_dict.xml b/lib/diameter/doc/src/diameter_dict.xml index 4fcde495b3..8bf4a14240 100644 --- a/lib/diameter/doc/src/diameter_dict.xml +++ b/lib/diameter/doc/src/diameter_dict.xml @@ -609,7 +609,9 @@ UTF8String() = [integer()]

List elements are the UTF-8 encodings of the individual characters in the string. -Invalid codepoints will result in encode/decode failure.

+Invalid codepoints will result in encode/decode failure. +On encode, a UTF8String() can be specified as a binary, or as a nested +list of binaries and codepoints.

diff --git a/lib/diameter/src/base/diameter_types.erl b/lib/diameter/src/base/diameter_types.erl
index 9ae289034c..ab7a7f3549 100644
--- a/lib/diameter/src/base/diameter_types.erl
+++ b/lib/diameter/src/base/diameter_types.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
 %%
 %% The contents of this file are subject to the Erlang Public License,
 %% Version 1.1, (the "License"); you may not use this file except in
@@ -360,7 +360,7 @@ v6enc([], B) ->
     'UTF8String'(M, []);
 
 'UTF8String'(encode, S) ->
-    uenc(S, []).
+    uenc(if is_binary(S) -> [S]; true -> S end, []).
 
 udec(<<>>, Acc) ->
     lists:reverse(Acc);
@@ -368,20 +368,20 @@ udec(<<>>, Acc) ->
 udec(<>, Acc) ->
     udec(Rest, [C | Acc]).
 
-uenc(E, Acc)
-  when E == [];
-       E == <<>> ->
+uenc([], Acc) ->
     list_to_binary(lists:reverse(Acc));
 
-uenc(<>, Acc) ->
-    uenc(Rest, [<> | Acc]);
-
-uenc([[] | Rest], Acc) ->
+uenc([E | Rest], Acc)
+  when E == <<>>;
+       E == [] ->
     uenc(Rest, Acc);
 
 uenc([[H|T] | Rest], Acc) ->
     uenc([H, T | Rest], Acc);
 
+uenc([<> | Rest], Acc) ->
+    uenc([C, T | Rest], Acc);
+
 uenc([C | Rest], Acc) ->
     uenc(Rest, [<> | Acc]).
 
diff --git a/lib/diameter/test/diameter_codec_test.erl b/lib/diameter/test/diameter_codec_test.erl
index 24d4c7665e..6f1b7d0b7d 100644
--- a/lib/diameter/test/diameter_codec_test.erl
+++ b/lib/diameter/test/diameter_codec_test.erl
@@ -1,3 +1,4 @@
+%% coding: utf-8
 %%
 %% %CopyrightBegin%
 %%
@@ -19,7 +20,9 @@
 
 -module(diameter_codec_test).
 
--compile(export_all).
+-export([base/0,
+         gen/1,
+         lib/0]).
 
 %%
 %% Test encode/decode of dictionary-related modules.
@@ -38,28 +41,27 @@
 %% Interface.
 
 base() ->
-    [] = run([{?MODULE, [base, T]} || T <- [zero, decode]]).
+    [] = run([[fun base/1, T] || T <- [zero, decode]]).
 
 gen(Mod) ->
     Fs = [{Mod, F, []} || F <- [name, id, vendor_id, vendor_name]],
-    [] = run(Fs ++ [{?MODULE, [gen, Mod, T]} || T <- [messages,
-                                                      command_codes,
-                                                      avp_types,
-                                                      grouped,
-                                                      enum,
-                                                      import_avps,
-                                                      import_groups,
-                                                      import_enums]]).
+    [] = run(Fs ++ [[fun gen/2, Mod, T] || T <- [messages,
+                                                 command_codes,
+                                                 avp_types,
+                                                 grouped,
+                                                 enum,
+                                                 import_avps,
+                                                 import_groups,
+                                                 import_enums]]).
 
 lib() ->
-    Vs = {_,_} = values('Address'),
-    [] = run([[fun lib/2, N, Vs] || N <- [1,2]]).
+    Vs = {_,_,_} = values('Address'),
+    [] = run([[fun lib/2, N, Vs] || N <- [{1, true}, {3, false}]]).
 
 %% ===========================================================================
 %% Internal functions.
 
-lib(N, {_,_} = T) ->
-    B = 1 == N rem 2,
+lib({N,B}, {_,_,_} = T) ->
     [] = run([[fun lib/2, A, B] || A <- element(N,T)]);
 
 lib(IP, B) ->
@@ -90,7 +92,7 @@ ip([_,_,_,_,_,_,_,_] = A) ->
 %% ------------------------------------------------------------------------
 
 base(T) ->
-    [] = run([{?MODULE, [base, T, F]} || F <- types()]).
+    [] = run([[fun base/2, T, F] || F <- types()]).
 
 %% Ensure that 'zero' values encode only zeros.
 base(zero = T, F) ->
@@ -100,32 +102,23 @@ base(zero = T, F) ->
 %% Ensure that we can decode what we encode and vice-versa, and that
 %% we can't decode invalid values.
 base(decode, F) ->
-    {Eq, Vs, Ns} = b(values(F)),
-    [] = run([{?MODULE, [base_decode, F, Eq, V]}  || V <- Vs]),
-    [] = run([{?MODULE, [base_invalid, F, Eq, V]} || V <- Ns]).
+    {Ts, Fs, Is} = values(F),
+    [] = run([[fun base_decode/3, F, true, V]  || V <- Ts]),
+    [] = run([[fun base_decode/3, F, false, V] || V <- Fs]),
+    [] = run([[fun base_invalid/2, F, V]       || V <- Is]).
 
 base_decode(F, Eq, Value) ->
     d(fun(X,V) -> diameter_types:F(X,V) end, Eq, Value).
 
-base_invalid(F, Eq, Value) ->
+base_invalid(F, Value) ->
     try
-        base_decode(F, Eq, Value),
+        base_decode(F, false, Value),
         exit(nok)
     catch
         error: _ ->
             ok
     end.
 
-b({_,_,_} = T) ->
-    T;
-b({B,Vs})
-  when is_atom(B) ->
-    {B,Vs,[]};
-b({Vs,Ns}) ->
-    {true, Vs, Ns};
-b(Vs) ->
-    {true, Vs, []}.
-
 types() ->
     [F || {F,2} <- diameter_types:module_info(exports)].
 
@@ -136,7 +129,7 @@ types() ->
 %% ------------------------------------------------------------------------
 
 gen(M, T) ->
-    [] = run(lists:map(fun(X) -> {?MODULE, [gen, M, T, X]} end,
+    [] = run(lists:map(fun(X) -> [fun gen/3, M, T, X] end,
                        fetch(T, dict(M)))).
 
 fetch(T, Spec) ->
@@ -197,18 +190,20 @@ gen(M, enum = T, {Name, ED})
     gen(M, T, {?A(Name), lists:map(fun({E,D}) -> {?A(E), D} end, ED)});
 
 gen(M, enum, {Name, ED}) ->
-    [] = run([{?MODULE, [enum, M, Name, T]} || T <- ED]);
+    [] = run([[fun enum/3, M, Name, T] || T <- ED]);
 
 gen(M, Tag, {_Mod, L}) ->
     T = retag(Tag),
-    [] = run([{?MODULE, [gen, M, T, I]} || I <- L]).
+    [] = run([[fun gen/3, M, T, I] || I <- L]).
 
 %% avp_decode/3
 
 avp_decode(Mod, Type, Name) ->
-    {Eq, Vs, _} = b(values(Type, Name, Mod)),
-    [] = run([{?MODULE, [avp_decode, Mod, Name, Type, Eq, V]}
-              || V <- v(Vs)]).
+    {Ts, Fs, _} = values(Type, Name, Mod),
+    [] = run([[fun avp_decode/5, Mod, Name, Type, true, V]
+              || V <- v(Ts)]),
+    [] = run([[fun avp_decode/5, Mod, Name, Type, false, V]
+              || V <- v(Fs)]).
 
 avp_decode(Mod, Name, Type, Eq, Value) ->
     d(fun(X,V) -> avp(Mod, X, V, Name, Type) end, Eq, Value).
@@ -250,7 +245,7 @@ v(N, Ord, E, Acc) ->
 
 arity(M, Name, Rname) ->
     Rec = M:'#new-'(Rname),
-    [] = run([{?MODULE, [arity, M, Name, F, Rec]}
+    [] = run([[fun arity/4, M, Name, F, Rec]
               || F <- M:'#info-'(Rname, fields)]).
 
 arity(M, Name, AvpName, Rec) ->
@@ -299,68 +294,93 @@ z(B) ->
 %% tested.)
 
 values('OctetString' = T) ->
-    {["", atom_to_list(T)], [-1, 256]};
+    {["", atom_to_list(T)],
+     [],
+     [-1, 256]};
 
 values('Integer32') ->
     Mx = (1 bsl 31) - 1,
     Mn = -1*Mx,
-    {[Mn, 0, random(Mn,Mx), Mx], [Mn - 1, Mx + 1]};
+    {[Mn, 0, random(Mn,Mx), Mx],
+     [],
+     [Mn - 1, Mx + 1]};
 
 values('Integer64') ->
     Mx = (1 bsl 63) - 1,
     Mn = -1*Mx,
-    {[Mn, 0, random(Mn,Mx), Mx], [Mn - 1, Mx + 1]};
+    {[Mn, 0, random(Mn,Mx), Mx],
+     [],
+     [Mn - 1, Mx + 1]};
 
 values('Unsigned32') ->
     M = (1 bsl 32) - 1,
-    {[0, random(M), M], [-1, M + 1]};
+    {[0, random(M), M],
+     [],
+     [-1, M + 1]};
 
 values('Unsigned64') ->
     M = (1 bsl 64) - 1,
-    {[0, random(M), M], [-1, M + 1]};
+    {[0, random(M), M],
+     [],
+     [-1, M + 1]};
 
 values('Float32') ->
     E = (1 bsl  8) - 2,
     F = (1 bsl 23) - 1,
     <> = <<0:1, E:8, F:23>>,
     <> = <<1:1, E:8, F:23>>,
-    {[0.0, infinity, '-infinity', Mx, Mn], [0]};
+    {[0.0, infinity, '-infinity', Mx, Mn],
+     [],
+     [0]};
 
 values('Float64') ->
     E = (1 bsl 11) - 2,
     F = (1 bsl 52) - 1,
     <> = <<0:1, E:11, F:52>>,
     <> = <<1:1, E:11, F:52>>,
-    {[0.0, infinity, '-infinity', Mx, Mn], [0]};
+    {[0.0, infinity, '-infinity', Mx, Mn],
+     [],
+     [0]};
 
 values('Address') ->
     {[{255,0,random(16#FF),1}, {65535,0,0,random(16#FFFF),0,0,0,1}],
+     [],
      [{256,0,0,1}, {65536,0,0,0,0,0,0,1}]};
 
 values('DiameterIdentity') ->
-    {["x", "diameter.com"], [""]};
+    {["x", "diameter.com"],
+     [],
+     [""]};
 
 values('DiameterURI') ->
-    {false, ["aaa" ++ S ++ "://diameter.se" ++ P ++ Tr ++ Pr
-             || S  <- ["", "s"],
-                P  <- ["", ":1234"],
-                Tr <- ["" | [";transport=" ++ X
-                             || X <- ["tcp", "sctp", "udp"]]],
-                Pr <- ["" | [";protocol=" ++ X
-                             || X <- ["diameter","radius","tacacs+"]]]]};
+    {[],
+     ["aaa" ++ S ++ "://diameter.se" ++ P ++ Tr ++ Pr
+      || S  <- ["", "s"],
+         P  <- ["", ":1234"],
+         Tr <- ["" | [";transport=" ++ X
+                      || X <- ["tcp", "sctp", "udp"]]],
+         Pr <- ["" | [";protocol=" ++ X
+                      || X <- ["diameter","radius","tacacs+"]]]],
+     []};
 
 values(T)
   when T == 'IPFilterRule';
        T == 'QoSFilterRule' ->
-    ["deny in 0 from 127.0.0.1 to 10.0.0.1"];
+    {["deny in 0 from 127.0.0.1 to 10.0.0.1"],
+     [],
+     []};
 
 %% RFC 3629 defines the UTF-8 encoding of U+0000 through U+10FFFF with the
 %% exception of U+D800 through U+DFFF.
 values('UTF8String') ->
+    S = "ᚠᚢᚦᚨᚱᚲ",
+    B = unicode:characters_to_binary(S),
     {[[],
+      S,
       lists:seq(0,16#1FF),
       [0,16#D7FF,16#E000,16#10FFFF],
       [random(16#D7FF), random(16#E000,16#10FFFF)]],
+     [B, [B, S, hd(S)], [S, B]],
      [[-1],
       [16#D800],
       [16#DFFF],
@@ -372,6 +392,7 @@ values('Time') ->
       {{2036,2,7},{6,28,15}},
       {{2036,2,7},{6,28,16}},    %% 19000101T000000 + 2 bsl 31
       {{2104,2,26},{9,42,23}}],
+     [],
      [{{1968,1,20},{3,14,7}},
       {{2104,2,26},{9,42,24}}]}. %% 19000101T000000 + 3 bsl 31
 
@@ -382,18 +403,24 @@ values('Time') ->
 
 values('Enumerated', Name, Mod) ->
     {_Name, Vals} = lists:keyfind(?S(Name), 1, types(enum, Mod)),
-    lists:map(fun({_,N}) -> N end, Vals);
+    {lists:map(fun({_,N}) -> N end, Vals),
+     [],
+     []};
 
 values('Grouped', Name, Mod) ->
     Rname = Mod:name2rec(Name),
     Rec = Mod:'#new-'(Rname),
     Avps = Mod:'#info-'(Rname, fields),
-    Enum = diameter_enum:combine(lists:map(fun({_,Vs,_}) -> to_enum(Vs) end,
+    Enum = diameter_enum:combine(lists:map(fun({Vs,_,_}) -> to_enum(Vs) end,
                                            [values(F, Mod) || F <- Avps])),
-    {false, diameter_enum:append(group(Mod, Name, Rec, Avps, Enum))};
+    {[],
+     diameter_enum:append(group(Mod, Name, Rec, Avps, Enum)),
+     []};
 
 values(_, 'Framed-IP-Address', _) ->
-    [{127,0,0,1}];
+    {[{127,0,0,1}],
+     [],
+     []};
 
 values(Type, _, _) ->
     values(Type).
@@ -407,12 +434,14 @@ to_enum(E) ->
 %% values/2
 
 values('AVP', _) ->
-    {true, [#diameter_avp{code = 0, data = <<0>>}], []};
+    {[#diameter_avp{code = 0, data = <<0>>}],
+     [],
+     []};
 
 values(Name, Mod) ->
     Avps = types(avp_types, Mod),
     {_Name, _Code, Type, _Flags} = lists:keyfind(?S(Name), 1, Avps),
-    b(values(?A(Type), Name, Mod)).
+    values(?A(Type), Name, Mod).
 
 %% group/5
 %%
-- 
cgit v1.2.3


From ff4f753dbf02693c288d7e15cb3486359e73831f Mon Sep 17 00:00:00 2001
From: Anders Svensson 
Date: Mon, 29 Jul 2013 15:59:35 +0200
Subject: Use module unicode for UTF8String encode/decode

The original code predates that module but there's no reason not to use
it now.
---
 lib/diameter/src/base/diameter_types.erl | 27 ++-------------------------
 1 file changed, 2 insertions(+), 25 deletions(-)

(limited to 'lib')

diff --git a/lib/diameter/src/base/diameter_types.erl b/lib/diameter/src/base/diameter_types.erl
index ab7a7f3549..7ebf891819 100644
--- a/lib/diameter/src/base/diameter_types.erl
+++ b/lib/diameter/src/base/diameter_types.erl
@@ -354,36 +354,13 @@ v6enc([], B) ->
 %% --------------------
 
 'UTF8String'(decode, Bin) ->
-    udec(Bin, []);
+    tl([0|_] = unicode:characters_to_list([0, Bin])); %% assert list return
 
 'UTF8String'(encode = M, zero) ->
     'UTF8String'(M, []);
 
 'UTF8String'(encode, S) ->
-    uenc(if is_binary(S) -> [S]; true -> S end, []).
-
-udec(<<>>, Acc) ->
-    lists:reverse(Acc);
-
-udec(<>, Acc) ->
-    udec(Rest, [C | Acc]).
-
-uenc([], Acc) ->
-    list_to_binary(lists:reverse(Acc));
-
-uenc([E | Rest], Acc)
-  when E == <<>>;
-       E == [] ->
-    uenc(Rest, Acc);
-
-uenc([[H|T] | Rest], Acc) ->
-    uenc([H, T | Rest], Acc);
-
-uenc([<> | Rest], Acc) ->
-    uenc([C, T | Rest], Acc);
-
-uenc([C | Rest], Acc) ->
-    uenc(Rest, [<> | Acc]).
+    <<_/binary>> = unicode:characters_to_binary(S).   %% assert binary return
 
 %% --------------------
 
-- 
cgit v1.2.3


From 8e7402a8b4230d25d89ddd2492908b4206083392 Mon Sep 17 00:00:00 2001
From: Anders Svensson 
Date: Tue, 30 Jul 2013 16:48:27 +0200
Subject: Simplify Address encode/decode

By using binary comprehensions. Add string-valued addresses to codec
suite.
---
 lib/diameter/src/base/diameter_types.erl  | 39 +++++++------------------------
 lib/diameter/test/diameter_codec_test.erl | 22 ++++++++---------
 2 files changed, 18 insertions(+), 43 deletions(-)

(limited to 'lib')

diff --git a/lib/diameter/src/base/diameter_types.erl b/lib/diameter/src/base/diameter_types.erl
index 7ebf891819..8c07e84777 100644
--- a/lib/diameter/src/base/diameter_types.erl
+++ b/lib/diameter/src/base/diameter_types.erl
@@ -250,13 +250,10 @@
 'Address'(encode, zero) ->
     <<0:48>>;
 
-'Address'(decode, <<1:16, B/binary>>)
-  when size(B) == 4 ->
-    list_to_tuple(binary_to_list(B));
-
-'Address'(decode, <<2:16, B/binary>>)
-  when size(B) == 16 ->
-    list_to_tuple(v6dec(B, []));
+'Address'(decode, <>)
+  when 1 == A,  4 == size(B);
+       2 == A, 16 == size(B) ->
+    list_to_tuple([N || <> <= B]);
 
 'Address'(decode, <> = B)
   when 1 == A;
@@ -264,30 +261,10 @@
     ?INVALID_LENGTH(B);
 
 'Address'(encode, T) ->
-    ipenc(diameter_lib:ipaddr(T)).
-
-ipenc(T)
-  when is_tuple(T), size(T) == 4 ->
-    B = list_to_binary(tuple_to_list(T)),
-    <<1:16, B/binary>>;
-
-ipenc(T)
-  when is_tuple(T), size(T) == 8 ->
-    B = v6enc(lists:reverse(tuple_to_list(T)), <<>>),
-    <<2:16, B/binary>>.
-
-v6dec(<>, Acc) ->
-    v6dec(B, [N | Acc]);
-
-v6dec(<<>>, Acc) ->
-    lists:reverse(Acc).
-
-v6enc([N | Rest], B)
-  when ?UINT(16,N) ->
-    v6enc(Rest, <>);
-
-v6enc([], B) ->
-    B.
+    Ns = tuple_to_list(diameter_lib:ipaddr(T)),  %% length 4 or 8
+    A = length(Ns) div 4,                        %% 1 or 2
+    B = << <> || N <- Ns >>,
+    <>.
 
 %% --------------------
 
diff --git a/lib/diameter/test/diameter_codec_test.erl b/lib/diameter/test/diameter_codec_test.erl
index 6f1b7d0b7d..295d23912b 100644
--- a/lib/diameter/test/diameter_codec_test.erl
+++ b/lib/diameter/test/diameter_codec_test.erl
@@ -62,15 +62,13 @@ lib() ->
 %% Internal functions.
 
 lib({N,B}, {_,_,_} = T) ->
-    [] = run([[fun lib/2, A, B] || A <- element(N,T)]);
+    [] = run([[fun lib/2, A, B] || A <- element(N,T), is_tuple(A)]);
 
 lib(IP, B) ->
-    LA = tuple_to_list(IP),
-    {SA,Fun} = ip(LA),
-    [] = run([[fun lib/4, IP, B, Fun, A] || A <- [IP, SA]]).
+    [] = run([[fun lib/3, IP, B, A] || A <- [IP, ntoa(tuple_to_list(IP))]]).
 
-lib(IP, B, Fun, A) ->
-    try Fun(A) of
+lib(IP, B, A) ->
+    try diameter_lib:ipaddr(A) of
         IP when B ->
             ok
     catch
@@ -78,12 +76,12 @@ lib(IP, B, Fun, A) ->
             ok
     end.
 
-ip([_,_,_,_] = A) ->
+ntoa([_,_,_,_] = A) ->
     [$.|S] = lists:append(["." ++ integer_to_list(N) || N <- A]),
-    {S, fun diameter_lib:ipaddr/1};
-ip([_,_,_,_,_,_,_,_] = A) ->
+    S;
+ntoa([_,_,_,_,_,_,_,_] = A) ->
     [$:|S] = lists:flatten([":" ++ io_lib:format("~.16B", [N]) || N <- A]),
-    {S, fun diameter_lib:ipaddr/1}.
+    S.
 
 %% ------------------------------------------------------------------------
 %% base/1
@@ -344,8 +342,8 @@ values('Float64') ->
 
 values('Address') ->
     {[{255,0,random(16#FF),1}, {65535,0,0,random(16#FFFF),0,0,0,1}],
-     [],
-     [{256,0,0,1}, {65536,0,0,0,0,0,0,1}]};
+     ["127.0.0.1", "FFFF:FF::1.2.3.4"],
+     [{256,0,0,1}, {65536,0,0,0,0,0,0,1}, "256.0.0.1", "10000::1"]};
 
 values('DiameterIdentity') ->
     {["x", "diameter.com"],
-- 
cgit v1.2.3