stun_codec.erl 12.4 KB
Newer Older
1 2 3 4 5 6 7
%%%-------------------------------------------------------------------
%%% File    : stun_codec.erl
%%% Author  : Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%% Description : STUN codec
%%% Created :  7 Aug 2009 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
8
%%% stun, Copyright (C) 2002-2015   ProcessOne
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 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 General Public License
%%% along with this program; if not, write to the Free Software
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
%%% 02111-1307 USA
%%%
%%%-------------------------------------------------------------------
26

27 28 29
-module(stun_codec).

%% API
30 31 32 33 34 35 36
-export([decode/2,
	 encode/1,
	 encode/2,
	 version/1,
	 error/1,
	 check_integrity/2,
	 add_fingerprint/1,
37 38 39 40
	 pp/1]).

-include("stun.hrl").

41 42 43
%%====================================================================
%% API
%%====================================================================
44
decode(<<0:2, Type:14, Len:16, Magic:32, TrID:96,
45
	Body:Len/binary, Tail/binary>> = Data, Transport) ->
46
    case catch decode(Type, Magic, TrID, Body) of
47 48 49 50 51 52
	{'EXIT', _} ->
	    {error, unparsed};
	{Res, RawSize} when Transport == datagram ->
	    {ok, add_raw(Res, Data, RawSize)};
	{Res, RawSize} ->
	    {ok, add_raw(Res, Data, RawSize), Tail}
