mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	Fix handling of timestamps in Teleinfo component. (#2392)
* teleinfo: avoid a buffer overflow.
When reading tag or values, data is written to the buffer even if the
size if bigger than the buffer. Add a new 'max_len' argument to
get_field() to avoid this error.
Signed-off-by: 0hax <0hax@protonmail.com>
* teleinfo: read extra timestamp field for some tags.
Some tags has an extra timestamp field that need to be read before the
actual data.
The code is inspired by Jpsy work:
29339c14f9
Signed-off-by: 0hax <0hax@protonmail.com>
* teleinfo: increase MAX_BUF_SIZE to suffice for 3-phase Linky in Standard mode.
* teleinfo: handle DATE tag correctly.
The DATE tag is special due its format and need to be handled
separately.
Fix from DrCoolzic.
Signed-off-by: 0hax <0hax@protonmail.com>
Co-authored-by: Jörg Wagner <jwagner@digilog.de>
			
			
This commit is contained in:
		| @@ -7,7 +7,7 @@ namespace teleinfo { | ||||
| static const char *const TAG = "teleinfo"; | ||||
|  | ||||
| /* Helpers */ | ||||
| static int get_field(char *dest, char *buf_start, char *buf_end, int sep) { | ||||
| static int get_field(char *dest, char *buf_start, char *buf_end, int sep, int max_len) { | ||||
|   char *field_end; | ||||
|   int len; | ||||
|  | ||||
| @@ -15,6 +15,8 @@ static int get_field(char *dest, char *buf_start, char *buf_end, int sep) { | ||||
|   if (!field_end) | ||||
|     return 0; | ||||
|   len = field_end - buf_start; | ||||
|   if (len >= max_len) | ||||
|     return len; | ||||
|   strncpy(dest, buf_start, len); | ||||
|   dest[len] = '\0'; | ||||
|  | ||||
| @@ -106,9 +108,22 @@ void TeleInfo::loop() { | ||||
|        * 0xa | Tag | 0x9 | Data | 0x9 | CRC | 0xd | ||||
|        *     ^^^^^^^^^^^^^^^^^^^^^^^^^ | ||||
|        * Checksum is computed on the above in standard mode. | ||||
|        * | ||||
|        * Note that some Tags may have a timestamp in Standard mode. In this case | ||||
|        * the group would looks like this: | ||||
|        * 0xa | Tag | 0x9 | Timestamp | 0x9 | Data | 0x9 | CRC | 0xd | ||||
|        * | ||||
|        * The DATE tag is a special case. The group looks like this | ||||
|        * 0xa | Tag | 0x9 | Timestamp | 0x9 | 0x9 | CRC | 0xd | ||||
|        * | ||||
|        */ | ||||
|       while ((buf_finger = static_cast<char *>(memchr(buf_finger, (int) 0xa, buf_index_ - 1))) && | ||||
|              ((buf_finger - buf_) < buf_index_)) { | ||||
|         /* | ||||
|          * Make sure timesamp is nullified between each tag as some tags don't | ||||
|          * have a timestamp | ||||
|          */ | ||||
|         timestamp_[0] = '\0'; | ||||
|         /* Point to the first char of the group after 0xa */ | ||||
|         buf_finger += 1; | ||||
|  | ||||
| @@ -123,7 +138,7 @@ void TeleInfo::loop() { | ||||
|           continue; | ||||
|  | ||||
|         /* Get tag */ | ||||
|         field_len = get_field(tag_, buf_finger, grp_end, separator_); | ||||
|         field_len = get_field(tag_, buf_finger, grp_end, separator_, MAX_TAG_SIZE); | ||||
|         if (!field_len || field_len >= MAX_TAG_SIZE) { | ||||
|           ESP_LOGE(TAG, "Invalid tag."); | ||||
|           break; | ||||
| @@ -132,8 +147,22 @@ void TeleInfo::loop() { | ||||
|         /* Advance buf_finger to after the tag and the separator. */ | ||||
|         buf_finger += field_len + 1; | ||||
|  | ||||
|         /* Get value (after next separator) */ | ||||
|         field_len = get_field(val_, buf_finger, grp_end, separator_); | ||||
|         /* | ||||
|          * If there is two separators and the tag is not equal to "DATE", | ||||
|          * it means there is a timestamp to read first. | ||||
|          */ | ||||
|         if (std::count(buf_finger, grp_end, separator_) == 2 && strcmp(tag_, "DATE") != 0) { | ||||
|           field_len = get_field(timestamp_, buf_finger, grp_end, separator_, MAX_TIMESTAMP_SIZE); | ||||
|           if (!field_len || field_len >= MAX_TIMESTAMP_SIZE) { | ||||
|             ESP_LOGE(TAG, "Invalid Timestamp"); | ||||
|             break; | ||||
|           } | ||||
|  | ||||
|           /* Advance buf_finger to after the first data and the separator. */ | ||||
|           buf_finger += field_len + 1; | ||||
|         } | ||||
|  | ||||
|         field_len = get_field(val_, buf_finger, grp_end, separator_, MAX_VAL_SIZE); | ||||
|         if (!field_len || field_len >= MAX_VAL_SIZE) { | ||||
|           ESP_LOGE(TAG, "Invalid Value"); | ||||
|           break; | ||||
|   | ||||
| @@ -11,7 +11,8 @@ namespace teleinfo { | ||||
|  */ | ||||
| static const uint8_t MAX_TAG_SIZE = 64; | ||||
| static const uint16_t MAX_VAL_SIZE = 256; | ||||
| static const uint16_t MAX_BUF_SIZE = 1024; | ||||
| static const uint16_t MAX_BUF_SIZE = 2048; | ||||
| static const uint16_t MAX_TIMESTAMP_SIZE = 14; | ||||
|  | ||||
| class TeleInfoListener { | ||||
|  public: | ||||
| @@ -36,6 +37,7 @@ class TeleInfo : public PollingComponent, public uart::UARTDevice { | ||||
|   uint32_t buf_index_{0}; | ||||
|   char tag_[MAX_TAG_SIZE]; | ||||
|   char val_[MAX_VAL_SIZE]; | ||||
|   char timestamp_[MAX_TIMESTAMP_SIZE]; | ||||
|   enum State { | ||||
|     OFF, | ||||
|     ON, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user