Демонстрационная прошивка отладочного комплекта МТС NB-IoT
coap_essentials.c
См. документацию.
1 #include "coap_essentials.h"
2 
9 static uint8_t URI_copy[MAX_URI_LENGTH];
10 static uint8_t* URI_ptrs[MAX_URI_ENTRIES];
11 
13 {
14  /* Notice: header is assumed to be in a network byte order, i.e. as received from UDP layer. */
15  out->version = (hdr[0] & 0xC0) >> 6;
16  out->type = (hdr[0] & 0x30) >> 4;
17  out->token_length = (hdr[0] & 0x0F) >> 0;
18  out->code_class = (hdr[1] & 0xE0) >> 5;
19  out->code_detail = (hdr[1] & 0x1F) >> 0;
20  out->message_ID = (((uint16_t)hdr[2]) << 8) | (hdr[3]);
21 }
22 
23 uint16_t CoAP_parse_option(uint8_t *buf,CoAP_option_info_t *out)
24 {
25  uint16_t buf_index;
26  uint16_t option_delta,option_length;
27 
28  buf_index = 0;
29 
30  option_delta = buf[buf_index] >> 4;
31  option_length = buf[buf_index] & 0x0F;
32 
33  buf_index++;
34 
35  switch (option_delta)
36  {
37  case COAP_OPTION_DELTA_EXTRA1B:
38  /* One extra byte follows the main header, which contains option delta minus 13 */
39  option_delta = buf[buf_index] + 13;
40  buf_index++;
41  break;
42  case COAP_OPTION_DELTA_EXTRA2B:
43  /* Two extra bytes follow the main header (in a network order), which contain option delta minus 269 */
44  option_delta = ((((uint16_t)(buf[buf_index]))<<8) | (buf[buf_index+1])) + 269;
45  buf_index+=2;
46  break;
47  case COAP_OPTION_DELTA_DATA_MKR:
48  /* This is probably a data marker */
49  if (buf[0]==0xFF)
50  {
51  /* Data marker has a length of one byte */
52  return 1;
53  }
54  else
55  {
56  /* This is a message format violation */
57  return 0xFFFF;
58  }
59  break;
60  default:
61  /* Nothing needs to be done, option delta is specified directly */
62  break;
63  }
64 
65  switch (option_length)
66  {
67  case COAP_OPTION_LENGTH_EXTRA1B:
68  /* One extra byte precedes data, which contains data length value minus 13 */
69  option_length = buf[buf_index] + 13;
70  buf_index++;
71  break;
72  case COAP_OPTION_LENGTH_EXTRA2B:
73  /* Two extra bytes precede data, which contain data length value minus 269 */
74  option_length = ((((uint16_t)(buf[buf_index]))<<8) | (buf[buf_index+1])) + 269;
75  buf_index+=2;
76  break;
77  case COAP_OPTION_LENGTH_DATA_MKR:
78  if (buf[0]==0xFF)
79  {
80  return 1;
81  }
82  else
83  {
84  return 0xFFFF;
85  }
86  break;
87  default:
88  /* Nothing needs to be done, option content length was specified directly */
89  break;
90  }
91 
92  out->option_id = out->option_id + option_delta;
93  out->data_length = option_length;
94  out->data_ptr = &(buf[buf_index]);
95 
96  return (buf_index + option_length);
97 }
98 
99 CoAP_parsing_error_t CoAP_parse_message(uint8_t *buf,uint16_t buf_length,uint8_t **data_start)
100 {
101  CoAP_header_info_t hdr_data;
102  CoAP_option_info_t opt_data;
103  uint16_t buf_ptr;
104  uint16_t safety_counter;
105  uint16_t total_option_length;
106 
107  CoAP_parse_message_header(buf,&hdr_data);
108 
109  if (hdr_data.version!=COAP_MSG_VERSION)
110  {
111  //Unsupported (and probably non-existent) protocol version
112  return ERR_WRONG_PROTOCOL_VER;
113  }
114 
115  if ((hdr_data.type!=COAP_TYPE_CONFIRMABLE) &&
116  (hdr_data.type!=COAP_TYPE_NON_CONFIRMABLE) &&
117  (hdr_data.type!=COAP_TYPE_ACK) &&
118  (hdr_data.type!=COAP_TYPE_RESET))
119  {
120  //Unknown message type
121  return ERR_UNKNOWN_MSG_TYPE;
122  }
123 
124  if (hdr_data.token_length>8)
125  {
126  //Unallowed token length
127  return ERR_WRONG_TKL;
128  }
129 
130  //Find out where the options start
131  buf_ptr = COAP_MSG_HDR_LENGTH + hdr_data.token_length;
132 
133  if ((buf_ptr<buf_length) && (buf[buf_ptr]!=0xFF)) /* This means that there is at least one option after the header/token */
134  {
135  //It is essential to initialize option ID to zero, for the options are encoded in incremental form
136  opt_data.option_id=0;
137  safety_counter=0;
138  while ((buf[buf_ptr]!=0xFF) && (safety_counter<MAX_PARSED_OPTIONS) && (buf_ptr<buf_length))
139  {
140  total_option_length = CoAP_parse_option(&(buf[buf_ptr]),&opt_data);
141 
142  if (total_option_length == 0xFFFF)
143  {
144  return ERR_WRONG_OPTION;
145  }
146 
147  buf_ptr += total_option_length;
148  safety_counter++;
149  }
150 
151  if (safety_counter>=MAX_PARSED_OPTIONS)
152  {
153  //Too much options
154  return ERR_TOO_MUCH_OPTIONS;
155  }
156  }
157 
158  if (data_start!=NULL)
159  {
160  if ((hdr_data.code_class == COAP_CLASS_RESP_SUCCESS) && (hdr_data.code_detail == 5))
161  {
162  /* Returning data pointer only makes sense when the code is 2.05, "content" */
163  (*data_start) = &(buf[buf_ptr+1]);
164  }
165  else
166  {
167  (*data_start) = NULL;
168  }
169  }
170 
171  return ERR_NONE;
172 }
173 
174 uint16_t CoAP_assemble_request_extended(uint8_t request_method,uint8_t is_observe_request,uint8_t *msg_buffer,uint16_t buffer_length,uint8_t *url_string,uint8_t *data,uint16_t data_length)
175 {
176  static uint8_t message_ID_counter=0;
177 
178  uint16_t uri_ptr_counter;
179  uint16_t uri_entry_length;
180  uint8_t option_id;
181  uint16_t buffer_ptr;
182  uint16_t k,j;
183 
184  /* Populating CoAP header; one-byte token assumed for a request. */
185  msg_buffer[0] = (COAP_MSG_VERSION<<6) | (COAP_TYPE_NON_CONFIRMABLE<<4) | 1;
186  msg_buffer[1] = (COAP_CLASS_REQUEST<<5) | (request_method & 0x1F);
187  msg_buffer[2] = (message_ID_counter & 0xFF00) >> 8;
188  msg_buffer[3] = (message_ID_counter & 0x00FF);
189 
190  /* Populating token field. */
191  msg_buffer[4] = message_ID_counter & 0x00FF;
192 
193  message_ID_counter++;
194 
195  if (url_string[0]!='/')
196  {
197  return 0;
198  }
199 
200  option_id = 0;
201  buffer_ptr = COAP_MSG_HDR_LENGTH + 1;
202 
203  if ((is_observe_request != 0) && (request_method == COAP_METHOD_GET))
204  {
205  /* Add an OBSERVE option */
206  option_id = COAP_OPTION_OBSERVE;
207  msg_buffer[buffer_ptr] = (option_id << 4) | 1;
208  buffer_ptr++;
209  msg_buffer[buffer_ptr] = 0; //OBSERVE = 0 (register)
210  buffer_ptr++;
211  }
212 
213  strncpy(URI_copy,url_string,MAX_URI_LENGTH);
214 
215  /* Splitting URI into entries */
216  k=0;
217  uri_ptr_counter=0;
218  while ((URI_copy[k]!=0) && (k<MAX_URI_LENGTH))
219  {
220  if (URI_copy[k]=='/')
221  {
222  if (uri_ptr_counter>=MAX_URI_ENTRIES)
223  {
224  return 0;
225  }
226 
227  URI_ptrs[uri_ptr_counter] = &(URI_copy[k+1]);
228  URI_copy[k]=0;
229  uri_ptr_counter++;
230  }
231  k++;
232  }
233 
234  for (k=0; k<uri_ptr_counter; k++)
235  {
236  if (buffer_ptr>=buffer_length)
237  {
238  return 0;
239  }
240 
241  /*
242  CoAP option identifiers are encoded in a weird delta-format,
243  so the first option has to have the actual value, and all subsequent similar
244  must have id of zero.
245  */
246  if (k==0)
247  {
248  option_id = COAP_OPTION_URI_PATH - option_id;
249  }
250  else
251  {
252  option_id = 0;
253  }
254 
255  uri_entry_length = strlen(URI_ptrs[k]);
256 
257  /*
258  CoAP weirdness does not end there. The option header is dynamic. If the length of a string
259  is lower than 13 bytes, one has to fit length value into header byte. Otherwise a byte or two have to
260  be appended, which contain string size minus 13 (and this prevents implementer from hardcoding
261  this byte to be present and simply changing its value; let's thank authors of RFC7252 for this).
262  */
263  if (uri_entry_length<13)
264  {
265  msg_buffer[buffer_ptr] = (option_id << 4) | uri_entry_length;
266  buffer_ptr++;
267  }
268  else
269  {
270  msg_buffer[buffer_ptr] = (option_id << 4) | 13;
271  buffer_ptr++;
272  msg_buffer[buffer_ptr] = uri_entry_length - 13;
273  buffer_ptr++;
274  }
275 
276  /*
277  And now copy the value of an option.
278  */
279  for (j=0; j<uri_entry_length; j++)
280  {
281  msg_buffer[buffer_ptr] = URI_ptrs[k][j];
282  buffer_ptr++;
283 
284  if (buffer_ptr>=buffer_length)
285  {
286  return 0;
287  }
288  }
289  }
290 
291  /*
292  Now we can copy the data, if present.
293  */
294  if ((data!=NULL) && (data_length!=0))
295  {
296  /*
297  Placing the data start marker.
298  Fun fact: there's no guarantee that the value of 0xFF can not be present among option data,
299  so, when parsing, you can not skip options directly to data by searching for 0xFF. Haha.
300  */
301  msg_buffer[buffer_ptr] = 0xFF;
302  buffer_ptr++;
303 
304  /*
305  Copy data.
306  */
307  for (j=0; j<data_length; j++)
308  {
309  msg_buffer[buffer_ptr] = data[j];
310  buffer_ptr++;
311 
312  if (buffer_ptr>=buffer_length)
313  {
314  return 0;
315  }
316  }
317  }
318 
319  return buffer_ptr;
320 }
CoAP_header_info_t
Definition: coap_essentials.h:102
MAX_URI_ENTRIES
#define MAX_URI_ENTRIES
Максимально допустимое количество сегментов в URI.
Definition: coap_essentials.h:97
CoAP_parsing_error_t
CoAP_parsing_error_t
Definition: coap_essentials.h:130
ERR_WRONG_PROTOCOL_VER
Definition: coap_essentials.h:132
ERR_WRONG_OPTION
Definition: coap_essentials.h:138
CoAP_header_info_t::code_class
uint8_t code_class
Definition: coap_essentials.h:111
ERR_TOO_MUCH_OPTIONS
Definition: coap_essentials.h:140
ERR_UNKNOWN_MSG_TYPE
Definition: coap_essentials.h:134
coap_essentials.h
ERR_WRONG_TKL
Definition: coap_essentials.h:136
CoAP_parse_message
CoAP_parsing_error_t CoAP_parse_message(uint8_t *buf, uint16_t buf_length, uint8_t **data_start)
Выполняет поиск данных в пакете CoAP.
Definition: coap_essentials.c:99
CoAP_header_info_t::message_ID
uint16_t message_ID
Definition: coap_essentials.h:115
MAX_PARSED_OPTIONS
#define MAX_PARSED_OPTIONS
Максимальное количество опций, которое будет анализироваться при разборе сообщения
Definition: coap_essentials.h:94
MAX_URI_LENGTH
#define MAX_URI_LENGTH
Предельная длина URI.
Definition: coap_essentials.h:100
ERR_NONE
Definition: coap_essentials.h:142
CoAP_option_info_t
Definition: coap_essentials.h:119
CoAP_assemble_request_extended
uint16_t CoAP_assemble_request_extended(uint8_t request_method, uint8_t is_observe_request, uint8_t *msg_buffer, uint16_t buffer_length, uint8_t *url_string, uint8_t *data, uint16_t data_length)
Формирует CoAP-сообщение с запросом к серверу
Definition: coap_essentials.c:174
CoAP_header_info_t::version
uint8_t version
Definition: coap_essentials.h:105
CoAP_header_info_t::token_length
uint8_t token_length
Definition: coap_essentials.h:109
CoAP_parse_message_header
void CoAP_parse_message_header(uint8_t *hdr, CoAP_header_info_t *out)
Выполняет разбор заголовка CoAP-сообщения с извлечением информации в структуру типа CoAP_header_info_...
Definition: coap_essentials.c:12
CoAP_header_info_t::type
uint8_t type
Definition: coap_essentials.h:107
CoAP_header_info_t::code_detail
uint8_t code_detail
Definition: coap_essentials.h:113