11
11
import struct
12
12
import socket
13
13
import time
14
+ import ssl
14
15
15
16
from scapy .contrib .automotive import log_automotive
16
17
from scapy .fields import (
27
28
XStrField ,
28
29
)
29
30
from scapy .packet import Packet , bind_layers , bind_bottom_up
30
- from scapy .supersocket import StreamSocket
31
+ from scapy .supersocket import StreamSocket , SSLStreamSocket
31
32
from scapy .layers .inet import TCP , UDP
32
33
from scapy .contrib .automotive .uds import UDS
33
34
from scapy .data import MTU
39
40
Optional ,
40
41
)
41
42
43
+
42
44
# ISO 13400-2 sect 9.2
43
45
44
46
@@ -247,8 +249,8 @@ def post_build(self, pkt, pay):
247
249
This will set the Field 'payload_length' to the correct value.
248
250
"""
249
251
if self .payload_length is None :
250
- pkt = pkt [:4 ] + struct .pack ("!I" , len ( pay ) + len ( pkt ) - 8 ) + \
251
- pkt [8 :]
252
+ pkt = pkt [:4 ] + struct .pack (
253
+ "!I" , len ( pay ) + len ( pkt ) - 8 ) + pkt [8 :]
252
254
return pkt + pay
253
255
254
256
def extract_padding (self , s ):
@@ -259,13 +261,24 @@ def extract_padding(self, s):
259
261
return b"" , None
260
262
261
263
262
- class DoIPSocket (StreamSocket ):
263
- """ Custom StreamSocket for DoIP communication. This sockets automatically
264
- sends a routing activation request as soon as a TCP connection is
264
+ bind_bottom_up (UDP , DoIP , sport = 13400 )
265
+ bind_bottom_up (UDP , DoIP , dport = 13400 )
266
+ bind_layers (UDP , DoIP , sport = 13400 , dport = 13400 )
267
+
268
+ bind_layers (TCP , DoIP , sport = 13400 )
269
+ bind_layers (TCP , DoIP , dport = 13400 )
270
+
271
+ bind_layers (DoIP , UDS , payload_type = 0x8001 )
272
+
273
+
274
+ class DoIPSocket (SSLStreamSocket ):
275
+ """Socket for DoIP communication. This sockets automatically
276
+ sends a routing activation request as soon as a TCP or TLS connection is
265
277
established.
266
278
267
279
:param ip: IP address of destination
268
280
:param port: destination port, usually 13400
281
+ :param tls_port: destination port for TLS connection, usually 3496
269
282
:param activate_routing: If true, routing activation request is
270
283
automatically sent
271
284
:param source_address: DoIP source address
@@ -275,84 +288,158 @@ class DoIPSocket(StreamSocket):
275
288
the routing activation request
276
289
:param reserved_oem: Optional parameter to set value for reserved_oem field
277
290
of routing activation request
291
+ :param force_tls: Skip establishing of a TCP connection and directly try to
292
+ connect via SSL/TLS
293
+ :param context: Optional ssl.SSLContext object for initialization of ssl socket
294
+ connections.
278
295
279
296
Example:
280
297
>>> socket = DoIPSocket("169.254.0.131")
281
298
>>> pkt = DoIP(payload_type=0x8001, source_address=0xe80, target_address=0x1000) / UDS() / UDS_RDBI(identifiers=[0x1000])
282
299
>>> resp = socket.sr1(pkt, timeout=1)
283
300
""" # noqa: E501
284
- def __init__ (self , ip = '127.0.0.1' , port = 13400 , activate_routing = True ,
285
- source_address = 0xe80 , target_address = 0 ,
286
- activation_type = 0 , reserved_oem = b"" ):
287
- # type: (str, int, bool, int, int, int, bytes) -> None
301
+
302
+ def __init__ (self ,
303
+ ip = '127.0.0.1' , # type: str
304
+ port = 13400 , # type: int
305
+ tls_port = 3496 , # type: int
306
+ activate_routing = True , # type: bool
307
+ source_address = 0xe80 , # type: int
308
+ target_address = 0 , # type: int
309
+ activation_type = 0 , # type: int
310
+ reserved_oem = b"" , # type: bytes
311
+ force_tls = False , # type: bool
312
+ context = None # type: Optional[ssl.SSLContext]
313
+ ): # type: (...) -> None
288
314
self .ip = ip
289
315
self .port = port
316
+ self .tls_port = tls_port
317
+ self .activate_routing = activate_routing
290
318
self .source_address = source_address
319
+ self .target_address = target_address
320
+ self .activation_type = activation_type
321
+ self .reserved_oem = reserved_oem
291
322
self .buffer = b""
292
- self ._init_socket ()
293
-
294
- if activate_routing :
295
- self ._activate_routing (
296
- source_address , target_address , activation_type , reserved_oem )
323
+ self .force_tls = force_tls
324
+ self .context = context
325
+ try :
326
+ self ._init_socket (socket .AF_INET )
327
+ except Exception :
328
+ self .close ()
329
+ raise
297
330
298
331
def recv (self , x = MTU , ** kwargs ):
299
332
# type: (Optional[int], **Any) -> Optional[Packet]
300
- if self .buffer :
301
- len_data = self .buffer [:8 ]
302
- else :
303
- len_data = self .ins .recv (8 , socket .MSG_PEEK )
304
- if len (len_data ) != 8 :
305
- return None
333
+ if len (self .buffer ) < 8 :
334
+ self .buffer += self .ins .recv (8 )
335
+ if len (self .buffer ) < 8 :
336
+ return None
337
+ len_data = self .buffer [:8 ]
306
338
307
339
len_int = struct .unpack (">I" , len_data [4 :8 ])[0 ]
308
340
len_int += 8
309
- self .buffer += self .ins .recv (len_int - len (self .buffer ))
310
341
311
- if len (self .buffer ) != len_int :
342
+ self .buffer += self .ins .recv (len_int - len (self .buffer ))
343
+ if len (self .buffer ) < len_int :
312
344
return None
345
+ pktbuf = self .buffer [:len_int ]
346
+ self .buffer = self .buffer [len_int :]
313
347
314
- pkt = self .basecls (self .buffer , ** kwargs ) # type: Packet
315
- self .buffer = b""
348
+ pkt = self .basecls (pktbuf , ** kwargs ) # type: Packet
316
349
return pkt
317
350
318
351
def _init_socket (self , sock_family = socket .AF_INET ):
319
352
# type: (int) -> None
353
+ connected = False
320
354
s = socket .socket (sock_family , socket .SOCK_STREAM )
355
+ s .settimeout (5 )
321
356
s .setsockopt (socket .IPPROTO_TCP , socket .TCP_NODELAY , 1 )
322
357
s .setsockopt (socket .SOL_SOCKET , socket .SO_REUSEADDR , 1 )
323
- addrinfo = socket .getaddrinfo (self .ip , self .port , proto = socket .IPPROTO_TCP )
324
- s .connect (addrinfo [0 ][- 1 ])
325
- StreamSocket .__init__ (self , s , DoIP )
326
-
327
- def _activate_routing (self ,
328
- source_address , # type: int
329
- target_address , # type: int
330
- activation_type , # type: int
331
- reserved_oem = b"" # type: bytes
332
- ): # type: (...) -> None
358
+
359
+ if not self .force_tls :
360
+ addrinfo = socket .getaddrinfo (self .ip , self .port , proto = socket .IPPROTO_TCP )
361
+ s .connect (addrinfo [0 ][- 1 ])
362
+ connected = True
363
+ SSLStreamSocket .__init__ (self , s , DoIP )
364
+
365
+ if not self .activate_routing :
366
+ return
367
+
368
+ activation_return = self ._activate_routing ()
369
+ else :
370
+ # Let's overwrite activation_return to force TLS Connection
371
+ activation_return = 0x07
372
+
373
+ if activation_return == 0x10 :
374
+ # Routing successfully activated.
375
+ return
376
+ elif activation_return == 0x07 :
377
+ # Routing activation denied because the specified activation
378
+ # type requires a secure TLS TCP_DATA socket.
379
+ if self .context is None :
380
+ raise ValueError ("SSLContext 'context' can not be None" )
381
+ if connected :
382
+ s .close ()
383
+ s = socket .socket (sock_family , socket .SOCK_STREAM )
384
+ s .settimeout (5 )
385
+ s .setsockopt (socket .IPPROTO_TCP , socket .TCP_NODELAY , 1 )
386
+ s .setsockopt (socket .SOL_SOCKET , socket .SO_REUSEADDR , 1 )
387
+
388
+ ss = self .context .wrap_socket (s )
389
+ addrinfo = socket .getaddrinfo (
390
+ self .ip , self .tls_port , proto = socket .IPPROTO_TCP )
391
+ ss .connect (addrinfo [0 ][- 1 ])
392
+ SSLStreamSocket .__init__ (self , ss , DoIP )
393
+
394
+ if not self .activate_routing :
395
+ return
396
+
397
+ activation_return = self ._activate_routing ()
398
+ if activation_return == 0x10 :
399
+ # Routing successfully activated.
400
+ return
401
+ else :
402
+ raise Exception (
403
+ "DoIPSocket activate_routing failed with "
404
+ "routing_activation_response 0x%x" % activation_return )
405
+
406
+ elif activation_return == - 1 :
407
+ raise Exception ("DoIPSocket._activate_routing failed" )
408
+ else :
409
+ raise Exception (
410
+ "DoIPSocket activate_routing failed with "
411
+ "routing_activation_response 0x%x!" % activation_return )
412
+
413
+ def _activate_routing (self ): # type: (...) -> int
333
414
resp = self .sr1 (
334
- DoIP (payload_type = 0x5 , activation_type = activation_type ,
335
- source_address = source_address , reserved_oem = reserved_oem ),
415
+ DoIP (payload_type = 0x5 , activation_type = self . activation_type ,
416
+ source_address = self . source_address , reserved_oem = self . reserved_oem ),
336
417
verbose = False , timeout = 1 )
337
418
if resp and resp .payload_type == 0x6 and \
338
419
resp .routing_activation_response == 0x10 :
339
- self .target_address = target_address or \
340
- resp .logical_address_doip_entity
420
+ self .target_address = (
421
+ self . target_address or resp .logical_address_doip_entity )
341
422
log_automotive .info (
342
423
"Routing activation successful! Target address set to: 0x%x" ,
343
424
self .target_address )
344
425
else :
345
426
log_automotive .error (
346
427
"Routing activation failed! Response: %s" , repr (resp ))
347
428
429
+ if resp and resp .payload_type == 0x6 :
430
+ return resp .routing_activation_response
431
+ else :
432
+ return - 1
433
+
348
434
349
435
class DoIPSocket6 (DoIPSocket ):
350
- """ Custom StreamSocket for DoIP communication over IPv6.
351
- This sockets automatically sends a routing activation request as soon as
352
- a TCP connection is established.
436
+ """Socket for DoIP communication. This sockets automatically
437
+ sends a routing activation request as soon as a TCP or TLS connection is
438
+ established.
353
439
354
440
:param ip: IPv6 address of destination
355
441
:param port: destination port, usually 13400
442
+ :param tls_port: destination port for TLS connection, usually 3496
356
443
:param activate_routing: If true, routing activation request is
357
444
automatically sent
358
445
:param source_address: DoIP source address
@@ -362,29 +449,49 @@ class DoIPSocket6(DoIPSocket):
362
449
the routing activation request
363
450
:param reserved_oem: Optional parameter to set value for reserved_oem field
364
451
of routing activation request
452
+ :param force_tls: Skip establishing of a TCP connection and directly try to
453
+ connect via SSL/TLS
454
+ :param context: Optional ssl.SSLContext object for initialization of ssl socket
455
+ connections.
365
456
366
457
Example:
367
458
>>> socket = DoIPSocket6("2001:16b8:3f0e:2f00:21a:37ff:febf:edb9")
368
459
>>> socket_link_local = DoIPSocket6("fe80::30e8:80ff:fe07:6d43%eth1")
369
460
>>> pkt = DoIP(payload_type=0x8001, source_address=0xe80, target_address=0x1000) / UDS() / UDS_RDBI(identifiers=[0x1000])
370
461
>>> resp = socket.sr1(pkt, timeout=1)
371
462
""" # noqa: E501
372
- def __init__ (self , ip = '::1' , port = 13400 , activate_routing = True ,
373
- source_address = 0xe80 , target_address = 0 ,
374
- activation_type = 0 , reserved_oem = b"" ):
375
- # type: (str, int, bool, int, int, int, bytes) -> None
463
+
464
+ def __init__ (self ,
465
+ ip = '::1' , # type: str
466
+ port = 13400 , # type: int
467
+ tls_port = 3496 , # type: int
468
+ activate_routing = True , # type: bool
469
+ source_address = 0xe80 , # type: int
470
+ target_address = 0 , # type: int
471
+ activation_type = 0 , # type: int
472
+ reserved_oem = b"" , # type: bytes
473
+ force_tls = False , # type: bool
474
+ context = None # type: Optional[ssl.SSLContext]
475
+ ): # type: (...) -> None
376
476
self .ip = ip
377
477
self .port = port
478
+ self .tls_port = tls_port
479
+ self .activate_routing = activate_routing
378
480
self .source_address = source_address
481
+ self .target_address = target_address
482
+ self .activation_type = activation_type
483
+ self .reserved_oem = reserved_oem
379
484
self .buffer = b""
380
- super (DoIPSocket6 , self )._init_socket (socket .AF_INET6 )
381
-
382
- if activate_routing :
383
- super (DoIPSocket6 , self )._activate_routing (
384
- source_address , target_address , activation_type , reserved_oem )
485
+ self .force_tls = force_tls
486
+ self .context = context
487
+ try :
488
+ self ._init_socket (socket .AF_INET6 )
489
+ except Exception :
490
+ self .close ()
491
+ raise
385
492
386
493
387
- class UDS_DoIPSocket ( DoIPSocket ):
494
+ class _UDS_DoIPSocketBase ( StreamSocket ):
388
495
"""
389
496
Application-Layer socket for DoIP endpoints. This socket takes care about
390
497
the encapsulation of UDS packets into DoIP packets.
@@ -394,11 +501,14 @@ class UDS_DoIPSocket(DoIPSocket):
394
501
>>> pkt = UDS() / UDS_RDBI(identifiers=[0x1000])
395
502
>>> resp = socket.sr1(pkt, timeout=1)
396
503
"""
504
+
397
505
def send (self , x ):
398
506
# type: (Union[Packet, bytes]) -> int
399
507
if isinstance (x , UDS ):
400
- pkt = DoIP (payload_type = 0x8001 , source_address = self .source_address ,
401
- target_address = self .target_address ) / x
508
+ pkt = DoIP (payload_type = 0x8001 ,
509
+ source_address = self .source_address , # type: ignore
510
+ target_address = self .target_address # type: ignore
511
+ ) / x
402
512
else :
403
513
pkt = x
404
514
@@ -407,35 +517,38 @@ def send(self, x):
407
517
except AttributeError :
408
518
pass
409
519
410
- return super (UDS_DoIPSocket , self ).send (pkt )
520
+ return super ().send (pkt )
411
521
412
522
def recv (self , x = MTU , ** kwargs ):
413
523
# type: (Optional[int], **Any) -> Optional[Packet]
414
- pkt = super (UDS_DoIPSocket , self ).recv (x , ** kwargs )
524
+ pkt = super ().recv (x , ** kwargs )
415
525
if pkt and pkt .payload_type == 0x8001 :
416
526
return pkt .payload
417
527
else :
418
528
return pkt
419
529
420
530
421
- class UDS_DoIPSocket6 ( DoIPSocket6 , UDS_DoIPSocket ):
531
+ class UDS_DoIPSocket ( _UDS_DoIPSocketBase , DoIPSocket ):
422
532
"""
423
533
Application-Layer socket for DoIP endpoints. This socket takes care about
424
534
the encapsulation of UDS packets into DoIP packets.
425
535
426
536
Example:
427
- >>> socket = UDS_DoIPSocket6("2001:16b8:3f0e:2f00:21a:37ff:febf:edb9 ")
537
+ >>> socket = UDS_DoIPSocket("169.254.117.238 ")
428
538
>>> pkt = UDS() / UDS_RDBI(identifiers=[0x1000])
429
539
>>> resp = socket.sr1(pkt, timeout=1)
430
540
"""
431
541
pass
432
542
433
543
434
- bind_bottom_up (UDP , DoIP , sport = 13400 )
435
- bind_bottom_up (UDP , DoIP , dport = 13400 )
436
- bind_layers (UDP , DoIP , sport = 13400 , dport = 13400 )
437
-
438
- bind_layers (TCP , DoIP , sport = 13400 )
439
- bind_layers (TCP , DoIP , dport = 13400 )
544
+ class UDS_DoIPSocket6 (_UDS_DoIPSocketBase , DoIPSocket6 ):
545
+ """
546
+ Application-Layer socket for DoIP endpoints. This socket takes care about
547
+ the encapsulation of UDS packets into DoIP packets.
440
548
441
- bind_layers (DoIP , UDS , payload_type = 0x8001 )
549
+ Example:
550
+ >>> socket = UDS_DoIPSocket6("2001:16b8:3f0e:2f00:21a:37ff:febf:edb9")
551
+ >>> pkt = UDS() / UDS_RDBI(identifiers=[0x1000])
552
+ >>> resp = socket.sr1(pkt, timeout=1)
553
+ """
554
+ pass
0 commit comments