@@ -63,6 +63,7 @@ defmodule ExICE.Priv.ICEAgentTest do
63
63
end
64
64
65
65
@ remote_cand ExICE.Candidate . new ( :host , address: { 192 , 168 , 0 , 2 } , port: 8445 )
66
+ @ remote_cand2 ExICE.Candidate . new ( :host , address: { 192 , 168 , 0 , 3 } , port: 8445 )
66
67
67
68
describe "unmarshal_remote_candidate/1" do
68
69
test "with correct candidate" do
@@ -644,6 +645,62 @@ defmodule ExICE.Priv.ICEAgentTest do
644
645
assert_bad_request_error_response ( socket , request )
645
646
end
646
647
648
+ test "with role conflict" , % { ice_agent: ice_agent , remote_cand: remote_cand } do
649
+ [ socket ] = ice_agent . sockets
650
+
651
+ binding_request = fn tiebreaker ->
652
+ Message . new ( % Type { class: :request , method: :binding } , [
653
+ % Username { value: "#{ ice_agent . local_ufrag } :someufrag" } ,
654
+ % Priority { priority: 1234 } ,
655
+ % ICEControlling { tiebreaker: tiebreaker } ,
656
+ % UseCandidate { }
657
+ ] )
658
+ |> Message . with_integrity ( ice_agent . local_pwd )
659
+ |> Message . with_fingerprint ( )
660
+ end
661
+
662
+ # feed binding request with higher tiebreaker
663
+ request = binding_request . ( ice_agent . tiebreaker + 1 )
664
+ raw_request = Message . encode ( request )
665
+
666
+ new_ice_agent =
667
+ ICEAgent . handle_udp (
668
+ ice_agent ,
669
+ socket ,
670
+ remote_cand . address ,
671
+ remote_cand . port ,
672
+ raw_request
673
+ )
674
+
675
+ # agent should switch its role and send success response
676
+ assert new_ice_agent . role == :controlled
677
+ assert new_ice_agent . tiebreaker == ice_agent . tiebreaker
678
+ assert packet = Transport.Mock . recv ( socket )
679
+ assert { :ok , msg } = ExSTUN.Message . decode ( packet )
680
+ assert msg . type == % ExSTUN.Message.Type { class: :success_response , method: :binding }
681
+
682
+ # feed binding request with smaller tiebreaker
683
+ request = binding_request . ( ice_agent . tiebreaker - 1 )
684
+ raw_request = Message . encode ( request )
685
+
686
+ new_ice_agent =
687
+ ICEAgent . handle_udp (
688
+ ice_agent ,
689
+ socket ,
690
+ remote_cand . address ,
691
+ remote_cand . port ,
692
+ raw_request
693
+ )
694
+
695
+ # agent shouldn't switch its role and should send 487 error response
696
+ assert new_ice_agent . role == :controlling
697
+ assert new_ice_agent . tiebreaker == ice_agent . tiebreaker
698
+ assert packet = Transport.Mock . recv ( socket )
699
+ assert { :ok , msg } = ExSTUN.Message . decode ( packet )
700
+ assert msg . type == % ExSTUN.Message.Type { class: :error_response , method: :binding }
701
+ assert { :ok , % ErrorCode { code: 487 , reason: "" } } = Message . get_attribute ( msg , ErrorCode )
702
+ end
703
+
647
704
test "without username" , % { ice_agent: ice_agent , remote_cand: remote_cand } do
648
705
[ socket ] = ice_agent . sockets
649
706
@@ -1157,6 +1214,80 @@ defmodule ExICE.Priv.ICEAgentTest do
1157
1214
assert new_pair . responses_received == pair . responses_received + 1
1158
1215
end
1159
1216
1217
+ test "role conflict error response" do
1218
+ ice_agent =
1219
+ ICEAgent . new (
1220
+ controlling_process: self ( ) ,
1221
+ role: :controlling ,
1222
+ if_discovery_module: IfDiscovery.Mock ,
1223
+ transport_module: Transport.Mock
1224
+ )
1225
+ |> ICEAgent . set_remote_credentials ( "someufrag" , "somepwd" )
1226
+ |> ICEAgent . gather_candidates ( )
1227
+ |> ICEAgent . add_remote_candidate ( @ remote_cand )
1228
+
1229
+ [ socket ] = ice_agent . sockets
1230
+
1231
+ # trigger check
1232
+ ice_agent = ICEAgent . handle_ta_timeout ( ice_agent )
1233
+ [ pair1 ] = Map . values ( ice_agent . checklist )
1234
+ req1 = read_binding_request ( socket , ice_agent . remote_pwd )
1235
+
1236
+ # Add the second candidate and trigger another check.
1237
+ # We add candidate after generating the first check to be sure
1238
+ # it is related to @remote_cand2.
1239
+ ice_agent = ICEAgent . add_remote_candidate ( ice_agent , @ remote_cand2 )
1240
+ ice_agent = ICEAgent . handle_ta_timeout ( ice_agent )
1241
+ [ pair2 ] = Map . values ( ice_agent . checklist ) |> Enum . reject ( & ( & 1 . id == pair1 . id ) )
1242
+ req2 = read_binding_request ( socket , ice_agent . remote_pwd )
1243
+
1244
+ # reply to the first check with role conflict error response
1245
+ resp =
1246
+ Message . new ( req1 . transaction_id , % Type { class: :error_response , method: :binding } , [
1247
+ % ErrorCode { code: 487 }
1248
+ ] )
1249
+ |> Message . with_integrity ( ice_agent . remote_pwd )
1250
+ |> Message . with_fingerprint ( )
1251
+ |> Message . encode ( )
1252
+
1253
+ new_ice_agent =
1254
+ ICEAgent . handle_udp (
1255
+ ice_agent ,
1256
+ socket ,
1257
+ @ remote_cand . address ,
1258
+ @ remote_cand . port ,
1259
+ resp
1260
+ )
1261
+
1262
+ # assert that the agent changed its role and tiebreaker
1263
+ assert new_ice_agent . role != ice_agent . role
1264
+ assert new_ice_agent . tiebreaker != ice_agent . tiebreaker
1265
+ assert Map . fetch! ( new_ice_agent . checklist , pair1 . id ) . state == :waiting
1266
+
1267
+ # reply to the second check with role conflict error response
1268
+ resp =
1269
+ Message . new ( req2 . transaction_id , % Type { class: :error_response , method: :binding } , [
1270
+ % ErrorCode { code: 487 }
1271
+ ] )
1272
+ |> Message . with_integrity ( new_ice_agent . remote_pwd )
1273
+ |> Message . with_fingerprint ( )
1274
+ |> Message . encode ( )
1275
+
1276
+ new_ice_agent2 =
1277
+ ICEAgent . handle_udp (
1278
+ new_ice_agent ,
1279
+ socket ,
1280
+ @ remote_cand2 . address ,
1281
+ @ remote_cand2 . port ,
1282
+ resp
1283
+ )
1284
+
1285
+ # assert that agent didn't switch its role and tiebreaker
1286
+ assert new_ice_agent2 . role == new_ice_agent . role
1287
+ assert new_ice_agent2 . tiebreaker == new_ice_agent . tiebreaker
1288
+ assert Map . fetch! ( new_ice_agent2 . checklist , pair2 . id ) . state == :waiting
1289
+ end
1290
+
1160
1291
test "unauthenticated error response" , % { ice_agent: ice_agent , remote_cand: remote_cand } do
1161
1292
[ socket ] = ice_agent . sockets
1162
1293
[ pair ] = Map . values ( ice_agent . checklist )
0 commit comments