53
    end;
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
decode(<<1:2, _:6, _/binary>> = Pkt, datagram) ->
    case Pkt of
	<<Channel:16, Len:16, Data:Len/binary, _/binary>> ->
	    {ok, #turn{channel = Channel, data = Data}};
	_ ->
	    {error, unparsed}
    end;
decode(<<1:2, _:6, _/binary>> = Pkt, stream) ->
    case Pkt of
	<<Channel:16, Len:16, Rest/binary>> ->
	    PaddLen = padd_len(Len),
	    case Rest of
		<<Data:Len/binary, _:PaddLen, Tail/binary>> ->
		    {ok, #turn{channel = Channel, data = Data}, Tail};
		_ ->
		    more
	    end;
	_ ->
	    more
    end;
decode(<<0:2, _:6, _/binary>>, stream) ->
    more;
decode(<<>>, stream) ->
    empty;
decode(_, _Transport) ->
    {error, unparsed}.

encode(Msg) ->
    encode(Msg, undefined).

encode(#turn{channel = Channel, data = Data}, _Password) ->
    Len = size(Data),
    PaddLen = padd_len(Len),
    <<Channel:16, Len:16, Data/binary, 0:PaddLen>>;
encode(#stun{class = Class,
	     method = Method,
	     magic = Magic,
	     trid = TrID} = Msg, Key) ->
92
    ClassCode = case Class of
93 94 95 96
		    request -> 0;
		    indication -> 1;
		    response -> 2;
		    error -> 3
97
		end,
98
    Type = ?STUN_TYPE(ClassCode, Method),
99
    Attrs = enc_attrs(Msg),
100 101 102 103
    Len = size(Attrs),
    if Key /= undefined ->
	    NewKey = case Key of
			 {User, Realm, Password} ->
104
			     crypto:hash(md5, [User, $:, Realm, $:, Password]);
105 106 107 108 109
			 _ ->
			     Key
		     end,
	    Data = <<0:2, Type:14, (Len+24):16, Magic:32,
		    TrID:96, Attrs/binary>>,
110
	    MessageIntegrity = crypto:hmac(sha, NewKey, Data),
111 112 113 114 115 116
	    <<Data/binary, ?STUN_ATTR_MESSAGE_INTEGRITY:16,
	     20:16, MessageIntegrity/binary>>;
       true ->
	    <<0:2, Type:14, Len:16, Magic:32,
	     TrID:96, Attrs/binary>>
    end.
117

118 119 120 121 122 123 124 125 126
add_fingerprint(<<T:16, L:16, Tail/binary>>) ->
    Data = <<T:16, (L+8):16, Tail/binary>>,
    CRC32 = erlang:crc32(Data),
    <<Data/binary, ?STUN_ATTR_FINGERPRINT:16, 4:16, CRC32:32>>.

check_integrity(#stun{raw = Raw, 'MESSAGE-INTEGRITY' = MI}, Key)
  when is_binary(Raw), is_binary(MI), Key /= undefined ->
    NewKey = case Key of
		 {User, Realm, Password} ->
127
		     crypto:hash(md5, [User, $:, Realm, $:, Password]);
128 129 130
		 _ ->
		     Key
	     end,
131
    crypto:hmac(sha, NewKey, Raw) == MI;
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
check_integrity(_Msg, _Key) ->
    false.

pp(Term) ->
    io_lib_pretty:print(Term, fun pp/2).

version(#stun{magic = ?STUN_MAGIC}) ->
    new;
version(#stun{}) ->
    old.

error(300) -> {300, <<"Try Alternate">>};
error(400) -> {400, <<"Bad Request">>};
error(401) -> {401, <<"Unauthorized">>};
error(403) -> {403, <<"Forbidden">>};
error(405) -> {405, <<"Method Not Allowed">>};
error(420) -> {420, <<"Unknown Attribute">>};
error(437) -> {437, <<"Allocation Mismatch">>};
error(438) -> {438, <<"Stale Nonce">>};
error(441) -> {441, <<"Wrong Credentials">>};
error(442) -> {442, <<"Unsupported Transport Protocol">>};
error(486) -> {486, <<"Allocation Quota Reached">>};
error(500) -> {500, <<"Server Error">>};
error(508) -> {508, <<"Insufficient Capacity">>};
error(Int) -> {Int, <<"Undefined Error">>}.

%%====================================================================
%% Internal functions
%%====================================================================
161
decode(Type, Magic, TrID, Body) ->
162
    Method = ?STUN_METHOD(Type),
163
    Class = case ?STUN_CLASS(Type) of
164 165 166 167
		0 -> request;
		1 -> indication;
		2 -> response;
		3 -> error
168
	    end,
169 170 171 172
    dec_attrs(Body, 20, #stun{class = Class,
			      method = Method,
			      magic = Magic,
			      trid = TrID}).
173

174
dec_attrs(<<Type:16, Len:16, Rest/binary>>, Bytes, Msg) ->
175 176 177
    PaddLen = padd_len(Len),
    <<Val:Len/binary, _:PaddLen, Tail/binary>> = Rest,
    NewMsg = dec_attr(Type, Val, Msg),
178 179 180 181 182
    if Type == ?STUN_ATTR_MESSAGE_INTEGRITY ->
	    {NewMsg, Bytes};
       true ->
	    NewBytes = Bytes + 4 + Len + (PaddLen div 8),
	    dec_attrs(Tail, NewBytes, NewMsg)
183
    end;
184 185
dec_attrs(<<>>, _Bytes, Msg) ->
    {Msg, 0}.
186 187

enc_attrs(Msg) ->
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
    iolist_to_binary(
      [enc_attr(?STUN_ATTR_SOFTWARE, Msg#stun.'SOFTWARE'),
       enc_addr(?STUN_ATTR_MAPPED_ADDRESS, Msg#stun.'MAPPED-ADDRESS'),
       enc_xor_addr(?STUN_ATTR_XOR_MAPPED_ADDRESS,
		    Msg#stun.magic, Msg#stun.trid,
		    Msg#stun.'XOR-MAPPED-ADDRESS'),
       enc_xor_addr(?STUN_ATTR_XOR_RELAYED_ADDRESS,
		    Msg#stun.magic, Msg#stun.trid,
		    Msg#stun.'XOR-RELAYED-ADDRESS'),
       enc_xor_peer_addr(Msg#stun.magic, Msg#stun.trid,
			 Msg#stun.'XOR-PEER-ADDRESS'),
       enc_req_trans(Msg#stun.'REQUESTED-TRANSPORT'),
       enc_attr(?STUN_ATTR_DATA, Msg#stun.'DATA'),
       enc_df(Msg#stun.'DONT-FRAGMENT'),
       enc_addr(?STUN_ATTR_ALTERNATE_SERVER, Msg#stun.'ALTERNATE-SERVER'),
       enc_attr(?STUN_ATTR_USERNAME, Msg#stun.'USERNAME'),
       enc_attr(?STUN_ATTR_REALM, Msg#stun.'REALM'),
       enc_attr(?STUN_ATTR_NONCE, Msg#stun.'NONCE'),
       enc_error_code(Msg#stun.'ERROR-CODE'),
       enc_uint32(?STUN_ATTR_LIFETIME, Msg#stun.'LIFETIME'),
       enc_chan(Msg#stun.'CHANNEL-NUMBER'),
       enc_unknown_attrs(Msg#stun.'UNKNOWN-ATTRIBUTES')]).
210 211 212 213 214 215

dec_attr(?STUN_ATTR_MAPPED_ADDRESS, Val, Msg) ->
    <<_, Family, Port:16, AddrBin/binary>> = Val,
    Addr = dec_addr(Family, AddrBin),
    Msg#stun{'MAPPED-ADDRESS' = {Addr, Port}};
dec_attr(?STUN_ATTR_XOR_MAPPED_ADDRESS, Val, Msg) ->
216 217
    AddrPort = dec_xor_addr(Val, Msg),
    Msg#stun{'XOR-MAPPED-ADDRESS' = AddrPort};
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
dec_attr(?STUN_ATTR_SOFTWARE, Val, Msg) ->
    Msg#stun{'SOFTWARE' = Val};
dec_attr(?STUN_ATTR_USERNAME, Val, Msg) ->
    Msg#stun{'USERNAME' = Val};
dec_attr(?STUN_ATTR_REALM, Val, Msg) ->
    Msg#stun{'REALM' = Val};
dec_attr(?STUN_ATTR_NONCE, Val, Msg) ->
    Msg#stun{'NONCE' = Val};
dec_attr(?STUN_ATTR_MESSAGE_INTEGRITY, Val, Msg) ->
    Msg#stun{'MESSAGE-INTEGRITY' = Val};
dec_attr(?STUN_ATTR_ALTERNATE_SERVER, Val, Msg) ->
    <<_, Family, Port:16, Address/binary>> = Val,
    IP = dec_addr(Family, Address),
    Msg#stun{'ALTERNATE-SERVER' = {IP, Port}};
dec_attr(?STUN_ATTR_ERROR_CODE, Val, Msg) ->
    <<_:21, Class:3, Number:8, Reason/binary>> = Val,
234 235 236
    if Class >=3, Class =< 6, Number >=0, Number =< 99 ->
	    Code = Class * 100 + Number,
	    Msg#stun{'ERROR-CODE' = {Code, Reason}}
237 238 239 240
    end;
dec_attr(?STUN_ATTR_UNKNOWN_ATTRIBUTES, Val, Msg) ->
    Attrs = dec_unknown_attrs(Val, []),
    Msg#stun{'UNKNOWN-ATTRIBUTES' = Attrs};
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
dec_attr(?STUN_ATTR_XOR_RELAYED_ADDRESS, Val, Msg) ->
    AddrPort = dec_xor_addr(Val, Msg),
    Msg#stun{'XOR-RELAYED-ADDRESS' = AddrPort};
dec_attr(?STUN_ATTR_XOR_PEER_ADDRESS, Val, Msg) ->
    AddrPort = dec_xor_addr(Val, Msg),
    Tail = Msg#stun.'XOR-PEER-ADDRESS',
    Msg#stun{'XOR-PEER-ADDRESS' = [AddrPort|Tail]};
dec_attr(?STUN_ATTR_REQUESTED_TRANSPORT, Val, Msg) ->
    <<ProtoInt, _:3/binary>> = Val,
    Proto = case ProtoInt of
		17 -> udp;
		_ -> unknown
	    end,
    Msg#stun{'REQUESTED-TRANSPORT' = Proto};
dec_attr(?STUN_ATTR_DATA, Val, Msg) ->
    Msg#stun{'DATA' = Val};
dec_attr(?STUN_ATTR_LIFETIME, Val, Msg) ->
    <<Seconds:32>> = Val,
    Msg#stun{'LIFETIME' = Seconds};
dec_attr(?STUN_ATTR_DONT_FRAGMENT, _Val, Msg) ->
    Msg#stun{'DONT-FRAGMENT' = true};
dec_attr(?STUN_ATTR_CHANNEL_NUMBER, Val, Msg) ->
    <<Channel:16, _:16>> = Val,
    Msg#stun{'CHANNEL-NUMBER' = Channel};
265
dec_attr(Attr, _Val, #stun{unsupported = Attrs} = Msg)
266 267 268 269 270 271 272 273 274
  when Attr < 16#8000 ->
    Msg#stun{unsupported = [Attr|Attrs]};
dec_attr(_Attr, _Val, Msg) ->
    Msg.

dec_addr(1, <<A1, A2, A3, A4>>) ->
    {A1, A2, A3, A4};
dec_addr(2, <<A1:16, A2:16, A3:16, A4:16,
	     A5:16, A6:16, A7:16, A8:16>>) ->
275 276
    {A1, A2, A3, A4, A5, A6, A7, A8}.

277 278 279 280 281 282
dec_xor_addr(<<_, Family, XPort:16, XAddr/binary>>, Msg) ->
    Magic = Msg#stun.magic,
    Port = XPort bxor (Magic bsr 16),
    Addr = dec_xor_addr(Family, Magic, Msg#stun.trid, XAddr),
    {Addr, Port}.

283
dec_xor_addr(1, Magic, _TrID, <<XAddr:32>>) ->
284 285
    Addr = XAddr bxor Magic,
    dec_addr(1, <<Addr:32>>);
286
dec_xor_addr(2, Magic, TrID, <<XAddr:128>>) ->
287
    Addr = XAddr bxor ((Magic bsl 96) bor TrID),
288 289 290
    dec_addr(2, <<Addr:128>>).

dec_unknown_attrs(<<Attr:16, Tail/binary>>, Acc) ->
291 292 293
    dec_unknown_attrs(Tail, [Attr|Acc]);
dec_unknown_attrs(<<>>, Acc) ->
    lists:reverse(Acc).
294

295 296
enc_attr(_Attr, undefined) ->
    <<>>;
297
enc_attr(Attr, Val) ->
298
    Len = size(Val),
299 300 301
    PaddLen = padd_len(Len),
    <<Attr:16, Len:16, Val/binary, 0:PaddLen>>.

302 303
enc_addr(_Type, undefined) ->
    <<>>;
304 305
enc_addr(Type, {{A1, A2, A3, A4}, Port}) ->
    enc_attr(Type, <<0, 1, Port:16, A1, A2, A3, A4>>);
306 307 308 309 310 311 312
enc_addr(Type, {{A1, A2, A3, A4, A5, A6, A7, A8}, Port}) ->
    enc_attr(Type, <<0, 2, Port:16, A1:16, A2:16, A3:16,
		    A4:16, A5:16, A6:16, A7:16, A8:16>>).

enc_xor_addr(_Type, _Magic, _TrID, undefined) ->
    <<>>;
enc_xor_addr(Type, Magic, _TrID, {{A1, A2, A3, A4}, Port}) ->
313 314 315 316 317 318 319
    XPort = Port bxor (Magic bsr 16),
    <<Addr:32>> = <<A1, A2, A3, A4>>,
    XAddr = Addr bxor Magic,
    enc_attr(Type, <<0, 1, XPort:16, XAddr:32>>);
enc_xor_addr(Type, Magic, TrID,
	     {{A1, A2, A3, A4, A5, A6, A7, A8}, Port}) ->
    XPort = Port bxor (Magic bsr 16),
320 321 322
    <<Addr:128>> = <<A1:16, A2:16, A3:16, A4:16,
		    A5:16, A6:16, A7:16, A8:16>>,
    XAddr = Addr bxor ((Magic bsl 96) bor TrID),
323 324
    enc_attr(Type, <<0, 2, XPort:16, XAddr:128>>).

325 326 327 328 329 330 331
enc_xor_peer_addr(Magic, TrID, AddrPortList) ->
    [enc_xor_addr(?STUN_ATTR_XOR_PEER_ADDRESS,
		  Magic, TrID, AddrPort) ||
	AddrPort <- AddrPortList].

enc_error_code(undefined) ->
    <<>>;
332 333 334 335 336 337
enc_error_code({Code, Reason}) ->
    Class = Code div 100,
    Number = Code rem 100,
    enc_attr(?STUN_ATTR_ERROR_CODE,
	     <<0:21, Class:3, Number:8, Reason/binary>>).

338 339
enc_unknown_attrs([]) ->
    <<>>;
340 341 342 343
enc_unknown_attrs(Attrs) ->
    enc_attr(?STUN_ATTR_UNKNOWN_ATTRIBUTES,
	     iolist_to_binary([<<Attr:16>> || Attr <- Attrs])).

344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
enc_uint32(_Type, undefined) ->
    <<>>;
enc_uint32(Type, Seconds) ->
    enc_attr(Type, <<Seconds:32>>).

enc_req_trans(undefined) ->
    <<>>;
enc_req_trans(udp) ->
    enc_attr(?STUN_ATTR_REQUESTED_TRANSPORT, <<17, 0:24>>).

enc_df(false) ->
    <<>>;
enc_df(true) ->
    enc_attr(?STUN_ATTR_DONT_FRAGMENT, <<>>).

enc_chan(undefined) ->
    <<>>;
enc_chan(Channel) ->
    enc_attr(?STUN_ATTR_CHANNEL_NUMBER, <<Channel:16, 0:16>>).

%%====================================================================
%% Auxiliary functions
%%====================================================================
pp(Tag, N) ->
    try
	pp1(Tag, N)
    catch _:_ ->
	    no
    end.
373 374 375 376

pp1(stun, N) ->
    N = record_info(size, stun) - 1,
    record_info(fields, stun);
377 378 379 380 381 382 383 384 385 386 387 388 389
pp1(turn, N) ->
    N = record_info(size, turn) - 1,
    record_info(fields, turn);
pp1(_, _) ->
    no.

add_raw(Msg, _Data, 0) ->
    Msg;
add_raw(Msg, Data, Size) ->
    <<Head:Size/binary, _/binary>> = Data,
    <<Type:16, _:16, Tail/binary>> = Head,
    Raw = <<Type:16, (Size+4):16, Tail/binary>>,
    Msg#stun{raw = Raw}.
390 391 392

%% Workaround for stupid clients.
-ifdef(NO_PADDING).
393 394
padd_len(_Len) ->
    0.
395 396 397
-else.
padd_len(Len) ->
    case Len rem 4 of
398 399
	0 -> 0;
	N -> 8*(4-N)
400 401
    end.
-endif.