Beispiel #1
0
SXE_RETURN
sxe_hex_to_bytes(unsigned char * bytes, const char * hex, unsigned hex_length)
{
    SXE_RETURN result = SXE_RETURN_ERROR_INTERNAL;
    unsigned   i;
    char       character;
    unsigned   nibble_high;
    unsigned   nibble_low;

    SXEA1((hex_length % 1) == 0, "sxe_hex_to_bytes: hex string length %u is odd", hex_length);
    SXEE6("(bytes=%p,hex='%.*s',hex_length=%u)", bytes, hex_length, hex, hex_length);

    for (i = 0; i < hex_length; i++) {
        if (((nibble_high = hex_character_to_nibble[(unsigned)(character = hex[  i])]) == NA)
         || ((nibble_low  = hex_character_to_nibble[(unsigned)(character = hex[++i])]) == NA))
        {
            SXEL3(isprint(character) ? "%s%c'" : "%s\\x%02x'", "sxe_hex_to_bytes: Unexpected hex character '", hex[i]);
            goto SXE_EARLY_OUT;
        }

        bytes[(i - 1) / 2] = (nibble_high << 4) | nibble_low;
    }

    result = SXE_RETURN_OK;

SXE_EARLY_OUT:
    SXER6("return result=%s", sxe_return_to_string(result));
    return result;
}
Beispiel #2
0
SXE_RETURN
sxe_ring_buffer_consumed(void * base, SXE_RING_BUFFER_CONTEXT * context, unsigned len)
{
    SXE_RETURN result = SXE_RETURN_OK;
    SXEE81("sxe_ring_buffer_consumed(len:%u)", len);

    if ((result = sxe_ring_buffer_check_over_run(base, context)) == SXE_RETURN_ERROR_INTERNAL) {
        goto SXE_ERROR_OUT;
    }

    SXEL92("data_block was: %p, moving to %p", context->data_block, context->data_block + len);
    context->data_block     += len;
    context->data_block_len -= len;

    SXEA10(context->data_block <= SXE_RING_BUFFER_WRITEN_END + 1, "the data block did not go off the end of the array");

    if ((context->data_block == SXE_RING_BUFFER_WRITEN_END + 1) && (context->data_block != SXE_RING_BUFFER_CURRENT)) {
        SXEA10(context->data_block_len == 0, "If this consume brought us to the end of the array, the remaining length must be zero");
        SXEL90("Wrapping data_block to the start of the ring");
        context->data_block = SXE_RING_BUFFER_ARRAY_BASE;
        context->itteration++;
    }

SXE_EARLY_OR_ERROR_OUT:
    SXEL93("context data_block: %p, data_block_len: %u itteration: %u", context->data_block, context->data_block_len, context->itteration);
    SXER81("return %s", sxe_return_to_string(result));
    return result;
}
Beispiel #3
0
SXE_RETURN
sxe_valid_hex_to_unsigned(const char * hex, unsigned hex_length_maximum, unsigned * value)
{
    SXE_RETURN result = SXE_RETURN_ERROR_INTERNAL;
    unsigned   val = 0;
    unsigned   i;

    SXEE6("(hex='%.*s', hex_length_maximum=%u)", hex_length_maximum, hex, hex_length_maximum);

    for (i = 0; (i < hex_length_maximum) && (hex[i] != '\0'); i++) {
        if (hex_character_to_nibble[(unsigned)(hex[i])] == NA) {
            SXEL6("Encountered unexpected hex character '%c'", hex[i]);
            goto SXE_EARLY_OUT;
        }

        val = (val << 4) | hex_character_to_nibble[(unsigned)(hex[i])];
    }

    *value = val;
    result = SXE_RETURN_OK;

SXE_EARLY_OUT:
    SXER6("return %s", sxe_return_to_string(result));
    return result;
}
Beispiel #4
0
SXE_RETURN
sha1_from_hex(SOPHOS_SHA1 * sha1, const char * sha1_in_hex)
{
    SXE_RETURN result;

    SXEE82("sxe_sha1_from_hex(sha1=%p,sha1_in_hex='%s'", sha1, sha1_in_hex);
    result = sxe_hex_to_bytes((unsigned char *)sha1, sha1_in_hex, SHA1_IN_HEX_LENGTH);
    SXER81("return %s", sxe_return_to_string(result));
    return result;
}
Beispiel #5
0
SXE_RETURN
md5_from_hex(SOPHOS_MD5 * md5, const char * md5_in_hex)
{
    SXE_RETURN result;

    SXEE82("(md5=%p,md5_in_hex='%s'", md5, md5_in_hex);
    result = sxe_hex_to_bytes((unsigned char *)md5, md5_in_hex, MD5_IN_HEX_LENGTH);
    SXER81("return %s", sxe_return_to_string(result));
    return result;
}
Beispiel #6
0
/* Given a DNS query or reply, extract the DNS question from the DNS packet.
 *
 * Note that the encoded question may be rot13 transformed - see the
 * sxe_dns_rot13_on_string() function to correct this.
 *
 *  dns_packet - DNS packet containting encoded question
 *  question - character buffer into which the decoded zero terminated question text will be written
 *  question_length_maximum - length of 'question'
 *  question_length - pointer (or NULL) which will be set to actual question length
 *
 *  Returns
 *   SXE_RETURN_OK on success
 */
