1
%% ====================================================================
2
%% Program which resulted in an erl_types crash due to incomplete
3
%% handling of tuple_sets in function inf_tuples_in_sets/4.
4
%% Reported by Alexey Romanov on 10/10/2010 and fixed 16/10/2010.
5
%% Stavros Aronis provided a better fix of the issue on 8/11/2010.
6
%% ====================================================================
8
-module(tuple_set_crash).
11
%% ====================================================================
13
-define(PREPEND_IF_BIT_SET(BitMap, Bit,
14
PatternInBinary, PatternInList,
15
OldRestVar, NewRestVar,
16
OldAccVar, NewAccVar),
17
case byteset:contains(Bit, BitMap) of
19
<<PatternInBinary, NewRestVar/binary>> = OldRestVar,
20
NewAccVar = [PatternInList | OldAccVar];
22
NewRestVar = OldRestVar,
26
%% ====================================================================
28
%% Types used in parsing binaries
29
-define(BITMAP1, 8/integer-big-unsigned).
30
-define(BYTE, 8/integer-little-unsigned).
31
-define(WORD, 16/integer-little-unsigned).
32
-define(DWORD, 32/integer-little-unsigned).
33
-define(DATE, 16/integer-little-signed).
34
-define(TIME, 32/float-little-unsigned).
35
-define(TINY_STRING_M(Var, Size), Size:?BYTE, Var:Size/binary).
36
-define(SMALL_STRING_M(Var, Size), Size:?WORD, Var:Size/binary).
38
-type config_change() ::
45
audio_output, [{atom(), any()}]}.
47
-type message_from_server() ::
51
{media_item_url_reply, integer(), binary()}.
53
%% ====================================================================
55
-spec test(integer(), [integer()], binary(), binary(), binary()) -> {binary(), binary()}.
56
test(_TargetId, [], _Key, IVT, IVF) ->
58
test(TargetId, [Date | DateTail], Key, IVT, IVF) ->
59
PlayListRequest = play_list_request(TargetId, Date),
60
{ok, Reply, IVT1, IVF1} = culprit(PlayListRequest, Key, IVT, IVF),
62
{play_list, _Playlist} ->
63
test(TargetId, DateTail, Key, IVT1, IVF1);
65
{IVT1, IVF1} %% we can finish early
68
-spec culprit(binary(), binary(), binary(), binary()) ->
69
{ok, message_from_server(), binary(), binary()}.
70
culprit(Message, Key, IVecToServer, IVecFromServer) ->
71
{Packet, NewIVecToServer} = message_to_packet(Message, Key, IVecToServer),
72
Message = crypto:aes_cbc_128_decrypt(Key, IVecFromServer, Packet),
73
NewIVecFromServer = crypto:aes_cbc_ivec(Packet),
74
ParsedMessage = parse_message(Message),
75
{ok, ParsedMessage, NewIVecToServer, NewIVecFromServer}.
77
%% ====================================================================
79
-spec play_list_request(integer(), integer()) -> binary().
80
play_list_request(TargetId, Date) ->
81
<<16#06:?WORD, TargetId:?DWORD, Date:?DATE>>.
83
-spec parse_message(binary()) -> message_from_server().
84
parse_message(<<MessageID:?WORD, Rest/binary>>) ->
86
16#00 -> parse_error_code(Rest);
87
16#22 -> {device_properties, parse_device_properties(Rest)};
88
16#24 -> {video_target_info, parse_video_target_info(Rest)};
89
16#25 -> {audio_target_info, parse_audio_target_info(Rest)};
90
16#26 -> {video_device_info, parse_av_device_info(Rest)};
91
16#27 -> {audio_device_info, parse_av_device_info(Rest)};
92
16#28 -> {video_output_info, parse_video_output_info(Rest)};
93
16#29 -> {audio_output_info, parse_audio_output_info(Rest)}
96
-spec parse_error_code(binary()) -> ok | {error, integer()}.
97
parse_error_code(<<ErrorCode:?BYTE, _Padding/binary>>) ->
100
_ -> {error, ErrorCode}
103
-spec parse_device_properties(binary()) -> config_change().
104
parse_device_properties(<<BitMap:?BITMAP1, Rest/binary>>) ->
106
?PREPEND_IF_BIT_SET(BitMap, 0,
107
FwVersion:3/binary, {fw_version, FwVersion},
108
Rest, Rest1, Acc0, Acc1),
109
?PREPEND_IF_BIT_SET(BitMap, 1,
110
?TINY_STRING_M(ControllerName, _S1),
111
{controller_name, ControllerName},
112
Rest1, Rest2, Acc1, Acc2),
113
?PREPEND_IF_BIT_SET(BitMap, 2,
114
?SMALL_STRING_M(ControllerDescription, _S2),
115
{controller_description, ControllerDescription},
116
Rest2, Rest3, Acc2, Acc3),
117
?PREPEND_IF_BIT_SET(BitMap, 3,
118
ControllerStatus:?BYTE,
119
{controller_status, ControllerStatus},
120
Rest3, _Padding, Acc3, Acc4),
123
-spec parse_video_target_info(binary()) -> config_change().
124
parse_video_target_info(<<TargetId:?DWORD, Status:?BYTE, _Padding/binary>>) ->
125
[{target_id, TargetId}, {status, Status}].
127
-spec parse_audio_target_info(binary()) -> [config_change()].
128
parse_audio_target_info(<<TargetId:?DWORD, BitMap:?BITMAP1, Rest/binary>>) ->
129
Acc0 = [{target_id, TargetId}],
130
?PREPEND_IF_BIT_SET(BitMap, 0,
131
Status:?BYTE, {status, Status},
132
Rest, Rest1, Acc0, Acc1),
133
?PREPEND_IF_BIT_SET(BitMap, 1,
134
MasterVolume:?WORD, {master_volume, MasterVolume},
135
Rest1, _Padding, Acc1, Acc2),
138
-spec parse_av_device_info(binary()) -> [config_change()].
139
parse_av_device_info(<<DeviceId:?DWORD, BitMap:?BITMAP1, Rest/binary>>) ->
140
Acc0 = [{device_id, DeviceId}],
141
?PREPEND_IF_BIT_SET(BitMap, 0,
142
TargetId:?DWORD, {target_id, TargetId},
143
Rest, Rest1, Acc0, Acc1),
144
?PREPEND_IF_BIT_SET(BitMap, 1,
145
?TINY_STRING_M(Model, _S1), {model, Model},
146
Rest1, Rest2, Acc1, Acc2),
147
?PREPEND_IF_BIT_SET(BitMap, 2,
148
Address:?BYTE, {address, Address},
149
Rest2, Rest3, Acc2, Acc3),
150
?PREPEND_IF_BIT_SET(BitMap, 3,
151
Status:?BYTE, {status, Status},
152
Rest3, _Padding, Acc3, Acc4),
155
-spec parse_video_output_info(binary()) -> [config_change()].
156
parse_video_output_info(<<Output:?DWORD, BitMap:?BITMAP1, Rest/binary>>) ->
157
Acc0 = [{output_id, Output}],
158
?PREPEND_IF_BIT_SET(BitMap, 0,
159
DeviceId:?DWORD, {device_id, DeviceId},
160
Rest, Rest1, Acc0, Acc1),
161
?PREPEND_IF_BIT_SET(BitMap, 1,
162
?TINY_STRING_M(DisplayType, _S1),
163
{display_type, DisplayType},
164
Rest1, Rest2, Acc1, Acc2),
165
?PREPEND_IF_BIT_SET(BitMap, 2,
167
{audio_volume, AudioVolume},
168
Rest2, _Padding, Acc2, Acc3),
171
-spec parse_audio_output_info(binary()) -> [config_change()].
172
parse_audio_output_info(<<Output:?DWORD, BitMap:?BITMAP1, Rest/binary>>) ->
173
Acc0 = [{output_id, Output}],
174
?PREPEND_IF_BIT_SET(BitMap, 0,
175
DeviceId:?DWORD, {device_id, DeviceId},
176
Rest, Rest1, Acc0, Acc1),
177
?PREPEND_IF_BIT_SET(BitMap, 1,
178
AudioVolume:?WORD, {audio_volume, AudioVolume},
179
Rest1, Rest2, Acc1, Acc2),
180
?PREPEND_IF_BIT_SET(BitMap, 2,
181
Delay:?WORD, {delay, Delay},
182
Rest2, _Padding, Acc2, Acc3),
185
-spec message_to_packet(binary(), binary(), binary()) -> {binary(), binary()}.
186
message_to_packet(Message, Key, IVec) ->
187
PaddedMessage = pad_pkcs5(Message),
188
Packet = crypto:aes_cbc_128_encrypt(Key, IVec, PaddedMessage),
189
TotalSize = byte_size(Packet),
190
NewIVec = crypto:aes_cbc_ivec(Packet),
191
{<<TotalSize:?WORD, Packet/binary>>, NewIVec}.
193
-spec pad_pkcs5(binary()) -> binary().
194
pad_pkcs5(Message) ->
195
Size = byte_size(Message),
196
PaddingSize = case Size rem 16 of
200
pad_pkcs5(Message, PaddingSize, PaddingSize).
202
-spec pad_pkcs5(binary(), integer(), integer()) -> binary().
203
pad_pkcs5(Message, _PaddingSize, 0) ->
205
pad_pkcs5(Message, PaddingSize, PaddingSizeRemaining) ->
206
pad_pkcs5(<<Message/binary, PaddingSize:?BYTE>>,
207
PaddingSize, PaddingSizeRemaining - 1).