1111import struct
1212import socket
1313import time
14+ import ssl
1415
1516from scapy .contrib .automotive import log_automotive
1617from scapy .fields import (
2728 XStrField ,
2829)
2930from scapy .packet import Packet , bind_layers , bind_bottom_up
30- from scapy .supersocket import StreamSocket
31+ from scapy .supersocket import StreamSocket , SSLStreamSocket
3132from scapy .layers .inet import TCP , UDP
3233from scapy .contrib .automotive .uds import UDS
3334from scapy .data import MTU
3940 Optional ,
4041)
4142
43+
4244# ISO 13400-2 sect 9.2
4345
4446
@@ -247,8 +249,8 @@ def post_build(self, pkt, pay):
247249 This will set the Field 'payload_length' to the correct value.
248250 """
249251 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 :]
252254 return pkt + pay
253255
254256 def extract_padding (self , s ):
@@ -259,13 +261,24 @@ def extract_padding(self, s):
259261 return b"" , None
260262
261263
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
265277 established.
266278
267279 :param ip: IP address of destination
268280 :param port: destination port, usually 13400
281+ :param tls_port: destination port for TLS connection, usually 3496
269282 :param activate_routing: If true, routing activation request is
270283 automatically sent
271284 :param source_address: DoIP source address
@@ -275,84 +288,158 @@ class DoIPSocket(StreamSocket):
275288 the routing activation request
276289 :param reserved_oem: Optional parameter to set value for reserved_oem field
277290 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.
278295
279296 Example:
280297 >>> socket = DoIPSocket("169.254.0.131")
281298 >>> pkt = DoIP(payload_type=0x8001, source_address=0xe80, target_address=0x1000) / UDS() / UDS_RDBI(identifiers=[0x1000])
282299 >>> resp = socket.sr1(pkt, timeout=1)
283300 """ # 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
288314 self .ip = ip
289315 self .port = port
316+ self .tls_port = tls_port
317+ self .activate_routing = activate_routing
290318 self .source_address = source_address
319+ self .target_address = target_address
320+ self .activation_type = activation_type
321+ self .reserved_oem = reserved_oem
291322 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
297330
298331 def recv (self , x = MTU , ** kwargs ):
299332 # 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 ]
306338
307339 len_int = struct .unpack (">I" , len_data [4 :8 ])[0 ]
308340 len_int += 8
309- self .buffer += self .ins .recv (len_int - len (self .buffer ))
310341
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 :
312344 return None
345+ pktbuf = self .buffer [:len_int ]
346+ self .buffer = self .buffer [len_int :]
313347
314- pkt = self .basecls (self .buffer , ** kwargs ) # type: Packet
315- self .buffer = b""
348+ pkt = self .basecls (pktbuf , ** kwargs ) # type: Packet
316349 return pkt
317350
318351 def _init_socket (self , sock_family = socket .AF_INET ):
319352 # type: (int) -> None
353+ connected = False
320354 s = socket .socket (sock_family , socket .SOCK_STREAM )
355+ s .settimeout (5 )
321356 s .setsockopt (socket .IPPROTO_TCP , socket .TCP_NODELAY , 1 )
322357 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
333414 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 ),
336417 verbose = False , timeout = 1 )
337418 if resp and resp .payload_type == 0x6 and \
338419 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 )
341422 log_automotive .info (
342423 "Routing activation successful! Target address set to: 0x%x" ,
343424 self .target_address )
344425 else :
345426 log_automotive .error (
346427 "Routing activation failed! Response: %s" , repr (resp ))
347428
429+ if resp and resp .payload_type == 0x6 :
430+ return resp .routing_activation_response
431+ else :
432+ return - 1
433+
348434
349435class 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.
353439
354440 :param ip: IPv6 address of destination
355441 :param port: destination port, usually 13400
442+ :param tls_port: destination port for TLS connection, usually 3496
356443 :param activate_routing: If true, routing activation request is
357444 automatically sent
358445 :param source_address: DoIP source address
@@ -362,29 +449,49 @@ class DoIPSocket6(DoIPSocket):
362449 the routing activation request
363450 :param reserved_oem: Optional parameter to set value for reserved_oem field
364451 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.
365456
366457 Example:
367458 >>> socket = DoIPSocket6("2001:16b8:3f0e:2f00:21a:37ff:febf:edb9")
368459 >>> socket_link_local = DoIPSocket6("fe80::30e8:80ff:fe07:6d43%eth1")
369460 >>> pkt = DoIP(payload_type=0x8001, source_address=0xe80, target_address=0x1000) / UDS() / UDS_RDBI(identifiers=[0x1000])
370461 >>> resp = socket.sr1(pkt, timeout=1)
371462 """ # 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
376476 self .ip = ip
377477 self .port = port
478+ self .tls_port = tls_port
479+ self .activate_routing = activate_routing
378480 self .source_address = source_address
481+ self .target_address = target_address
482+ self .activation_type = activation_type
483+ self .reserved_oem = reserved_oem
379484 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
385492
386493
387- class UDS_DoIPSocket ( DoIPSocket ):
494+ class _UDS_DoIPSocketBase ( StreamSocket ):
388495 """
389496 Application-Layer socket for DoIP endpoints. This socket takes care about
390497 the encapsulation of UDS packets into DoIP packets.
@@ -394,11 +501,14 @@ class UDS_DoIPSocket(DoIPSocket):
394501 >>> pkt = UDS() / UDS_RDBI(identifiers=[0x1000])
395502 >>> resp = socket.sr1(pkt, timeout=1)
396503 """
504+
397505 def send (self , x ):
398506 # type: (Union[Packet, bytes]) -> int
399507 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
402512 else :
403513 pkt = x
404514
@@ -407,35 +517,38 @@ def send(self, x):
407517 except AttributeError :
408518 pass
409519
410- return super (UDS_DoIPSocket , self ).send (pkt )
520+ return super ().send (pkt )
411521
412522 def recv (self , x = MTU , ** kwargs ):
413523 # type: (Optional[int], **Any) -> Optional[Packet]
414- pkt = super (UDS_DoIPSocket , self ).recv (x , ** kwargs )
524+ pkt = super ().recv (x , ** kwargs )
415525 if pkt and pkt .payload_type == 0x8001 :
416526 return pkt .payload
417527 else :
418528 return pkt
419529
420530
421- class UDS_DoIPSocket6 ( DoIPSocket6 , UDS_DoIPSocket ):
531+ class UDS_DoIPSocket ( _UDS_DoIPSocketBase , DoIPSocket ):
422532 """
423533 Application-Layer socket for DoIP endpoints. This socket takes care about
424534 the encapsulation of UDS packets into DoIP packets.
425535
426536 Example:
427- >>> socket = UDS_DoIPSocket6("2001:16b8:3f0e:2f00:21a:37ff:febf:edb9 ")
537+ >>> socket = UDS_DoIPSocket("169.254.117.238 ")
428538 >>> pkt = UDS() / UDS_RDBI(identifiers=[0x1000])
429539 >>> resp = socket.sr1(pkt, timeout=1)
430540 """
431541 pass
432542
433543
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.
440548
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