cscg24-guacamole

CSCG 2024 Challenge 'Guacamole Mashup'
git clone https://git.sinitax.com/sinitax/cscg24-guacamole
Log | Files | Refs | sfeed.txt

parser.c (9759B)


      1/*
      2 * Licensed to the Apache Software Foundation (ASF) under one
      3 * or more contributor license agreements.  See the NOTICE file
      4 * distributed with this work for additional information
      5 * regarding copyright ownership.  The ASF licenses this file
      6 * to you under the Apache License, Version 2.0 (the
      7 * "License"); you may not use this file except in compliance
      8 * with the License.  You may obtain a copy of the License at
      9 *
     10 *   http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing,
     13 * software distributed under the License is distributed on an
     14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     15 * KIND, either express or implied.  See the License for the
     16 * specific language governing permissions and limitations
     17 * under the License.
     18 */
     19
     20#include "config.h"
     21
     22#include "guacamole/mem.h"
     23#include "guacamole/error.h"
     24#include "guacamole/parser.h"
     25#include "guacamole/socket.h"
     26#include "guacamole/unicode.h"
     27
     28#include <stdlib.h>
     29#include <stdio.h>
     30#include <string.h>
     31
     32static void guac_parser_reset(guac_parser* parser) {
     33    parser->opcode = NULL;
     34    parser->argc = 0;
     35    parser->state = GUAC_PARSE_LENGTH;
     36    parser->__elementc = 0;
     37    parser->__element_length = 0;
     38}
     39
     40guac_parser* guac_parser_alloc() {
     41
     42    /* Allocate space for parser */
     43    guac_parser* parser = guac_mem_alloc(sizeof(guac_parser));
     44    if (parser == NULL) {
     45        guac_error = GUAC_STATUS_NO_MEMORY;
     46        guac_error_message = "Insufficient memory to allocate parser";
     47        return NULL;
     48    }
     49
     50    /* Init parse start/end markers */
     51    parser->__instructionbuf_unparsed_start = parser->__instructionbuf;
     52    parser->__instructionbuf_unparsed_end = parser->__instructionbuf;
     53
     54    guac_parser_reset(parser);
     55    return parser;
     56
     57}
     58
     59int guac_parser_append(guac_parser* parser, void* buffer, int length) {
     60
     61    char* char_buffer = (char*) buffer;
     62    int bytes_parsed = 0;
     63
     64    /* Do not exceed maximum number of elements */
     65    if (parser->__elementc == GUAC_INSTRUCTION_MAX_ELEMENTS
     66            && parser->state != GUAC_PARSE_COMPLETE) {
     67        parser->state = GUAC_PARSE_ERROR;
     68        return 0;
     69    }
     70
     71    /* Parse element length */
     72    if (parser->state == GUAC_PARSE_LENGTH) {
     73
     74        int parsed_length = parser->__element_length;
     75        while (bytes_parsed < length) {
     76
     77            /* Pull next character */
     78            char c = *(char_buffer++);
     79            bytes_parsed++;
     80
     81            /* If digit, add to length */
     82            if (c >= '0' && c <= '9')
     83                parsed_length = parsed_length*10 + c - '0';
     84
     85            /* If period, switch to parsing content */
     86            else if (c == '.') {
     87                parser->__elementv[parser->__elementc++] = char_buffer;
     88                parser->state = GUAC_PARSE_CONTENT;
     89                break;
     90            }
     91
     92            /* If not digit, parse error */
     93            else {
     94                parser->state = GUAC_PARSE_ERROR;
     95                return 0;
     96            }
     97
     98        }
     99
    100        /* If too long, parse error */
    101        if (parsed_length > GUAC_INSTRUCTION_MAX_LENGTH) {
    102            parser->state = GUAC_PARSE_ERROR;
    103            return 0;
    104        }
    105
    106        /* Save length */
    107        parser->__element_length = parsed_length;
    108
    109    } /* end parse length */
    110
    111    /* Parse element content */
    112    if (parser->state == GUAC_PARSE_CONTENT) {
    113
    114        while (bytes_parsed < length && parser->__element_length >= 0) {
    115
    116            /* Get length of current character */
    117            char c = *char_buffer;
    118            int char_length = guac_utf8_charsize((unsigned char) c);
    119
    120            /* If full character not present in buffer, stop now */
    121            if (char_length + bytes_parsed > length)
    122                break;
    123
    124            /* Record character as parsed */
    125            bytes_parsed += char_length;
    126
    127            /* If end of element, handle terminator */
    128            if (parser->__element_length == 0) {
    129
    130                *char_buffer = '\0';
    131
    132                /* If semicolon, store end-of-instruction */
    133                if (c == ';') {
    134                    parser->state = GUAC_PARSE_COMPLETE;
    135                    parser->opcode = parser->__elementv[0];
    136                    parser->argv = &(parser->__elementv[1]);
    137                    parser->argc = parser->__elementc - 1;
    138                    break;
    139                }
    140
    141                /* If comma, move on to next element */
    142                else if (c == ',') {
    143                    parser->state = GUAC_PARSE_LENGTH;
    144                    break;
    145                }
    146
    147                /* Otherwise, parse error */
    148                else {
    149                    parser->state = GUAC_PARSE_ERROR;
    150                    return 0;
    151                }
    152
    153            } /* end if end of element */
    154
    155            /* Advance to next character */
    156            parser->__element_length--;
    157            char_buffer += char_length;
    158
    159        }
    160
    161    } /* end parse content */
    162
    163    return bytes_parsed;
    164
    165}
    166
    167int guac_parser_read(guac_parser* parser, guac_socket* socket, int usec_timeout) {
    168
    169    char* unparsed_end   = parser->__instructionbuf_unparsed_end;
    170    char* unparsed_start = parser->__instructionbuf_unparsed_start;
    171    char* instr_start    = parser->__instructionbuf_unparsed_start;
    172    char* buffer_end     = parser->__instructionbuf + sizeof(parser->__instructionbuf);
    173
    174    /* Begin next instruction if previous was ended */
    175    if (parser->state == GUAC_PARSE_COMPLETE)
    176        guac_parser_reset(parser);
    177
    178    while (parser->state != GUAC_PARSE_COMPLETE
    179        && parser->state != GUAC_PARSE_ERROR) {
    180
    181        /* Add any available data to buffer */
    182        int parsed = guac_parser_append(parser, unparsed_start, unparsed_end - unparsed_start);
    183
    184        /* Read more data if not enough data to parse */
    185        if (parsed == 0 && parser->state != GUAC_PARSE_ERROR) {
    186
    187            int retval;
    188
    189            /* If no space left to read, fail */
    190            if (unparsed_end == buffer_end) {
    191
    192                /* Shift backward if possible */
    193                if (instr_start != parser->__instructionbuf) {
    194
    195                    int i;
    196
    197                    /* Shift buffer */
    198                    int offset = instr_start - parser->__instructionbuf;
    199                    memmove(parser->__instructionbuf, instr_start,
    200                            unparsed_end - instr_start);
    201
    202                    /* Update tracking pointers */
    203                    unparsed_end -= offset;
    204                    unparsed_start -= offset;
    205                    instr_start = parser->__instructionbuf;
    206
    207                    /* Update parsed elements, if any */
    208                    for (i=0; i < parser->__elementc; i++)
    209                        parser->__elementv[i] -= offset;
    210
    211                }
    212
    213                /* Otherwise, no memory to read */
    214                else {
    215                    guac_error = GUAC_STATUS_NO_MEMORY;
    216                    guac_error_message = "Instruction too long";
    217                    return -1;
    218                }
    219
    220            }
    221
    222            /* No instruction yet? Get more data ... */
    223            retval = guac_socket_select(socket, usec_timeout);
    224            if (retval <= 0)
    225                return -1;
    226           
    227            /* Attempt to fill buffer */
    228            retval = guac_socket_read(socket, unparsed_end,
    229                    buffer_end - unparsed_end);
    230
    231            /* Set guac_error if read unsuccessful */
    232            if (retval < 0) {
    233                guac_error = GUAC_STATUS_SEE_ERRNO;
    234                guac_error_message = "Error filling instruction buffer";
    235                return -1;
    236            }
    237
    238            /* EOF */
    239            if (retval == 0) {
    240                guac_error = GUAC_STATUS_CLOSED;
    241                guac_error_message = "End of stream reached while "
    242                                     "reading instruction";
    243                return -1;
    244            }
    245
    246            /* Update internal buffer */
    247            unparsed_end += retval;
    248
    249        }
    250
    251        /* If data was parsed, advance buffer */
    252        else
    253            unparsed_start += parsed;
    254
    255    } /* end while parsing data */
    256
    257    /* Fail on error */
    258    if (parser->state == GUAC_PARSE_ERROR) {
    259        guac_error = GUAC_STATUS_PROTOCOL_ERROR;
    260        guac_error_message = "Instruction parse error";
    261        return -1;
    262    }
    263
    264    parser->__instructionbuf_unparsed_start = unparsed_start;
    265    parser->__instructionbuf_unparsed_end = unparsed_end;
    266    return 0;
    267
    268}
    269
    270int guac_parser_expect(guac_parser* parser, guac_socket* socket, int usec_timeout, const char* opcode) {
    271
    272    /* Read next instruction */
    273    if (guac_parser_read(parser, socket, usec_timeout) != 0)
    274        return -1;
    275
    276    /* Validate instruction */
    277    if (strcmp(parser->opcode, opcode) != 0) {
    278        guac_error = GUAC_STATUS_PROTOCOL_ERROR;
    279        guac_error_message = "Instruction read did not have expected opcode";
    280        return -1;
    281    }
    282
    283    /* Return non-zero only if valid instruction read */
    284    return parser->state != GUAC_PARSE_COMPLETE;
    285
    286}
    287
    288int guac_parser_length(guac_parser* parser) {
    289
    290    char* unparsed_end   = parser->__instructionbuf_unparsed_end;
    291    char* unparsed_start = parser->__instructionbuf_unparsed_start;
    292
    293    return unparsed_end - unparsed_start;
    294
    295}
    296
    297int guac_parser_shift(guac_parser* parser, void* buffer, int length) {
    298
    299    char* copy_end   = parser->__instructionbuf_unparsed_end;
    300    char* copy_start = parser->__instructionbuf_unparsed_start;
    301
    302    /* Contain copy region within length */
    303    if (copy_end - copy_start > length)
    304        copy_end = copy_start + length;
    305
    306    /* Copy buffer */
    307    length = copy_end - copy_start;
    308    memcpy(buffer, copy_start, length);
    309
    310    parser->__instructionbuf_unparsed_start = copy_end;
    311
    312    return length;
    313
    314}
    315
    316void guac_parser_free(guac_parser* parser) {
    317    guac_mem_free(parser);
    318}
    319