int multipart_process_chunk()

in apache2/msc_multipart.c [1059:1284]


int multipart_process_chunk(modsec_rec *msr, const char *buf,
    unsigned int size, char **error_msg)
{
    char *inptr = (char *)buf;
    unsigned int inleft = size;

    if (error_msg == NULL) return -1;
    *error_msg = NULL;

    if (size == 0) return 1;

    msr->mpd->seen_data = 1;

    if (msr->mpd->is_complete) {
        msr->mpd->flag_data_before = 1;

        if (msr->txcfg->debuglog_level >= 4) {
            msr_log(msr, 4, "Multipart: Ignoring data after last boundary (received %u bytes)", size);
        }

        return 1;
    }

    if (msr->mpd->bufleft == 0) {
        msr->mpd->flag_error = 1;
        *error_msg = apr_psprintf(msr->mp,
            "Multipart: Internal error in process_chunk: no space left in the buffer");
        return -1;
    }

    /* here we loop through the available data, one byte at a time */
    while(inleft > 0) {
        char c = *inptr;
        int process_buffer = 0;

        if ((c == '\r') && (msr->mpd->bufleft == 1)) {
            /* we don't want to take \r as the last byte in the buffer */
            process_buffer = 1;
        } else {
            inptr++;
            inleft = inleft - 1;

            *(msr->mpd->bufptr) = c;
            msr->mpd->bufptr++;
            msr->mpd->bufleft--;
        }

        /* until we either reach the end of the line
         * or the end of our internal buffer
         */
        if ((c == '\n') || (msr->mpd->bufleft == 0) || (process_buffer)) {
            int processed_as_boundary = 0;

            *(msr->mpd->bufptr) = 0;

            /* Do we have something that looks like a boundary? */
            if (   msr->mpd->buf_contains_line
                && (strlen(msr->mpd->buf) > 3)
                && (*(msr->mpd->buf) == '-')
                && (*(msr->mpd->buf + 1) == '-') )
            {
                /* Does it match our boundary? */
                if (   (strlen(msr->mpd->buf) >= strlen(msr->mpd->boundary) + 2)
                    && (strncmp(msr->mpd->buf + 2, msr->mpd->boundary, strlen(msr->mpd->boundary)) == 0) )
                {
                    char *boundary_end = msr->mpd->buf + 2 + strlen(msr->mpd->boundary);
                    int is_final = 0;

                    /* Is this the final boundary? */
                    if ((*boundary_end == '-') && (*(boundary_end + 1)== '-')) {
                        is_final = 1;
                        boundary_end += 2;

                        if (msr->mpd->is_complete != 0) {
                            msr->mpd->flag_error = 1;
                            *error_msg = apr_psprintf(msr->mp,
                                "Multipart: Invalid boundary (final duplicate).");
                            return -1;
                        }
                    }

                    /* Allow for CRLF and LF line endings. */
                    if (   ( (*boundary_end == '\r')
                              && (*(boundary_end + 1) == '\n')
                              && (*(boundary_end + 2) == '\0') )
                        || ( (*boundary_end == '\n')
                              && (*(boundary_end + 1) == '\0') ) )
                    {
                        if (*boundary_end == '\n') {
                            msr->mpd->flag_lf_line = 1;
                        } else {
                            msr->mpd->flag_crlf_line = 1;
                        }

                        if (multipart_process_boundary(msr, (is_final ? 1 : 0), error_msg) < 0) {
                            msr->mpd->flag_error = 1;
                            return -1;
                        }

                        if (is_final) {
                            msr->mpd->is_complete = 1;
                        }

                        processed_as_boundary = 1;
                        msr->mpd->boundary_count++;
                    }
                    else {
                        /* error */
                        msr->mpd->flag_error = 1;
                        *error_msg = apr_psprintf(msr->mp,
                            "Multipart: Invalid boundary: %s",
                            log_escape_nq(msr->mp, msr->mpd->buf));
                        return -1;
                    }
                } else { /* It looks like a boundary but we couldn't match it. */
                    char *p = NULL;

                    /* Check if an attempt to use quotes around the boundary was made. */
                    if (   (msr->mpd->flag_boundary_quoted)
                        && (strlen(msr->mpd->buf) >= strlen(msr->mpd->boundary) + 3)
                        && (*(msr->mpd->buf + 2) == '"')
                        && (strncmp(msr->mpd->buf + 3, msr->mpd->boundary, strlen(msr->mpd->boundary)) == 0)
                    ) {
                        msr->mpd->flag_error = 1;
                        *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary (quotes).");
                        return -1;
                    }

                    /* Check the beginning of the boundary for whitespace. */
                    p = msr->mpd->buf + 2;
                    while(isspace(*p)) {
                        p++;
                    }

                    if (   (p != msr->mpd->buf + 2)
                        && (strncmp(p, msr->mpd->boundary, strlen(msr->mpd->boundary)) == 0)
                    ) {
                        /* Found whitespace in front of a boundary. */
                        msr->mpd->flag_error = 1;
                        *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary (whitespace).");
                        return -1;
                    }

                    msr->mpd->flag_unmatched_boundary = 1;
                }
            } else { /* We do not think the buffer contains a boundary. */
                /* Look into the buffer to see if there's anything
                 * there that resembles a boundary.
                 */
                if (msr->mpd->buf_contains_line) {
                    int i, len = (MULTIPART_BUF_SIZE - msr->mpd->bufleft);
                    char *p = msr->mpd->buf;

                    for(i = 0; i < len; i++) {
                        if ((p[i] == '-') && (i + 1 < len) && (p[i + 1] == '-'))
                        {
                            if (strncmp(p + i + 2, msr->mpd->boundary, strlen(msr->mpd->boundary)) == 0) {
                                msr->mpd->flag_unmatched_boundary = 1;
                                break;
                            }
                        }
                    }
                }
            }

            /* Process as data if it was not a boundary. */
            if (processed_as_boundary == 0) {
                if (msr->mpd->mpp == NULL) {
                    msr->mpd->flag_data_before = 1;

                    if (msr->txcfg->debuglog_level >= 4) {
                        msr_log(msr, 4, "Multipart: Ignoring data before first boundary.");
                    }
                } else {
                    if (msr->mpd->mpp_state == 0) {
                        if ((msr->mpd->bufleft == 0) || (process_buffer)) {
                            /* part header lines must be shorter than
                             * MULTIPART_BUF_SIZE bytes
                             */
                            msr->mpd->flag_error = 1;
                            *error_msg = apr_psprintf(msr->mp,
                                "Multipart: Part header line over %d bytes long",
                                MULTIPART_BUF_SIZE);
                            return -1;
                        }

                        if (multipart_process_part_header(msr, error_msg) < 0) {
                            msr->mpd->flag_error = 1;
                            return -1;
                        }
                    } else {
                        if (multipart_process_part_data(msr, error_msg) < 0) {
                            msr->mpd->flag_error = 1;
                            return -1;
                        }
                    }
                }
            }

            /* Update the offset of the data we are about
             * to process. This is to allow us to know the
             * offsets of individual files and variables.
             */
            msr->mpd->buf_offset += (MULTIPART_BUF_SIZE - msr->mpd->bufleft);

            /* reset the pointer to the beginning of the buffer
             * and continue to accept input data
             */
            msr->mpd->bufptr = msr->mpd->buf;
            msr->mpd->bufleft = MULTIPART_BUF_SIZE;
            msr->mpd->buf_contains_line = (c == 0x0a) ? 1 : 0;
        }

        if ((msr->mpd->is_complete) && (inleft != 0)) {
            msr->mpd->flag_data_after = 1;

            if (msr->txcfg->debuglog_level >= 4) {
                msr_log(msr, 4, "Multipart: Ignoring data after last boundary (%u bytes left)", inleft);
            }

            return 1;
        }
    }

    return 1;
}