SXE_RETURN
sxe_dns_get_question(const unsigned char * dns_packet, char * question, unsigned question_length_maximum, unsigned * question_length)
{
    SXE_RETURN result = SXE_RETURN_ERROR_INTERNAL;

    SXEE84("sxe_dns_get_question(dns_packet=%p, question=%p, question_length_maximum=%d, question_length=%p)",
           dns_packet, question, question_length_maximum, question_length);

    result = sxe_dns_decode_name(dns_packet, SXE_BUF_SIZE, SXE_DNS_QUESTION_OFFSET, question, question_length_maximum, question_length);

    SXER81("return // result = %s", sxe_return_to_string(result));
    return result;
}
Beispiel #7
0
SXE_RETURN
md5_to_hex(SOPHOS_MD5 * md5, char * md5_in_hex, unsigned md5_in_hex_length)
{
    SXE_RETURN result = SXE_RETURN_OK;

    SXEE86("(md5=%08x%08x%08x%08x,md5_in_hex='%p',md5_in_hex_length='%u'",
           md5->word[3], md5->word[2], md5->word[1], md5->word[0],
           md5_in_hex, md5_in_hex_length);
    SXEA11(md5_in_hex_length == (MD5_IN_HEX_LENGTH + 1), "Incorrect length of char * for md5_to_hex(): '%u'", md5_in_hex_length);

    snprintf(md5_in_hex     , 9, "%08x", htonl(md5->word[0]));
    snprintf(md5_in_hex +  8, 9, "%08x", htonl(md5->word[1]));
    snprintf(md5_in_hex + 16, 9, "%08x", htonl(md5->word[2]));
    snprintf(md5_in_hex + 24, 9, "%08x", htonl(md5->word[3]));
    SXEL62("md5_in_hex: '%.*s'", MD5_IN_HEX_LENGTH, md5_in_hex);

    SXER81("return %s", sxe_return_to_string(result));
    return result;
}
Beispiel #8
0
SXE_RETURN
sha1_to_hex(SOPHOS_SHA1 * sha1, char * sha1_in_hex, unsigned sha1_in_hex_length)
{
    SXE_RETURN result = SXE_RETURN_OK;

    SXEE87("sxe_sha1_to_hex(sha1=%08x%08x%08x%08x%08x,sha1_in_hex='%p',sha1_in_hex_length='%u'",
           sha1->word[4], sha1->word[3], sha1->word[2], sha1->word[1], sha1->word[0],
           sha1_in_hex, sha1_in_hex_length);
    SXEA11(sha1_in_hex_length == (SHA1_IN_HEX_LENGTH + 1), "Incorrect length of char * for sha1_to_hex(): '%u'", sha1_in_hex_length);

    snprintf(sha1_in_hex     , 9, "%08x", htonl(sha1->word[0]));
    snprintf(sha1_in_hex +  8, 9, "%08x", htonl(sha1->word[1]));
    snprintf(sha1_in_hex + 16, 9, "%08x", htonl(sha1->word[2]));
    snprintf(sha1_in_hex + 24, 9, "%08x", htonl(sha1->word[3]));
    snprintf(sha1_in_hex + 32, 9, "%08x", htonl(sha1->word[4]));
    SXEL62("sha1_in_hex: '%.*s'", 40, sha1_in_hex);

    SXER81("return %s", sxe_return_to_string(result));
    return result;
}
Beispiel #9
0
static SXE_RETURN
sxe_ring_buffer_check_over_run(void * base, SXE_RING_BUFFER_CONTEXT * context)
{
    SXE_RETURN result = SXE_RETURN_OK;
    SXEE80("sxe_ring_buffer_check_over_run()");
    SXEL92("ring context data_block: %p, itteration: %u", SXE_RING_BUFFER_CURRENT, SXE_RING_BUFFER_ITERATION);
    SXEL92("user context data_block: %p, itteration: %u", context->data_block, context->itteration);

    if  ( (SXE_RING_BUFFER_ITERATION > (context->itteration + 1))
        || ((SXE_RING_BUFFER_ITERATION > context->itteration) && (SXE_RING_BUFFER_CURRENT > context->data_block)))
    {
       SXEL90("This reader has fallen behind the writer!");
       result = SXE_RETURN_ERROR_INTERNAL;
       SXEL90("Moving data_block to the start of the ring");
       context->data_block = SXE_RING_BUFFER_CURRENT;
       context->itteration = SXE_RING_BUFFER_ITERATION;
       context->data_block_len = 0;
    }

    SXER81("return %s", sxe_return_to_string(result));
    return result;
}
Beispiel #10
0
SXE_RETURN
sxe_ring_buffer_next_block(void * base, SXE_RING_BUFFER_CONTEXT * context)
{
    SXE_RETURN result = SXE_RETURN_OK;
    SXEE80("sxe_ring_buffer_next_block()");

    if ((result = sxe_ring_buffer_check_over_run(base, context)) == SXE_RETURN_ERROR_INTERNAL) {
        goto SXE_ERROR_OUT;
    }

    if (SXE_RING_BUFFER_CURRENT > context->data_block) {
        SXEL90("Reading up to the head of the ring");
        context->data_block_len = SXE_RING_BUFFER_CURRENT - context->data_block;
    }
    else if (SXE_RING_BUFFER_CURRENT <= context->data_block &&
             SXE_RING_BUFFER_ITERATION == context->itteration + 1 &&
             SXE_RING_BUFFER_WRITEN_END == context->data_block - 1) {
        SXEL90("The ring was forced wrapped while context was at the head");
        SXEL90("Wrap the ring and read up to the head");
        context->data_block = SXE_RING_BUFFER_ARRAY_BASE;
        context->itteration++;
        context->data_block_len = SXE_RING_BUFFER_CURRENT - context->data_block;
    }
    else if (SXE_RING_BUFFER_ITERATION > context->itteration) {
        SXEL90("Reading to the end of the ring");
        context->data_block_len = SXE_RING_BUFFER_WRITEN_END - context->data_block + 1;
    }
    else {
        SXEL90("Nothing to read");
    }

SXE_EARLY_OR_ERROR_OUT:
    SXEL93("context data_block: %p, data_block_len: %u itteration: %u", context->data_block, context->data_block_len, context->itteration);
    SXER81("return %s", sxe_return_to_string(result));
    return result;
}
Beispiel #11
0
/* Note that the decoded_name will be NUL terminated, and therefore the
 * maximum_name_length_maximum should probably be 254 and not 253 (though, with
 * pointers, the decoded name could well be *longer* than 253 characters!
 */
