// Parses trailing NUL character. Returns true iff progress was made. bool frameparser_parse_end(frameparser *fp, buffer *b) { // Valid input in this state is simply a single NUL character log_printf(LOG_LEVEL_DEBUG, "frameparser_parse_end\n"); // If there is already a parsed frame that hasn't been picked up yet, we // don't want to overwrite it, so refrain from finishing this frame. if (fp->fin_frame) return false; // No progress // Check for the NUL uint8_t byte = buffer_get_byte(b, 0); if (byte != '\x00') { frameparser_set_error(fp, "Expected trailing NUL at end of frame"); return false; } // Consume it buffer_consume(b, 1); // All done with the current frame fp->fin_frame = fp->cur_frame; fp->cur_frame = NULL; fp->state = FP_STATE_IDLE; return true; }
// Parses a frame's command. Returns true iff progress was made. bool frameparser_parse_command(frameparser *fp, buffer *b) { log_printf(LOG_LEVEL_DEBUG, "frameparser_parse_command\n"); // Valid input in this state is a string followed by CR/LF or LF, which matches a known frame command // Try to find LF line terminator int lfpos = buffer_find_byte(b, '\x0A'); if (lfpos < 0) // No LF yet? { if (buffer_get_length(b) > LIMIT_FRAME_CMD_LINE_LEN) frameparser_set_error(fp, "Line length limit exceeded waiting for command"); return false; // No progress } else if (lfpos == 0) abort(); // Should never happen in this state // Figure out number of bytes in command string int len = lfpos; if (buffer_get_byte(b, len - 1) == '\x0D') len--; // Extract the command into a new bytestring bytestring *bs = bytestring_new(len); buffer_append_bytestring(b, bs, 0, len); bytestring_dump(bs); // Consume the current line buffer_consume(b, lfpos + 1); // Find the command code for this command frame_command cmd = frame_command_code(bytestring_get_bytes(bs), bytestring_get_length(bs)); // Clean up the bytestring bytestring_free(bs); // Make sure command is valid if (cmd == CMD_NONE) { frameparser_set_error(fp, "Unknown command"); return false; } // Set up a new frame structure to hold parsed data if (!fp->cur_frame) fp->cur_frame = frame_new(); // Store parsed data frame_set_command(fp->cur_frame, cmd); // Got a valid command, so headers should be next fp->state = FP_STATE_HEADER; return true; }
// Parses keepalive linefeeds. Returns true iff progress was made. bool frameparser_parse_idle(frameparser *fp, buffer *b) { log_printf(LOG_LEVEL_DEBUG, "frameparser_parse_idle\n"); // Valid input in this state is a CR/LF pair, or bare LF uint8_t byte = buffer_get_byte(b, 0); if (byte == '\x0D') { // Should be CR/LF pair. Make sure we have at least two bytes size_t len = buffer_get_length(b); if (len < 2) return false; // No progress // Make sure second byte is LF byte = buffer_get_byte(b, 1); if (byte != '\x0A') { frameparser_set_error(fp, "Expected 0x0A after 0x0D, got 0x%02X", byte); return false; // No progress } // Eat the CR/LF pair buffer_consume(b, 2); return true; } else if (byte == '\x0A') { // Bare LF buffer_consume(b, 1); return true; } // Something else? Must be the start of a frame fp->state = FP_STATE_COMMAND; return true; }
packet_t* packet_decode(buffer_t* buffer) { int error = buffer_ok; uint16_t length; uint8_t id; buffer->position = 0; error |= buffer_get_short(buffer, &length); error |= buffer_get_byte(buffer, &id); error |= buffer_available(buffer) == length; if (error || length > 5000 || length == 0) return NULL; uint8_t payload[length]; memcpy(payload, buffer->payload + buffer->position, length); packet_t* packet = safe_alloc(sizeof(packet_t)); packet->id = id; packet->buffer = buffer_wrap(payload, length); packet->buffer->position = 0; return packet; }
// Parses a header line. Returns true iff progress was made. bool frameparser_parse_header(frameparser *fp, buffer *b) { log_printf(LOG_LEVEL_DEBUG, "frameparser_parse_header\n"); // Valid input in this state is either: // 1. CR/LF or LF alone, denoting the end of headers // 2. A key name, followed by a colon, followed by a value name, terminated with CR/LF or LF // Try to find LF line terminator int lfpos = buffer_find_byte(b, '\x0A'); if (lfpos < 0) // No LF yet? { if (buffer_get_length(b) > LIMIT_FRAME_HEADER_LINE_LEN) frameparser_set_error(fp, "Line length limit exceeded waiting for header"); return false; // No progress } else if (lfpos == 0) // LF alone { buffer_consume(b, 1); frameparser_parse_headers_complete(fp); return true; } else if ((lfpos == 1) && (buffer_get_byte(b, 0) == '\x0D')) // CR/LF alone { buffer_consume(b, 2); frameparser_parse_headers_complete(fp); return true; } // Figure out number of bytes in the line int len = lfpos; if (buffer_get_byte(b, len - 1) == '\x0D') len--; // Find the colon delimiter int colonpos = buffer_find_byte_within(b, ':', 0, len); if (colonpos < 0) // No colon? { frameparser_set_error(fp, "Expected colon delimiter on header line"); return false; } else if (colonpos == 0) // Starts with colon? { frameparser_set_error(fp, "Header name has zero length"); return false; } // Extract the key bytestring *key = bytestring_new(colonpos); buffer_append_bytestring(b, key, 0, colonpos); // Extract the value bytestring *val = bytestring_new(len - colonpos - 1); buffer_append_bytestring(b, val, colonpos + 1, len - colonpos - 1); // Consume the current line buffer_consume(b, lfpos + 1); // Unescape the key and value if needed frame_command cmd = frame_get_command(fp->cur_frame); if ((cmd != CMD_CONNECT) && (cmd != CMD_CONNECTED)) { key = unescape_header_bytestring(key); val = unescape_header_bytestring(val); } bytestring_dump(key); bytestring_dump(val); headerbundle *hb = frame_get_headerbundle(fp->cur_frame); headerbundle_append_header(hb, key, val); // The bundle takes ownership of key and val return true; }