Skip to content

Commit 141a95f

Browse files
committed
Code reviews:
- Moved the defines/enumerators to coap.py - Changed the send() function to match the SuperSocket declaration - Unconditionally gives the received packet to the user - Updated unit tests
1 parent aeb746a commit 141a95f

File tree

3 files changed

+86
-74
lines changed

3 files changed

+86
-74
lines changed

scapy/contrib/coap.py

+55-12
Original file line numberDiff line numberDiff line change
@@ -19,31 +19,71 @@
1919
from scapy.error import warning
2020
from scapy.compat import raw
2121

22+
"""
23+
CoAP message request codes (RFC 7252 @ section-5.8.1)
24+
"""
25+
EMPTY_MESSAGE = 0
26+
GET = 1
27+
POST = 2
28+
PUT = 3
29+
DELETE = 4
30+
COAP_REQ_CODES = [GET, POST, PUT, DELETE]
31+
"""
32+
CoAP message response codes (RFC 7252 @ section-12.1.2)
33+
"""
34+
EMPTY_ACK = EMPTY_MESSAGE
35+
CONTENT_205 = 69
36+
NOT_FOUND_404 = 132
37+
NOT_ALLOWED_405 = 133
38+
NOT_IMPLEMENTED_501 = 161
39+
"""
40+
CoAP content type (RFC 7252 @ section-12.3)
41+
"""
42+
CF_TEXT_PLAIN = b"\x00"
43+
CF_APP_LINK_FORMAT = b"\x28"
44+
CF_APP_XML = b"\x29"
45+
CF_APP_OCTET_STREAM = b"\x2A"
46+
CF_APP_EXI = b"\x2F"
47+
CF_APP_JSON = b"\x32"
48+
"""
49+
CoAP options (RFC 7252 @ section-5.10)
50+
"""
51+
PAYMARK = b"\xff"
52+
URI_PATH = 11
53+
CONTENT_FORMAT = 12
54+
"""
55+
CoAP message type
56+
"""
57+
CON = 0
58+
NON = 1
59+
ACK = 2
60+
RST = 3
61+
2262
coap_codes = {
23-
0: "Empty",
63+
EMPTY_MESSAGE: "Empty",
2464
# Request codes
25-
1: "GET",
26-
2: "POST",
27-
3: "PUT",
28-
4: "DELETE",
65+
GET: "GET",
66+
POST: "POST",
67+
PUT: "PUT",
68+
DELETE: "DELETE",
2969
# Response codes
3070
65: "2.01 Created",
3171
66: "2.02 Deleted",
3272
67: "2.03 Valid",
3373
68: "2.04 Changed",
34-
69: "2.05 Content",
74+
CONTENT_205: "2.05 Content",
3575
128: "4.00 Bad Request",
3676
129: "4.01 Unauthorized",
3777
130: "4.02 Bad Option",
3878
131: "4.03 Forbidden",
39-
132: "4.04 Not Found",
40-
133: "4.05 Method Not Allowed",
79+
NOT_FOUND_404: "4.04 Not Found",
80+
NOT_ALLOWED_405: "4.05 Method Not Allowed",
4181
134: "4.06 Not Acceptable",
4282
140: "4.12 Precondition Failed",
4383
141: "4.13 Request Entity Too Large",
4484
143: "4.15 Unsupported Content-Format",
4585
160: "5.00 Internal Server Error",
46-
161: "5.01 Not Implemented",
86+
NOT_IMPLEMENTED_501: "5.01 Not Implemented",
4787
162: "5.02 Bad Gateway",
4888
163: "5.03 Service Unavailable",
4989
164: "5.04 Gateway Timeout",
@@ -120,7 +160,8 @@ def _get_opt_val_size(pkt):
120160
class _CoAPOpt(Packet):
121161
fields_desc = [BitField("delta", 0, 4),
122162
BitField("len", 0, 4),
123-
StrLenField("delta_ext", "", length_from=_get_delta_ext_size), # noqa: E501
163+
StrLenField("delta_ext", "", length_from=_get_delta_ext_size),
164+
# noqa: E501
124165
StrLenField("len_ext", "", length_from=_get_len_ext_size),
125166
StrLenField("opt_val", "", length_from=_get_opt_val_size)]
126167

@@ -149,7 +190,8 @@ class _CoAPOptsField(StrField):
149190
islist = 1
150191

151192
def i2h(self, pkt, x):
152-
return [(coap_options[0][o[0]], o[1]) if o[0] in coap_options[0] else o for o in x] # noqa: E501
193+
return [(coap_options[0][o[0]], o[1]) if o[0] in coap_options[0] else o for o in
194+
x] # noqa: E501
153195

154196
# consume only the coap layer from the wire string
155197
def getfield(self, pkt, s):
@@ -214,7 +256,8 @@ class CoAP(Packet):
214256
name = "CoAP"
215257

216258
fields_desc = [BitField("ver", 1, 2),
217-
BitEnumField("type", 0, 2, {0: "CON", 1: "NON", 2: "ACK", 3: "RST"}), # noqa: E501
259+
BitEnumField("type", 0, 2, {0: "CON", 1: "NON", 2: "ACK", 3: "RST"}),
260+
# noqa: E501
218261
BitFieldLenField("tkl", None, 4, length_of='token'),
219262
ByteEnumField("code", 0, coap_codes),
220263
ShortField("msg_id", 0),

scapy/contrib/coap_socket.py

+24-55
Original file line numberDiff line numberDiff line change
@@ -23,55 +23,16 @@
2323

2424
from scapy.error import Scapy_Exception
2525
from scapy.packet import Packet
26-
from scapy.contrib.coap import CoAP, coap_options, coap_codes
26+
from scapy.contrib.coap import CoAP, coap_options, coap_codes, EMPTY_MESSAGE, GET, \
27+
POST, PUT, DELETE, COAP_REQ_CODES, CONTENT_205, NOT_FOUND_404, NOT_ALLOWED_405, \
28+
CF_TEXT_PLAIN, CF_APP_LINK_FORMAT, PAYMARK, URI_PATH, CONTENT_FORMAT, CON, NON, ACK
2729
from scapy.contrib.isotp.isotp_soft_socket import TimeoutScheduler
2830
from scapy.data import MTU
2931
from scapy.utils import EDecimal
3032
from scapy.automaton import ObjectPipe, select_objects
3133

3234
from scapy.supersocket import SuperSocket, SimpleSocket
3335

34-
"""
35-
CoAP message request codes (RFC 7252 @ section-5.8.1)
36-
"""
37-
EMPTY_MESSAGE = 0
38-
GET = 1
39-
POST = 2
40-
PUT = 3
41-
DELETE = 4
42-
COAP_REQ_CODES = [GET, POST, PUT, DELETE]
43-
"""
44-
CoAP message response codes (RFC 7252 @ section-12.1.2)
45-
Also, from scapy.contrib.coap.coap_codes
46-
"""
47-
EMPTY_ACK = EMPTY_MESSAGE
48-
CONTENT_205 = 69
49-
NOT_FOUND_404 = 132
50-
NOT_ALLOWED_405 = 133
51-
NOT_IMPLEMENTED_501 = 161
52-
"""
53-
CoAP content type (RFC 7252 @ section-12.3)
54-
"""
55-
CF_TEXT_PLAIN = b"\x00"
56-
CF_APP_LINK_FORMAT = b"\x28"
57-
CF_APP_XML = b"\x29"
58-
CF_APP_OCTET_STREAM = b"\x2A"
59-
CF_APP_EXI = b"\x2F"
60-
CF_APP_JSON = b"\x32"
61-
"""
62-
CoAP options (RFC 7252 @ section-5.10)
63-
"""
64-
PAYMARK = b"\xff"
65-
URI_PATH = 11
66-
CONTENT_FORMAT = 12
67-
"""
68-
CoAP message type
69-
"""
70-
CON = 0
71-
NON = 1
72-
ACK = 2
73-
RST = 3
74-
7536
log_coap_sock = logging.getLogger("scapy.contrib.coap_socket")
7637

7738

@@ -85,7 +46,7 @@ class CoAPSocket(SuperSocket):
8546
>>> with CoAPSocket("127.0.0.1", 1234) as coap_client:
8647
>>> req = CoAPSocket.make_coap_req_packet(
8748
>>> method=GET, uri="endpoint-uri", payload=b"")
88-
>>> coap_client.send("127.0.0.1", 5683, req)
49+
>>> coap_client.send(IP(dst="192.168.1.1") / UDP(dport=1234) / req)
8950
>>> # Careful, this will block until the coap_client receives something
9051
>>> res = coap_client.recv()
9152
@@ -172,12 +133,24 @@ def recv(self, x=MTU, **kwargs):
172133
def close(self):
173134
# type: () -> None
174135
if not self.closed:
175-
self.impl.close()
176136
self.closed = True
137+
self.impl.close()
177138

178-
def send(self, ip, port, x):
179-
# type: (str, int, CoAP) -> None
180-
self.impl.send(ip, port, x)
139+
def send(self, x):
140+
# type: (Packet) -> int
141+
"""
142+
Send the packet using this socket.
143+
Should be a CoAP packet with IP and UDP data.
144+
145+
Example:
146+
>>> IP(dst="192.168.1.1") / UDP(dport=1234) / CoAP()
147+
>>> IP(dst="192.168.1.1") / UDP(dport=1234) / CoAPSocket.make_coap_req_packet()
148+
149+
:param x: Concatenated packet with IP / UDP / CoAP
150+
:return: The length of x, which is the amount of bytes sent
151+
"""
152+
self.impl.send(x.dst, x.dport, x[CoAP])
153+
return len(x)
181154

182155
@staticmethod
183156
def make_coap_req_packet(method=GET, uri="", options=None, payload=b""):
@@ -630,8 +603,10 @@ def _on_pkt_recv(self, pkt, sa_ll):
630603
else:
631604
self._handle_rcv_request(pkt, sa_ll)
632605
else:
633-
# Response, check pending requests
606+
# Response, check pending requests and process internally
634607
self._handle_request_response(pkt, sa_ll)
608+
# Then give the response to the user.
609+
self.rx_queue.send((pkt.build(), pkt.time))
635610

636611
def _post(self):
637612
# type: () -> dict
@@ -805,8 +780,6 @@ def _handle_request_response(self, pkt, sa_ll):
805780
index[0], index[1],
806781
coap_codes[pkt.code])
807782
del self.pending_requests[index]
808-
# Piggybacked message, give it to the user
809-
self.rx_queue.send((pkt.build(), pkt.time))
810783
elif pkt.type == ACK and pkt.code == EMPTY_MESSAGE:
811784
log_coap_sock.debug(
812785
"Server sent an empty ack, request will be fulfilled later: "
@@ -824,14 +797,10 @@ def _handle_request_response(self, pkt, sa_ll):
824797
response = CoAPSocketImpl.empty_ack_params()
825798
response["msg_id"] = pkt.msg_id
826799
self._sock_send(sa_ll, CoAP(**response))
827-
828-
# Give the packet to the user
829-
self.rx_queue.send((pkt.build(), pkt.time))
830800
else:
831-
log_coap_sock.info("Not handled message, giving to user: "
801+
log_coap_sock.info("Not handled message: "
832802
"type=%s; code=%s;",
833803
pkt.type, coap_codes[pkt.code])
834-
self.rx_queue.send((pkt.build(), pkt.time))
835804

836805
def _sock_send(self, address, pl):
837806
# type: (tuple[str, int], Packet) -> None

test/contrib/coap_socket.uts

+7-7
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ lst_resources = [DummyResource("/dummy"), DelayedResource("delayed")]
4848

4949
with CoAPSocket("127.0.0.1", 5683, lst_resources=lst_resources) as coap_server, CoAPSocket("127.0.0.1", 5684) as coap_client:
5050
req = CoAPSocket.make_coap_req_packet(uri=".well-known/core", payload=b"")
51-
coap_client.send("127.0.0.1", 5683, req)
51+
coap_client.send(IP(dst="127.0.0.1")/UDP(dport=5683)/req)
5252
res = coap_client.recv()
5353
assert res.payload.load == b'</dummy>;ct=0,</delayed>;ct=0'
5454
assert res.type == ACK
@@ -60,7 +60,7 @@ with CoAPSocket("127.0.0.1", 5683, lst_resources=lst_resources) as coap_server,
6060

6161
with CoAPSocket("127.0.0.1", 5683, lst_resources=lst_resources) as coap_server, CoAPSocket("127.0.0.1", 5684) as coap_client:
6262
req = CoAPSocket.make_coap_req_packet(uri="dummy", payload=b"")
63-
coap_client.send("127.0.0.1", 5683, req)
63+
coap_client.send(IP(dst="127.0.0.1")/UDP(dport=5683)/req)
6464
res = coap_client.recv()
6565
assert res.payload.load == responses[0]
6666
assert res.type == ACK
@@ -72,7 +72,7 @@ with CoAPSocket("127.0.0.1", 5683, lst_resources=lst_resources) as coap_server,
7272

7373
with CoAPSocket("127.0.0.1", 5683, lst_resources=lst_resources) as coap_server, CoAPSocket("127.0.0.1", 5684) as coap_client:
7474
req = CoAPSocket.make_coap_req_packet(uri="/dummy", payload=b"")
75-
coap_client.send("127.0.0.1", 5683, req)
75+
coap_client.send(IP(dst="127.0.0.1")/UDP(dport=5683)/req)
7676
res = coap_client.recv()
7777
assert res.payload.load == responses[0]
7878
assert res.type == ACK
@@ -84,7 +84,7 @@ with CoAPSocket("127.0.0.1", 5683, lst_resources=lst_resources) as coap_server,
8484

8585
with CoAPSocket("127.0.0.1", 5683, lst_resources=lst_resources) as coap_server, CoAPSocket("127.0.0.1", 5684) as coap_client:
8686
req = CoAPSocket.make_coap_req_packet(uri="dummy/", payload=b"")
87-
coap_client.send("127.0.0.1", 5683, req)
87+
coap_client.send(IP(dst="127.0.0.1")/UDP(dport=5683)/req)
8888
res = coap_client.recv()
8989
assert res.type == ACK
9090
assert res.code == NOT_FOUND_404
@@ -95,7 +95,7 @@ with CoAPSocket("127.0.0.1", 5683, lst_resources=lst_resources) as coap_server,
9595

9696
with CoAPSocket("127.0.0.1", 5683, lst_resources=lst_resources) as coap_server, CoAPSocket("127.0.0.1", 5684) as coap_client:
9797
req = CoAPSocket.make_coap_req_packet(method=PUT, uri="dummy", payload=b"a payload")
98-
coap_client.send("127.0.0.1", 5683, req)
98+
coap_client.send(IP(dst="127.0.0.1")/UDP(dport=5683)/req)
9999
res = coap_client.recv()
100100
assert res.type == ACK
101101
assert res.code == NOT_ALLOWED_405
@@ -107,7 +107,7 @@ with CoAPSocket("127.0.0.1", 5683, lst_resources=lst_resources) as coap_server,
107107
with CoAPSocket("127.0.0.1", 5683, lst_resources=lst_resources) as coap_server, CoAPSocket("127.0.0.1", 5684) as coap_client:
108108
coap_server.impl._enable_debug = True
109109
req = CoAPSocket.make_coap_req_packet(uri="/dummy", payload=b"")
110-
coap_client.send("127.0.0.1", 5683, req)
110+
coap_client.send(IP(dst="127.0.0.1")/UDP(dport=5683)/req)
111111
res = coap_client.recv()
112112
assert res.payload.load == responses[0]
113113
assert res.type == ACK
@@ -119,7 +119,7 @@ with CoAPSocket("127.0.0.1", 5683, lst_resources=lst_resources) as coap_server,
119119

120120
with CoAPSocket("127.0.0.1", 5683, lst_resources=lst_resources) as coap_server, CoAPSocket("127.0.0.1", 5684) as coap_client:
121121
req = CoAPSocket.make_coap_req_packet(uri="/delayed", payload=b"")
122-
coap_client.send("127.0.0.1", 5683, req)
122+
coap_client.send(IP(dst="127.0.0.1")/UDP(dport=5683)/req)
123123
res = coap_client.recv()
124124
assert res.payload.load == responses[1]
125125
assert res.type == CON

0 commit comments

Comments
 (0)