diff options
author | Dan Williams <dcbw@redhat.com> | 2010-10-14 10:22:59 -0500 |
---|---|---|
committer | Dan Williams <dcbw@redhat.com> | 2010-10-14 10:22:59 -0500 |
commit | dd8a061d28ecaf286bfba3bdbd2a495c45cfb4a4 (patch) | |
tree | 85a4ecd53f7eb02d227d98d906e9d927febc0d04 | |
parent | 8a4f621245c7b5a1a06471260f5f75422e9098cc (diff) |
serial: make QCDM frame parsing more robust
Ensure that valid HDLC frames that are not valid QCDM frames
are correctly rejected, and that their data is correctly
discarded.
The core bug was that Sierra CnS frames have leading and trailing
HDLC frame terminator bytes (0x7E), and the code was incorrectly
treating the leading terminator as the end of a frame, not the
beginning. Thus it would consider the outstanding serial request
finished without actually parsing the response packet.
Now, we make sure we don't tell the serial receive code that
we have a full QCDM frame until we actually do have one, which is
at least 3 bytes + 0x7E.
-rw-r--r-- | src/mm-qcdm-serial-port.c | 107 |
1 files changed, 66 insertions, 41 deletions
diff --git a/src/mm-qcdm-serial-port.c b/src/mm-qcdm-serial-port.c index 72463fbf..7f4302c5 100644 --- a/src/mm-qcdm-serial-port.c +++ b/src/mm-qcdm-serial-port.c @@ -37,22 +37,38 @@ typedef struct { /*****************************************************************************/ static gboolean -parse_response (MMSerialPort *port, GByteArray *response, GError **error) +find_qcdm_start (GByteArray *response, gsize *start) { - int i; + int i, last = -1; - /* Look for the QCDM packet termination character; if we found it, treat - * the buffer as a qcdm command. + /* Look for 3 bytes and a QCDM frame marker, ie enough data for a valid + * frame. There will usually be three cases here; (1) a QCDM frame + * starting with data and terminated by 0x7E, and (2) a QCDM frame starting + * with 0x7E and ending with 0x7E, and (3) a non-QCDM frame that still + * uses HDLC framing (like Sierra CnS) that starts and ends with 0x7E. */ for (i = 0; i < response->len; i++) { - if (response->data[i] == 0x7E) - return TRUE; + if (response->data[i] == 0x7E) { + if (i > last + 3) { + /* Got a full QCDM frame; 3 non-0x7E bytes and a terminator */ + if (start) + *start = last + 1; + return TRUE; + } + + /* Save position of the last QCDM frame marker */ + last = i; + } } - - /* Otherwise, need more data from the device */ return FALSE; } +static gboolean +parse_response (MMSerialPort *port, GByteArray *response, GError **error) +{ + return find_qcdm_start (response, NULL); +} + static gsize handle_response (MMSerialPort *port, GByteArray *response, @@ -64,41 +80,49 @@ handle_response (MMSerialPort *port, GByteArray *unescaped = NULL; GError *dm_error = NULL; gsize used = 0; + gsize start = 0; + gboolean success = FALSE, more = FALSE; + gsize unescaped_len = 0; + + if (error) + goto callback; + + /* Get the offset into the buffer of where the QCDM frame starts */ + if (!find_qcdm_start (response, &start)) { + g_set_error_literal (&dm_error, + MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Failed to parse QCDM packet."); + /* Discard the unparsable data */ + used = response->len; + goto callback; + } - /* Ignore empty frames */ - if (response->len > 0 && response->data[0] == 0x7E) - return 1; - - if (!error) { - gboolean more = FALSE, success; - gsize unescaped_len = 0; - - /* FIXME: don't munge around with byte array internals */ - unescaped = g_byte_array_sized_new (1024); - success = dm_decapsulate_buffer ((const char *) response->data, - response->len, - (char *) unescaped->data, - 1024, - &unescaped_len, - &used, - &more); - if (!success) { - g_set_error_literal (&dm_error, - MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, - "Failed to unescape QCDM packet."); - g_byte_array_free (unescaped, TRUE); - unescaped = NULL; - } else if (more) { - /* Need more data; we shouldn't have gotten here since the parse - * function checks for the end-of-frame marker, but whatever. - */ - return 0; - } else { - /* Successfully decapsulated the DM command */ - unescaped->len = (guint) unescaped_len; - } + /* FIXME: don't munge around with byte array internals */ + unescaped = g_byte_array_sized_new (1024); + success = dm_decapsulate_buffer ((const char *) (response->data + start), + response->len - start, + (char *) unescaped->data, + 1024, + &unescaped_len, + &used, + &more); + if (!success) { + g_set_error_literal (&dm_error, + MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Failed to unescape QCDM packet."); + g_byte_array_free (unescaped, TRUE); + unescaped = NULL; + } else if (more) { + /* Need more data; we shouldn't have gotten here since the parse + * function checks for the end-of-frame marker, but whatever. + */ + return 0; + } else { + /* Successfully decapsulated the DM command */ + unescaped->len = (guint) unescaped_len; } +callback: response_callback (MM_QCDM_SERIAL_PORT (port), unescaped, dm_error ? dm_error : error, @@ -106,8 +130,9 @@ handle_response (MMSerialPort *port, if (unescaped) g_byte_array_free (unescaped, TRUE); + g_clear_error (&dm_error); - return used; + return start + used; } /*****************************************************************************/ |