static SXE_RETURN
sxe_dns_decode_name(
    const unsigned char * dns_packet                 ,
    unsigned                dns_packet_length          ,
    unsigned              name_offset                ,
    char                * decoded_name               ,
    unsigned                decoded_name_length_maximum,
    unsigned              * decoded_name_length        )
{
    SXE_RETURN result = SXE_RETURN_ERROR_INTERNAL;
    unsigned   decoded_name_offset = 0;

    SXEE86("sxe_dns_decode_name(dns_packet=%p, dns_packet_length=%u, name_offset=%u, decoded_name=%p, decoded_name_length_maximum=%u, decoded_name_length=%p)", dns_packet, dns_packet_length, name_offset, decoded_name, decoded_name_length_maximum, decoded_name_length);
    SXEA10(decoded_name_length != 0,                                 "A value for decoded_name_length must be provided");
    SXEA10(decoded_name == NULL || decoded_name_length_maximum != 0, "NAME can not be decoded into an empty buffer");

    while (name_offset < dns_packet_length) {
        unsigned char len = dns_packet[name_offset];

        if (len == '\0') {
            SXEL60("NAME is terminated normally");
            if (decoded_name_offset > 0) {
                --decoded_name_offset;
            }

            if (decoded_name != NULL) {
                decoded_name[decoded_name_offset] = '\0';
            }
            *decoded_name_length = decoded_name_offset;
            result = SXE_RETURN_OK;
            goto SXE_EARLY_OUT;
        }
        else if ((len & SXE_DNS_LABEL_LENGTH_POINTER) == 0) {
            SXEL73("Normal label of length %u '%.*s'", len, len, &dns_packet[name_offset + 1]);

            /* Don't allow the NAME to exceed the known packet length */
            if ((decoded_name != NULL) && (decoded_name_offset + len + 1 >= decoded_name_length_maximum)) {
                SXEL51("sxe_dns_decode_name(): NAME is invalid; Decoded NAME is longer than the provided buffer length %u", decoded_name_length_maximum);
                goto SXE_ERROR_OUT;
            }

            /* Copy the label */
            if (decoded_name != NULL) {
                memcpy((unsigned char*)(decoded_name + decoded_name_offset), dns_packet + name_offset + 1, len);
                decoded_name[decoded_name_offset + len] = '.';
            }

            decoded_name_offset += len + 1;
        }
        else if ((len & SXE_DNS_LABEL_LENGTH_POINTER) == SXE_DNS_LABEL_LENGTH_POINTER) {
            SXEL60("NAME includes a pointer");
            name_offset = (len & ~SXE_DNS_LABEL_LENGTH_POINTER) + dns_packet[name_offset + 1];
            SXEL61("New NAME offset is %u", name_offset);

            /* Prevent looping forever - a pointer should never point to a pointer
             */
            if ((dns_packet[name_offset] & SXE_DNS_LABEL_LENGTH_POINTER) != 0) {
                SXEL51("sxe_dns_decode_name(): NAME contains a pointer which points to another pointer at packet offset %u",
                       name_offset);
                goto SXE_ERROR_OUT;
            }

            continue;
        }
        else {
            SXEL51("sxe_dns_decode_name(): NAME is invalid; NAME contains an invalid length/pointer value at packet offset %u", name_offset);
            goto SXE_ERROR_OUT;
        }

        name_offset += len + 1;
    }

    SXEL60("NAME is invalid because it extends outside the packet");

SXE_ERROR_OUT:
SXE_EARLY_OUT:
    SXER81("return // result=%s", sxe_return_to_string(result));
    return result;
}
Beispiel #12
0
int
main(int argc, char ** argv)
{
#ifdef WINDOWS_NT
    SXEL10("WARNING: Need to implement sxe_spawn() on Windows to run this test file!");
#else
    int           fd;
    double        start_time;
    unsigned      count;
    unsigned      id;
    unsigned    * pool;
    unsigned    * shared;
    size_t        size;
    SXE_MMAP      memmap;
    SXE_RETURN    result;
    SXE_SPAWN     spawn[TEST_CLIENT_INSTANCES];

    if (argc > 1) {
        count = atoi(argv[1]);
        sxe_mmap_open(&memmap, "memmap");
        shared  = (unsigned *)(unsigned long)SXE_MMAP_ADDR(&memmap);
        pool    = sxe_pool_from_base(shared);
        SXEL63("Instance %u mapped to shared pool // base=%p, pool=%p", count, shared, pool);
        do {
            usleep(10000 * count);
            id = sxe_pool_set_oldest_element_state_locked(pool, TEST_STATE_FREE, TEST_STATE_CLIENT_TAKE);
            SXEA10(id != SXE_POOL_LOCK_NEVER_TAKEN, "Got SXE_POOL_LOCK_NEVER_TAKEN");;
        } while (id == SXE_POOL_NO_INDEX);

        SXEL62("Instance %u got pool element %u", count, id);
        pool[id] = count;
        sxe_pool_set_indexed_element_state_locked(pool, id, TEST_STATE_CLIENT_TAKE, TEST_STATE_CLIENT_DONE);
        sxe_mmap_close(&memmap);
        SXEL61("Instance %u exiting", count);
        return 0;
    }

    plan_tests(5);
    ok((size = sxe_pool_size(TEST_CLIENT_INSTANCES/2, sizeof(*pool), TEST_STATE_NUMBER_OF_STATES)) >= TEST_CLIENT_INSTANCES * sizeof(*pool),
       "Expect pool size %u to be at least the size of the array %u", size, TEST_CLIENT_INSTANCES * sizeof(*pool));

    SXEA11((fd = open("memmap", O_CREAT | O_TRUNC | O_WRONLY, 0666)) >= 0, "Failed to create file 'memmap': %s",         strerror(errno));
    SXEA12(ftruncate(fd, size)                                       >= 0, "Failed to extend the file to %lu bytes: %s", size, strerror(errno));
    close(fd);
    sxe_mmap_open(&memmap, "memmap");
    shared = (unsigned *)(unsigned long)SXE_MMAP_ADDR(&memmap);

    pool = sxe_pool_construct(shared, "shared-pool", TEST_CLIENT_INSTANCES/2, sizeof(*pool), TEST_STATE_NUMBER_OF_STATES, SXE_POOL_LOCKS_ENABLED);

    sxe_register(TEST_CLIENT_INSTANCES + 1, 0);
    SXEA11((result = sxe_init()) == SXE_RETURN_OK,  "Failed to initialize the SXE package: %s",  sxe_return_to_string(result));

    for (count = 1; count <= TEST_CLIENT_INSTANCES; count++) {
        char buffer[12];

        snprintf(buffer, sizeof(buffer), "%u", count);
        result = sxe_spawn(NULL, &spawn[count - 1], argv[0], buffer, NULL, NULL, NULL, NULL);
        SXEA13(result == SXE_RETURN_OK, "Failed to spawn '%s %s': %s", argv[0], buffer, sxe_return_to_string(result));
    }

    start_time = sxe_get_time_in_seconds();
    for (count = 0; (count < TEST_CLIENT_INSTANCES); ) {
        SXEA10((TEST_WAIT + start_time ) > sxe_get_time_in_seconds(), "Unexpected timeout... is the hardware too slow?");
        usleep(10000);
        id = sxe_pool_set_oldest_element_state_locked(pool, TEST_STATE_CLIENT_DONE, TEST_STATE_FREE);

        /* Assert here in  the test. The actual service would take specific action here */
        SXEA12(id != SXE_POOL_LOCK_NEVER_TAKEN, "Parent: Failed to acqure lock .. yield limit reached. id %u vs %u", id, SXE_POOL_LOCK_NEVER_TAKEN);

        if (id != SXE_POOL_NO_INDEX) {
            SXEL62("Looks like instance %u got element %u", pool[id], id);
            count++;
        }
    }
    ok(count == TEST_CLIENT_INSTANCES, "All clients got an element in the pool");

    start_time = sxe_get_time_in_seconds();
    for (count = 0; (count < TEST_CLIENT_INSTANCES); count++) {
        SXEA10((TEST_WAIT + start_time ) > sxe_get_time_in_seconds(), "Unexpected timeout... is the hardware too slow?");
        waitpid(spawn[count].pid, NULL, 0);
    }

    ok(SXE_POOL_LOCK_NEVER_TAKEN != sxe_pool_lock(pool), "Forced lock to be always locked!");
    id = sxe_pool_set_oldest_element_state_locked(pool, TEST_STATE_FREE, TEST_STATE_FREE);
    ok(id == SXE_POOL_LOCK_NEVER_TAKEN,   "sxe_pool_set_oldest_element_state_locked() Failed to acquire lock");
    id = sxe_pool_set_indexed_element_state_locked(pool, 0, TEST_STATE_FREE, TEST_STATE_FREE);
    ok(id == SXE_POOL_LOCK_NEVER_TAKEN,   "sxe_pool_set_indexed_element_state_locked() Failed to acquire lock");
    sxe_pool_unlock(pool);
    sxe_pool_override_locked(pool); /* for coverage */

    sxe_mmap_close(&memmap);
    return exit_status();
#endif
}
Beispiel #13
0
int
main(void) {
    struct object   self;
    struct object * this = &self;

    plan_tests(47);

    /* Test sxe_return_to_string()
     */
    is_eq(sxe_return_to_string(SXE_RETURN_OK),             "OK"            , "sxe_return_to_string(SXE_RETURN_OK) eq \"OK\"");
    is_eq(sxe_return_to_string(SXE_RETURN_ERROR_INTERNAL), "ERROR_INTERNAL", "sxe_return_to_string(SXE_RETURN_ERROR_INTERNAL) eq \"ERROR_INTERNAL\"");
    is(   sxe_return_to_string(~0U),                        NULL,            "sxe_return_to_string(~0U) == NULL");
    TEST_CASE_RETURN_TO_STRING(EXPIRED_VALUE);
    TEST_CASE_RETURN_TO_STRING(NO_UNUSED_ELEMENTS);
    TEST_CASE_RETURN_TO_STRING(IN_PROGRESS);
    TEST_CASE_RETURN_TO_STRING(UNCATEGORIZED);
    TEST_CASE_RETURN_TO_STRING(END_OF_FILE);
    TEST_CASE_RETURN_TO_STRING(WARN_ALREADY_INITIALIZED);
    TEST_CASE_RETURN_TO_STRING(WARN_WOULD_BLOCK);
    TEST_CASE_RETURN_TO_STRING(WARN_ALREADY_CLOSED);
    TEST_CASE_RETURN_TO_STRING(ERROR_NOT_INITIALIZED);
    TEST_CASE_RETURN_TO_STRING(ERROR_ALLOC);
    TEST_CASE_RETURN_TO_STRING(ERROR_NO_CONNECTION);
    TEST_CASE_RETURN_TO_STRING(ERROR_ALREADY_CONNECTED);
    TEST_CASE_RETURN_TO_STRING(ERROR_INVALID_URI);
    TEST_CASE_RETURN_TO_STRING(ERROR_BAD_MESSAGE);
    TEST_CASE_RETURN_TO_STRING(ERROR_ADDRESS_IN_USE);
    TEST_CASE_RETURN_TO_STRING(ERROR_INTERRUPTED);
    TEST_CASE_RETURN_TO_STRING(ERROR_COMMAND_NOT_RUN);
    TEST_CASE_RETURN_TO_STRING(ERROR_LOCK_NOT_TAKEN);
    TEST_CASE_RETURN_TO_STRING(ERROR_INCORRECT_STATE);
    TEST_CASE_RETURN_TO_STRING(ERROR_TIMED_OUT);
    TEST_CASE_RETURN_TO_STRING(ERROR_WRITE_FAILED);
    TEST_CASE_RETURN_TO_STRING(INVALID_VALUE);           /* Just for coverage */

    ok(signal(SIGABRT, test_abort_handler) != SIG_ERR, "Caught abort signal");
    sxe_log_hook_line_out(NULL); /* for coverage */
    sxe_log_hook_line_out(log_line);
    PUTENV("SXE_LOG_LEVEL=6");   /* Trigger processing of the level in the first call to the log */
    SXEE60(entering);
    PUTENV("SXE_LOG_LEVEL=1");   /* This should be ignored. If it is not, the tests will fail    */
    this->id = 99;
    SXEL60I(logging);
    SXEA60(1, "Asserting true");
    SXED60(dumpdata, 4);
    SXER60(exiting);
    SXEL60(verylong);
    SXEE61("really long entry message: %s", verylong);
    SXEL60(escape);
    SXEL60(hextrunc);
    SXED60(dumpdata, 0);    /* Edge case */
    SXEA80(1, "We should not get this, because level 8 is too low!");

    is(sxe_log_decrease_level(SXE_LOG_LEVEL_ERROR),       SXE_LOG_LEVEL_TRACE,       "Level decreased to ERROR (2) from TRACE (6)");
    is(sxe_log_set_level(     SXE_LOG_LEVEL_INFORMATION), SXE_LOG_LEVEL_ERROR,       "Level set to INFO, was ERROR");
    is(sxe_log_decrease_level(SXE_LOG_LEVEL_TRACE),       SXE_LOG_LEVEL_INFORMATION, "Level was INFO, TRACE is not a decrease");

#if defined(_WIN32) && defined(LOCAL_SXE_DEBUG)
    skip(3, "Can't test aborts in a Windows debug build, due to pop up Window stopping the build");
#else
    SXEA60(this != &self, "This is not self");  /* Abort - must be the last thing we do*/
    fail("Did not catch an abort signal");
#endif
    }    /* Oog! Close the brace opened in the SXEE61 macro above */