rt_shared void initstk(void) { /* Initialize both the local stack and the hector stack. Those two stacks * may have their context saved and restored in an Eiffel routine, so they * need to be correctly initialized. * In workbench mode, the debugger stack is also created here. */ EIF_GET_CONTEXT #ifdef ISE_GC char **top; #endif #ifdef EIF_ASSERTIONS #if defined(EIF_WINDOWS) && defined(_DEBUG) int tmpDbgFlag = 0; _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT); _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDOUT); _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDOUT); tmpDbgFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); tmpDbgFlag |= _CRTDBG_DELAY_FREE_MEM_DF; tmpDbgFlag |= _CRTDBG_LEAK_CHECK_DF; tmpDbgFlag |= _CRTDBG_CHECK_ALWAYS_DF; _CrtSetDbgFlag(tmpDbgFlag); #endif #endif #if defined(EIF_WINDOWS) && defined(_MSC_VER) /* This is to catch CRT raised exception when passing incorrect arguments to CRT routines. */ _set_invalid_parameter_handler(eif_invalid_paramter_handler); #endif #ifdef ISE_GC top = st_alloc(&loc_set, eif_stack_chunk); if (top != (char **) 0) top = st_alloc(&hec_stack, eif_stack_chunk); if (top == (char **) 0) eif_panic(MTC "can't create runtime stacks"); #endif #ifdef WORKBENCH initdb(); /* Initialize debugger stack */ #endif }
int main(int a, int b){ st_t * st1, * st2; ASSUME(a> 0); ASSUME(b > 0); st1 = st_alloc(a,b); st2 = st_alloc(-b,-a); st_compact(st1,st2); return 1; }
/** * @brief Merge a list of smtp message headers into a single string, preceded by the leading text and followed by the trailing text. * @param headers an inx holder containing a collection of header string data to be merged together. * @param leading a managed string containing text that will lead each header line. * @param trailing a managed string containing text that will trail each header line. * @return NULL on failure or a managed string containing the merged headers on success. */ stringer_t * portal_smtp_merge_headers(inx_t *headers, stringer_t *leading, stringer_t *trailing) { stringer_t *result = NULL, *tmp, *current; inx_cursor_t *cursor; if (!headers || !leading || !trailing) { return NULL; } if (!(cursor = inx_cursor_alloc(headers))) { return NULL; } while ((current = inx_cursor_value_next(cursor))) { if (!(tmp = st_merge("ssss", result, leading, current, trailing))) { inx_cursor_free(cursor); st_cleanup(result); return NULL; } result = tmp; } inx_cursor_free(cursor); // We should at least return an empty managed string if we have a valid inx holder but no data in it. if (!result) { result = st_alloc(0); } return result; }
/** * @brief Decompress data using the bzip engine. * @param compressed a pointer to the head of the compressed data. * @return NULL on failure, or a managed string containing the uncompressed data on success. */ stringer_t * decompress_bzip(compress_t *compressed) { int ret; void *bptr; uint64_t hash, rlen, blen; stringer_t *result = NULL; compress_head_t *head; if (!(head = (compress_head_t *)compressed)) { log_info("Invalid compression header. {compress_head = NULL}"); return NULL; } else if (head->engine != COMPRESS_ENGINE_BZIP) { log_info("The buffer passed in was not compressed using the BZIP engine. {engine = %hhu}", head->engine); return NULL; } else if (!(bptr = compress_body_data(compressed)) || !(blen = head->length.compressed) || !(rlen = head->length.original) || head->hash.compressed != (hash = hash_adler32(bptr, blen))) { log_info("The compressed has been corrupted. {expected = %lu / input = %lu}", head->hash.compressed, hash); return NULL; } else if (!(result = st_alloc(head->length.original + 1))) { log_info("Could not allocate a block of %lu bytes for the uncompressed data.", head->length.original); return NULL; } else if ((ret = BZ2_bzBuffToBuffDecompress_d(st_data_get(result), (unsigned int *)&rlen, bptr, blen, 0, 0)) != BZ_OK) { log_info("Unable to decompress the buffer. {BZ2_bzBuffToBuffDecompress = %i}", ret); st_free(result); return NULL; } else if (head->length.original != rlen || head->hash.original != (hash = hash_adler32(st_data_get(result), rlen))) { log_info("The uncompressed data is corrupted. {input = %lu != %lu / hash = %lu != %lu}", head->length.original, rlen, head->hash.original, hash); st_free(result); return NULL; } st_length_set(result, rlen); return result; }
/** * @brief Serialize an unsigned 16-bit integer into a managed string. * @note This function will reallocate the output managed string if necessary, and append the serialized data, updating the length field to reflect the changes. * @param data a pointer to the address of a managed string to receive the output, which will be allocated for the caller if NULL is passed. * @param number the value of the unsigned 16-bit integer to be serialized. * @return true if the specified value was serialized and stored successfully, or false on failure. */ bool_t serialize_uint16(stringer_t **data, uint16_t number) { stringer_t *holder = NULL; size_t length = uint16_digits(number); // Make sure we have room to write the number. if (!data) { return false; } else if (!*data && (*data = st_alloc(1024)) == NULL) { return false; } else if (st_avail_get(*data) - st_length_get(*data) < (length+1) && (holder = st_realloc(*data, st_avail_get(*data) + 1024)) == NULL) { return false; } else if (holder) { *data = holder; } if (length != snprintf(st_char_get(*data) + st_length_get(*data), st_avail_get(*data) - st_length_get(*data), "%hu", number)) { return false; } *(st_char_get(*data) + st_length_get(*data) + length) = ';'; st_length_set(*data, st_length_get(*data) + length + 1); return true; }
rt_public void failure(void) { /* A fatal Eiffel exception has occurred. The stack of exceptions is dumped * and the memory is cleaned up, if possible. */ GTCX /* When we arrive at this location, the Eiffel call stack is theoratically * empty, but `loc_set' still points to the location where the last Eiffel * call has been made. Since now `loc_set' records address of C local variable * which are usually located on the C call stack. Any addition to the C call * stack (like the call to `trapsig' below) will corrupt the information * stored in `loc_set' since it will replace a location by another. * * To prevent this, we have to manually empty `loc_set'. It does not matter * at this point that the GC forgets about all objects referenced through * a local variable since all Eiffel calls have been executed. * Doing so, enables a safe `reclaim' that will not traverse `loc_set' * objects. * We then create an empty `loc_set' as most of the run-time macros for stack * management expect `loc_set' to have at least one chunk. */ #ifdef ISE_GC st_reset (&loc_set); st_alloc (&loc_set, eif_stack_chunk); #endif trapsig(emergency); /* Weird signals are trapped */ esfail(MTC_NOARG); /* Dump the execution stack trace */ reclaim(); /* Reclaim all the objects */ exit(1); /* Abnormal termination */ /* NOTREACHED */ }
bool_t check_string_dupe(char *type, uint32_t check) { size_t len; stringer_t *s, *d; if (!(s = st_alloc(check, st_length_int(constant)))) { return false; } len = snprintf(st_char_get(s), st_length_int(constant) + 1, "%.*s", st_length_int(constant), st_char_get(constant)); if (check & MAPPED_T || check & MANAGED_T) { st_length_set(s, len); } if (!(d = st_dupe(s))) { st_free(s); return false; } log_print("%28.28s = %.*s", type, st_length_int(d), st_char_get(d)); if (memcmp(st_char_get(d), st_char_get(s), st_length_get(s))) { st_free(s); st_free(d); return false; } st_free(s); st_free(d); return true; }
/** * @brief Generate a MIME boundary string that is unique to a collection of content. * @param parts a pointer to an array of managed strings containing the MIME children data to be separated by the boundary. * @return NULL on failure, or a pointer to a managed string containing the generated boundary on success. */ stringer_t * mail_mime_generate_boundary (array_t *parts) { stringer_t *result, *cmp; chr_t *ptr; size_t blen = 16; int_t rnd; if (!parts) { log_pedantic("Cannot generate mime boundary with empty input data."); return NULL; } if (!(result = st_alloc(blen))) { log_error("Unable to allocate space for boundary string."); return NULL; } // Keep generating boundary strings until one of them is unique... we don't expect collisions to happen that often. while (1) { srand(rand_get_uint64()); ptr = st_char_get (result); // Generate blen's worth of random bytes, each being either a random digit or lowercase letter. for (size_t i = 0; i < blen; i++) { rnd = rand(); if (rnd % 2) { *ptr++ = '0' + (rnd % 10); } else { *ptr ++ = 'a' + (rnd % 26); } } st_length_set(result, blen); // Now make sure it's not found in any of the parts. for (size_t i = 0; i < ar_length_get(parts); i++) { if (!(cmp = (stringer_t *) ar_field_ptr(parts, i))) { log_pedantic("Could not generate mime boundary for null content."); st_free(result); return NULL; } if (st_search_ci(cmp, result, NULL)) { continue; } } // If we made it this far, the boundary string was not found in any of the supplied content. break; } return result; }
/** * @brief Convert a hex string into a binary data blob. * @note All hex strings should be composed of pairs of two hex characters representing individual bytes. * Invalid hex characters will simply be ignored during processing. * @param h a managed string containing the input hex string to be decoded. * @param output if not NULL, a pointer to a managed string to contain the decoded binary output; if NULL, a new string * will be allocated and returned to the caller. * @return a pointer to a managed string containing the decoded output, or NULL on failure. */ stringer_t * hex_decode_st(stringer_t *h, stringer_t *output) { uint32_t opts = 0; uchr_t *p = NULL, *o, c = 0; size_t w = 0, len = 0, valid; stringer_t *result = NULL; if (output && !st_valid_destination((opts = *((uint32_t *)output)))) { log_pedantic("An output string was supplied but it does not represent a buffer capable of holding the output."); return NULL; } else if (st_empty_out(h, &p, &len) || !(valid = hex_count_st(h))) { log_pedantic("The input block does not appear to hold any data ready for decoding. {%slen = %zu}", p ? "" : "p = NULL / ", len); return NULL; } // Make sure the output buffer is large enough or if output was passed in as NULL we'll attempt the allocation of our own buffer. if ((result = output) && ((st_valid_avail(opts) && st_avail_get(output) < (valid / 2)) || (!st_valid_avail(opts) && st_length_get(output) < (valid / 2)))) { log_pedantic("The output buffer supplied is not large enough to hold the result. {avail = %zu / required = %zu}", st_valid_avail(opts) ? st_avail_get(output) : st_length_get(output), valid / 2); return NULL; } else if (!output && !(result = st_alloc(valid / 2))) { log_pedantic("The output buffer memory allocation request failed. {requested = %zu}", (valid / 2)); return NULL; } // Store the memory address where the output should be written. o = st_data_get(result); // Loop through the input buffer and translate valid characters into a binary octet. for (size_t i = 0; i < len; i++) { if (hex_valid_chr(*p)) { if (!c) { c = *p; } else { *o++ = hex_decode_chr(c, *p); c = 0; w++; } } p++; } // If an output buffer was supplied that is capable of tracking the data length, or a managed string buffer was allocated update the length param. if (!output || st_valid_tracked(opts)) { st_length_set(result, w); } return result; }
// Check that we can't specify a length greater than the available buffer. bool_t check_string_logic(uint32_t check) { stringer_t *s; if (!(s = st_alloc(check, 128)) || st_length_set(s, st_avail_get(s) * 2) != st_avail_get(s)) { if (s) st_free(s); return false; } st_free(s); return true; }
ddb_dsp_context_t* st_open (void) { ddb_soundtouch_t *st = malloc (sizeof (ddb_soundtouch_t)); DDB_INIT_DSP_CONTEXT (st,ddb_soundtouch_t,&plugin); st->st = st_alloc (); st->changed = 1; st->tempo = 0; st->rate = 0; st->pitch = 0; st->use_aa_filter = 0; st->aa_filter_length = 32; st->use_quickseek = 0; st->sequence_ms = 82; st->seekwindow_ms = 28; return (ddb_dsp_context_t *)st; }
stringer_t * check_rand_sthread(void) { size_t len; uint64_t num = 0; stringer_t *buffer; if (!(buffer = st_alloc(RAND_CHECK_SIZE_MAX))) { return st_dupe(NULLER("Buffer allocation error.")); } for (int_t i = 0; status() && i < RAND_CHECK_ITERATIONS; i++) { num |= rand_get_int8(); num |= rand_get_int16(); num |= rand_get_int32(); num |= rand_get_int64(); num |= rand_get_uint8(); num |= rand_get_uint16(); num |= rand_get_uint32(); num |= rand_get_uint64(); // Pick a random length. len = (rand() % (RAND_CHECK_SIZE_MAX - RAND_CHECK_SIZE_MIN)) + RAND_CHECK_SIZE_MIN; if (rand_write(PLACER(st_char_get(buffer), len)) != len) { st_cleanup(buffer); return st_dupe(NULLER("Unable to fill the buffer with random data.")); } } st_cleanup(buffer); // This time through we use the choices function since it will allocate its own output buffer. for (int_t i = 0; status() && i < RAND_CHECK_ITERATIONS; i++) { // Pick a random length. len = (rand() % (RAND_CHECK_SIZE_MAX - RAND_CHECK_SIZE_MIN)) + RAND_CHECK_SIZE_MIN; if (!(buffer = rand_choices("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", len))) { return st_dupe(NULLER("Unable to fill the buffer with random data.")); } st_free(buffer); } return NULL; }
/** * @brief Convert a block of binary data into a hex string. * @param b a managed string containing the raw data to be encoded. * @param output if not NULL, a pointer to a managed string that will store the encoded output; if NULL, a new managed string will * be allocated and returned to the caller. * @return NULL on failure, or a pointer to a managed string containing the hex-encoded output on success. */ stringer_t * hex_encode_st(stringer_t *b, stringer_t *output) { size_t len = 0; uint32_t opts = 0; uchr_t *p = NULL, *o; stringer_t *result = NULL; if (output && !st_valid_destination((opts = *((uint32_t *)output)))) { log_pedantic("An output string was supplied but it does not represent a buffer capable of holding the output."); return NULL; } else if (st_empty_out(b, &p, &len)) { log_pedantic("The input block does not appear to hold any data ready for encoding. {%slen = %zu}", p ? "" : "p = NULL / ", len); return NULL; } // Make sure the output buffer is large enough or if output was passed in as NULL we'll attempt the allocation of our own buffer. if ((result = output) && ((st_valid_avail(opts) && st_avail_get(output) < (len * 2)) || (!st_valid_avail(opts) && st_length_get(output) < (len * 2)))) { log_pedantic("The output buffer supplied is not large enough to hold the result. {avail = %zu / required = %zu}", st_valid_avail(opts) ? st_avail_get(output) : st_length_get(output), len * 2); return NULL; } else if (!output && !(result = st_alloc(len * 2))) { log_pedantic("The output buffer memory allocation request failed. {requested = %zu}", len * 2); return NULL; } // Store the memory address where the output should be written. o = st_data_get(result); // Loop through the input buffer and write character pairs to the result string data buffer. for (size_t i = 0; i < len; i++) { hex_encode_chr(*p, o); o += 2; p += 1; } // If an output buffer was supplied that is capable of tracking the data length, or a managed string buffer was allocated update the length param. if (!output || st_valid_tracked(opts)) { st_length_set(result, len * 2); } return result; }
/** * @brief Allocate and initialize a new network buffer for a connection, if it is not already associated with one. * @note A network buffer of size magma.system.network_buffer bytes will be allocated for the connection if one does not exist. * @return true if the connection object has been associated with a network buffer or false on failure. */ bool_t con_init_network_buffer(connection_t *con) { if (!con) { return false; } else if (con->network.buffer) { return true; } if (!(con->network.buffer = st_alloc(magma.system.network_buffer))) { log_info("Unable to allocate a network buffer of %u bytes.", magma.system.network_buffer); return false; } con->network.line = pl_null(); return true; }
stringer_t * org_signet_get(prime_org_signet_t *org, stringer_t *output) { size_t length; int_t written = 0; stringer_t *result = NULL; if (!org || !(length = org_signet_length(org))) { log_pedantic("An invalid org signet was supplied for serialization."); return NULL; } // See if we have a valid output buffer, or if output is NULL, allocate a buffer to hold the output. else if (output && (!st_valid_destination(st_opt_get(output)) || st_avail_get(output) < length)) { log_pedantic("An output string was supplied but it does not represent a buffer capable of holding the output."); return NULL; } else if (!output && !(result = st_alloc(length))) { log_pedantic("Could not allocate a buffer large enough to hold encoded result. { requested = %zu }", length); return NULL; } else if (!output) { output = result; } st_wipe(output); // Calculate the size, by writing out all the fields (minus the header) using a NULL output. length = st_write(NULL, prime_field_write(PRIME_ORG_SIGNET, 1, ED25519_KEY_PUB_LEN, ed25519_public_get(org->signing, MANAGEDBUF(32)), MANAGEDBUF(34)), prime_field_write(PRIME_ORG_SIGNET, 3, SECP256K1_KEY_PUB_LEN, secp256k1_public_get(org->encryption, MANAGEDBUF(33)), MANAGEDBUF(35)), prime_field_write(PRIME_ORG_SIGNET, 4, ED25519_SIGNATURE_LEN, org->signature, MANAGEDBUF(65))); // Then output them again into the actual output buffer, but this time include the header. This is very primitive serialization logic. if ((written = st_write(output, prime_header_org_signet_write(length, MANAGEDBUF(5)), prime_field_write(PRIME_ORG_SIGNET, 1, ED25519_KEY_PUB_LEN, ed25519_public_get(org->signing, MANAGEDBUF(32)), MANAGEDBUF(34)), prime_field_write(PRIME_ORG_SIGNET, 3, SECP256K1_KEY_PUB_LEN, secp256k1_public_get(org->encryption, MANAGEDBUF(33)), MANAGEDBUF(35)), prime_field_write(PRIME_ORG_SIGNET, 4, ED25519_SIGNATURE_LEN, org->signature, MANAGEDBUF(65)))) != (length + 5)) { log_pedantic("The organizational signet didn't serialize to the expected length. { written = %i }", written); st_cleanup(result); return NULL; } return output; }
int st_asn1_allocate_context(st_asn1_context* pContext) { void* p; int len; len = st_asn1_get_context_size(); if (len < 0) return 0; p = st_alloc(len); if (p == NULL) return 0; if (st_asn1_allocate_context_ext(pContext, p, len)) ((unsigned char*)p)[0] = 1; /* Set allocated flag */ else st_free(p); return 1; }
rt_public void eif_extend_object_id_stack (EIF_INTEGER nb_chunks) /* extends of `nb_chunks the size of `object_id_stack' */ { #ifdef ISE_GC RT_GET_CONTEXT struct stack *st = &object_id_stack; char **top; struct stchunk * current; char **end; EIF_OBJECT_ID_LOCK; if (st->st_top == (char **) 0) { top = st_alloc(st, eif_stack_chunk); /* Create stack */ if (top == (char **) 0) { EIF_OBJECT_ID_UNLOCK; eraise ("Couldn't allocate object id stack", EN_MEM); } /* No memory */ st->st_top = top; /* Update new top */ } current = st->st_cur; /* save previous current stchunk */ top = st->st_top; /* save previous top of stack */ end = st->st_end; /*save previous st_end of stack */ SIGBLOCK; /* Critical section */ while (--nb_chunks) { if (-1 == st_extend(st, eif_stack_chunk)) { EIF_OBJECT_ID_UNLOCK; eraise ("Couldn't allocate object id stack", EN_MEM); } } st->st_cur = current; /* keep previous Current */ st->st_top = top; /* keep previous top */ st->st_end = end; SIGRESUME; /* End of critical section */ EIF_OBJECT_ID_UNLOCK; #endif }
sm_t *sm_alloc(const enum eSM_Type t, const size_t n) { struct STConf f; smf_setup(t, &f); return (sm_t *)st_alloc(&f, n); }
struct yasm *yasm_alloc(const char *pttfile) { char *tmp; size_t n; struct yasm *y; if (bug_on(!pttfile)) return NULL; y = calloc(1, sizeof(*y)); if (!y) return NULL; y->fl = fl_alloc(); if (!y->fl) goto error; y->st_asm = st_alloc(); if (!y->st_asm) goto error; y->fileroot = duplicate_str(pttfile); if (!y->fileroot) goto error; y->pttfile = duplicate_str(pttfile); if (!y->pttfile) goto error; tmp = strrchr(y->fileroot, '.'); if (tmp) *tmp = '\0'; tmp = strrchr(y->fileroot, path_separator); if (tmp) { tmp += 1; memmove(y->fileroot, tmp, strlen(tmp)+1); } y->binfile = malloc(strlen(y->fileroot)+strlen(bin_suffix)+1); if (!y->binfile) goto error; y->lstfile = malloc(strlen(y->fileroot)+strlen(lst_suffix)+1); if (!y->lstfile) goto error; n = strlen(y->fileroot); strcpy(y->binfile, y->fileroot); strcpy(y->binfile+n, bin_suffix); strcpy(y->lstfile, y->fileroot); strcpy(y->lstfile+n, lst_suffix); y->l = l_alloc(); if (!y->l) goto error; return y; error: yasm_free(y); return 0; }
/** * @brief Extract the contents of a quoted string and advance the position of the parser stream. * @note This function scans a string beginning with '"' for a terminating '"', returning an error if \r or \n is encountered first. * It is also able to handle escaped characters. * The supplied start and length pointers will be updated to reflect the input stream if the quoted string is parsed successfully. * @param output the address of a managed string that will receive a copy of the quoted string's contents on success, or NULL on failure or if it is zero length. * @param start the address of a pointer to the start of the buffer to be parsed (beginning with \"), that will also be updated to * point to the next argument in the sequence on success. * @param length a pointer to a size_t variable that contains the length of the string to be parsed, and which will be updated to reflect * the length of the remainder of the input string that follows the parsed quoted string. * @return -1 on general error or if an enclosing pair of double quotes was not found, or 1 if the supplied quoted string was valid. */ int_t imap_parse_qstring(stringer_t **output, chr_t **start, size_t *length) { chr_t *writer; chr_t *holder; size_t left; int_t escape = 0; stringer_t *result; // Get setup. holder = *start; left = *length; *output = NULL; // Skip the opening quote. if (*holder != '"' || !left) { return -1; } else { holder++; left--; } // Advance until we have a break character. Technically holder should be less than 127, but because were using a signed char, greater values are below NULL. while (left && *holder > '\0' && *holder != '\r' && *holder != '\n' && (escape == 1 || *holder != '"')) { if (escape == 0 && *holder == '\\') { escape = 1; } else if (escape != 0) { escape = 0; } holder++; left--; } // Check for valid data. if (*holder != '"') { return -1; } // Check for empty quoted strings. We return 1, but we also return a NULL pointer. if ((*start) + 1 == holder) { // Advance to the next argument. if (left) { holder++; left--; } // There should be a space before the next argument. if (left && *holder == ' ') { holder++; left--; } // Update *start = holder; *length = left; return 1; } // Allocate a buffer for the output. if (!(result = st_alloc(holder - *start))) { log_pedantic("Unable to allocate a buffer for the quoted string."); return -1; } // Get setup for the copy. writer = st_char_get(result); holder = *start; left = *length; // Skip the open quote again. holder++; left--; // Advance until we have a break character. Technically holder should be less than 127, but because were using a signed char, greater values are below NULL. while (left && *holder > '\0' && *holder != '\r' && *holder != '\n' && (escape == 1 || *holder != '"')) { if (escape == 0 && *holder == '\\') { escape = 1; } else if (escape != 0) { escape = 0; } // Copy unless we have the escape character. if (!escape) { *writer = *holder; writer++; } holder++; left--; } st_length_set(result, writer - (chr_t *)st_char_get(result)); // Advance to the next argument. if (left) { holder++; left--; } // There should be a space before the next argument. if (left && *holder == ' ') { holder++; left--; } // Update *start = holder; *length = left; *output = result; return 1; }
/** * @brief Perform QP (quoted-printable) decoding of a string. * @param s the managed string containing data to be decoded. * @return a pointer to a managed string containing the 8-bit decoded output, or NULL on failure. */ stringer_t * qp_decode(stringer_t *s) { uchr_t *p, *o; stringer_t *output; size_t len, written = 0; if (st_empty_out(s, &p, &len)) { log_pedantic("An empty string was passed in for decoding."); return NULL; } // Allocate one byte for printable characters and three bytes for non-printable characters. if (!(output = st_alloc(len))) { log_pedantic("Could not allocate a buffer large enough to hold decoded result. {requested = %zu}", len); return NULL; } // Get setup. o = st_data_get(output); #ifdef MAGMA_PEDANTIC // In pedantic mode we perform an extra check to make sure the loop doesn't loop past zero. while (len && len <= st_length_get(s)) { #else while (len) { #endif // Advance past the trigger. if (*p == '=') { len--; p++; // Valid hex pair. if (len >= 2 && hex_valid_chr(*p) && hex_valid_chr(*(p + 1))) { *o++ = hex_decode_chr(*p, *(p + 1)); written++; len -= 2; p += 2; } // Soft line breaks are signaled by a line break following an equal sign. else if (len >= 2 && *p == '\r' && *(p + 1) == '\n') { len -= 2; p += 2; } else if (len >= 1 && *p == '\n') { len--; p++; } // Equal signs which aren't followed by a valid hex pair or a line break are illegal, but if the character is printable // we can let through the original sequence. else if (len >= 1 && ((*p >= '!' && *p <= '<') || (*p >= '>' && *p <= '~'))) { *o++ = '='; *o++ = *p++; written += 2; len--; } // Characters outside the printable range are simply skipped. else if (len >= 1) { len--; p++; } } // Let through any characters found inside this range. else if ((*p >= '!' && *p <= '<') || (*p >= '>' && *p <= '~')) { *o++ = *p++; written++; len--; } // Characters outside the range above should have been encoded. Any that weren't should be skipped. else { len--; p++; } } // We allocated a default string buffer, which means the length is tracked so we need to set the data length. st_length_set(output, written); return output; }
/** * @brief Extract the contents of a literal string and advance the position of the parser stream. * @note This function expects as input a string beginning with '{' and followed by a numerical string, an optional '+', and a closing '}'. After reading in the numerical size parameter, it then attempts to read in that many bytes of input from the network stream. * @param con the client IMAP connection passing the literal string as input to the server. * @param output the address of a managed string that will receive a copy of the literal string's contents on success, or NULL on failure or if it is zero length. * @param start the address of a pointer to the start of the buffer to be parsed (beginning with '{'), that will also be updated to * point to the next argument in the sequence on success. * @param length a pointer to a size_t variable that contains the length of the string to be parsed, and that will be updated to reflect * the length of the remainder of the input string that follows the parsed literal string. * @return -1 on general or parse error or if an enclosing pair of double quotes was not found, or 1 if the supplied quoted string was valid. */ int_t imap_parse_literal(connection_t *con, stringer_t **output, chr_t **start, size_t *length) { chr_t *holder; int_t plus = 0; stringer_t *result; size_t characters, left; ssize_t nread; uint64_t literal, number; // Get setup. holder = *start; left = *length; *output = NULL; // Skip the opening bracket. if (*holder != '{' || !left) { return -1; } else { holder++; left--; } // Advance until we have a break character. while (left && *holder >= '0' && *holder <= '9') { holder++; left--; } // Store the length. characters = holder - *start - 1; if (left && *holder == '+') { plus = 1; holder++; left--; } if (*holder != '}' || !characters) { return -1; } // Convert to a number. Make sure the number is positive. if (!uint64_conv_bl(*start + 1, characters, &number)) { return -1; } literal = (size_t)number; // If the number is larger than 128 megabytes, then reject it. if (!plus && number > 134217728) { return -1; } // They client is already transmitting, so read the entire file, then reject it. else if (number > 134217728) { while (number > 0) { // Read the data. if ((nread = con_read(con)) <= 0) { log_pedantic("The connection was dropped while reading the literal."); return -1; } // Deal with signedness problem. characters = nread; if (number > (uint64_t)characters) { number -= characters; } else { // If we have any extra characters in the buffer, move them to the beginning. if ((uint64_t)characters > number) { mm_move(st_char_get(con->network.buffer), st_char_get(con->network.buffer) + number, characters - number); st_length_set(con->network.buffer, characters - number); con->network.line = line_pl_st(con->network.buffer, 0); } else { st_length_set(con->network.buffer, 0); con->network.line = pl_null(); } // Make sure we have a full line. if (pl_empty(con->network.line) && con_read_line(con, true) <= 0) { log_pedantic("The connection was dropped while reading the literal."); return -1; } number = 0; } } return -1; } // If this is not a plus literal, output the proceed statement. if (!plus) { con_write_bl(con, "+ GO\r\n", 6); } // Handle the special case of a zero length literal. if (literal == 0) { // Read the next line. if (con_read_line(con, true) <= 0) { log_pedantic("The connection was dropped while reading the literal."); return -1; } *start = st_char_get(con->network.buffer); *length = pl_length_get(con->network.line); // There should be a space before the next argument. if (*length && **start == ' ') { (*start)++; (*length)--; } return 1; } // Allocate a stringer for the buffer. if (!(result = st_alloc(literal))) { log_pedantic("Unable to allocate a buffer of %lu bytes for the literal argument.", literal); return -1; } // So we know how many more characters to read. left = literal; // Where we put the data. holder = st_char_get(result); // Keep looping until we run out of data. while (left) { // Read the data. if ((nread = con_read(con)) <= 0) { log_pedantic("The connection was dropped while reading the literal."); st_free(result); return -1; } characters = nread; // If we have a buffer, copy the data into the buffer. mm_copy(holder, st_char_get(con->network.buffer), (left > characters) ? characters : left); if (left > characters) { holder += characters; left -= characters; } else { st_length_set(result, literal); // If we have any extra characters in the buffer, move them to the beginning. if (characters > left) { mm_move(st_char_get(con->network.buffer), st_char_get(con->network.buffer) + left, characters - left); st_length_set(con->network.buffer, characters - left); con->network.line = line_pl_st(con->network.buffer, 0); } else { st_length_set(con->network.buffer, 0); con->network.line = pl_null(); } // Make sure we have a full line. if (pl_empty(con->network.line) && con_read_line(con, true) <= 0) { log_pedantic("The connection was dropped while reading the literal."); st_free(result); return -1; } left = 0; } } *start = st_char_get(con->network.buffer); *length = pl_length_get(con->network.line); // There should be a space before the next argument. if (*length && **start == ' ') { (*start)++; (*length)--; } if (result != NULL) { *output = result; } else { return -1; } return 1; }
// LOW: Function could use some serious cleanup; NEED TO TEST THIS IN UNIT TESTS int_t st_replace(stringer_t **target, stringer_t *pattern, stringer_t *replacement) { stringer_t *output; uchr_t *tptr, *optr, *rptr, *pptr; size_t hits = 0, tlen, plen, rlen, olen; // replacement can be blank but it can't be null if (!target || st_empty_out(*target, &tptr, &tlen) || st_empty_out(pattern, &pptr, &plen) || (st_empty_out(replacement, &rptr, &rlen) && !st_char_get(replacement))) { log_pedantic("Sanity check failed. Passed a NULL pointer."); return -1; } // Check to make sure the target is big enough to hold the pattern. if (tlen < plen) { // log_pedantic("The target isn't long enough to contain the pattern."); return 0; } // Increment through the entire target and find out how many times the pattern is present. for (size_t i = 0; i <= (tlen - plen); i++) { if (!st_cmp_cs_starts(PLACER(tptr++, tlen - i), pattern)) { hits++; i += plen - 1; tptr += plen - 1; } } // Did we get any hits? Or if the output length would be zero, return. // QUESTION: Should 2nd part of conditional ever be necessary? tlen - (plen * hits) can't be less than zero, // and hits has to be positive. So the expression will always evaluate positive. // QUESTION: Shouldn't we allow the output length to be zero? /*if (!hits || !(olen = tlen - (plen * hits) + (rlen * hits))) { return hits; }*/ if (!hits) { return 0; } olen = tlen - (plen * hits) + (rlen * hits); // If our new string is now empty we truncate the original target and return it. if (!olen) { *((char *)(st_data_get(*target))) = 0; st_length_set(*target, 0); return hits; } // Allocate a new stringer. if (!(output = st_alloc(olen))) { log_pedantic("Could not allocate %zu bytes for the new string.", olen); // QUESTION: -3? return -3; } // Setup. tptr = st_data_get(*target); optr = st_data_get(output); // Increment through the entire target and copy the bytes. for (size_t i = 0; i <= tlen; i++) { if (i <= (tlen - plen) && !st_cmp_cs_starts(PLACER(tptr, tlen - i), pattern)) { i += plen - 1; tptr += plen; for (size_t j = 0; j < rlen; j++) { *optr++ = *(rptr + j); } } else { *optr++ = *tptr++; } } if (st_valid_free(*((uint32_t *)(*target)))) { st_free(*target); } st_length_set(output, olen); *target = output; return hits; }
rt_public void evpush(va_alist) #endif { /* Push on the local variable stack the number of values indicated * by the first (int) argument. If all the values cannot successively * be pushed, we attempt to use the secure arena which should have been * pre-allocated for that purpose (we can't really call the GC before all * the locals have been pushed). */ EIF_GET_CONTEXT int n; /* Number of elements to be pushed */ char **top; /* The current top of the stack */ #ifdef EIF_WINDOWS va_list ap; /* The variable argument list */ #else va_list ap; /* The variable argument list */ #endif int i; /* Number of slots until end of chunk */ struct stack *stk; /* The local stack pointer */ #ifndef I_STDARG va_start(ap); n = va_arg(ap, int); #else va_start (ap, count); n = count; /* First argument is the number of items */ #endif /* This function is going to be called many many times, so it has to be * as mostly efficient as possible. Hence, the call to epush() which should * take place has been inlined. This also enables us to take advantage of * the secure arena at low costs--RAM. */ stk = &loc_stack; /* Load pointer in register for faster access */ top = stk->st_top; /* Current top of stack */ /* Create a new stack if none has been already allocated. If allocation * fails, we don't bother trying with the urgent memory stock: we must be * at the beginning of the process's lifetime and we are already out of * memory--RAM. */ if (top == (char **) 0) { /* No stack yet? */ top = st_alloc(stk, eif_stack_chunk); /* Create one */ if (top == (char **) 0) /* Cannot allocate stack */ enomem(MTC_NOARG); /* Critical exception */ } /* Attempt an optimization: if there is enough room until the end of the * current stack chunk, then we're able to do our job very efficiently. */ i = stk->st_end - top; /* # of slots until the end */ if (i >= n) { /* Enough room to do it once?*/ while (n--) /* Yes! Do it the fast way */ *top++ = va_arg(ap, char *); /* Push local address on stack */ stk->st_top = top; /* Update stack's top */ va_end(ap); /* End processing of argument list */ return; /* All done */ } /* We have to do it manually. Stack extension is done as needed. If it is * not possible to push a value on the stack, a critical "Out of memory" * exception is raised. We also need to protect against signals. */ SIGBLOCK; /* Entering critical section */ while (n > 0) { while (i-- > 0 && n-- > 0) /* Note the double & */ *top++ = va_arg(ap, char *); /* Push local address on stack */ if (n > 0) { /* Reached end of chunk */ if (stk->st_cur == stk->st_tl) { /* Last chunk */ if (-1 == extend(&loc_stack)) { /* Cannot extend stack at all */ va_end(ap); /* End processing of list */ enomem(MTC_NOARG); /* Critical exception */ } top = stk->st_top; /* Update new top */ } else { /* Go to next chunk */ struct stchunk *c; /* New current chunk */ c = stk->st_cur = stk->st_cur->sk_next; top = stk->st_top = c->sk_arena; stk->st_end = c->sk_end; } i = stk->st_end - top; /* # of slots until the end */ } } stk->st_top = top; /* Ensure top is up-to-date */ va_end(ap); /* End processing of argument list */ SIGRESUME; /* Leaving critical section */ }
/** * @brief Build a spam text signature for insertion into a message body. * @param server the server object of the web server where the teacher application is hosted. * @param type MESSAGE_TYPE_HTML to generate an html-based signature, or any other value for plain text. * @param content_encoding if MESSAGE_ENCODING_QUOTED_PRINTABLE, set the encoding type to qp. * @param signum the spam signature number referenced by the teacher url. * @param sigkey the spam signature key for client verification in the teacher app. * @param disposition if 0, the message disposition is "innocent"; otherwise, the disposition specifies "spam". * @return NULL on failure, or a newly allocated managed string containing the desired mail signature on success. */ stringer_t * mail_build_signature(server_t *server, int_t content_type, int_t content_encoding, uint64_t signum, uint64_t sigkey, int_t disposition) { size_t length; stringer_t *holder; stringer_t *result = NULL; stringer_t *spam_text = NULL; static const chr_t *line = "\r\n____________________________________________________________________________________\r\n"; // Build the spam signature. if (signum && sigkey) { // This is the format for spam signatures. if (content_type == MESSAGE_TYPE_HTML) { holder = st_merge("nnnnnsnsn", "\r\n<div style='display: block; text-transform: none; text-indent: 0px; letter-spacing: normal; " "line-height: normal; text-align: left; white-space: normal; height: auto; visibility: visible; border-bottom: 1px solid black; border-collapse: collapse; " "width: 50em; font-size: 12px; color: black; font-family: sans-serif; padding: 0px 0px 4px 0px; clear: both; font-weight: normal;" "text-decoration: none; background: white; ", "margin: 20px 2px 10px 2px; border-top: 1px solid black;", "'>\r\n\tUse the link below to report this message as ", !disposition ? "spam" : "innocent", ".<br />\r\n\t<a style='font-size: 12px; font-family: sans-serif; color: blue; background: white; text-decoration: underline; font-weight: normal;' href='https://", server->domain, "/apps/teacher?sig=%lu&key=%lu'>https://", server->domain, "/apps/teacher?sig=%lu&key=%lu</a>\r\n</div>\r\n"); } else { holder = st_merge("nnsn", "Use the link below to report this message as ", !disposition ? "spam.\r\nhttps://" : "innocent.\r\nhttps://", server->domain, "/apps/teacher?sig=%lu&key=%lu"); } if (!holder) { log_pedantic("Unable to build the spam signature."); return NULL; } // How big is the signature with the numbers inserted. length = st_length_get(holder) + uint64_digits(signum) + uint64_digits(sigkey) + uint64_digits(signum) + uint64_digits(sigkey); if (!(spam_text = st_alloc(length))) { log_pedantic("Unable to build the spam signature."); st_free(holder); return NULL; } // Print the spam signature text. if (content_type == MESSAGE_TYPE_HTML) { length = st_sprint(spam_text, st_char_get(holder), signum, sigkey, signum, sigkey); } else { length = st_sprint(spam_text, st_char_get(holder), signum, sigkey); } st_free(holder); if (!length) { log_pedantic("Unable to build the spam signature."); st_free(spam_text); return NULL; } } // Error check. if (!spam_text) { log_pedantic("Could not build the signature."); return NULL; } // Build the output signature. if (content_type == MESSAGE_TYPE_HTML) { result = spam_text; spam_text = NULL; } else { result = st_merge("nnsn", line, (spam_text == NULL) ? "" : line, spam_text, line); } if (!result) { log_pedantic("An error occurred while attempting to merge the signature."); } // Cleanup. st_cleanup(spam_text); // Check for quoted printable encodings. if (content_encoding == MESSAGE_ENCODING_QUOTED_PRINTABLE) { holder = qp_encode(result); if (holder && result) { st_free(result); result = holder; } } return result; }
/** * @brief Extract an html tag from a data buffer, searching backwards. * @warning Remember that this function takes the end and not the start of a buffer! * @note The extracted tag contains the enclosing brackets and only printable characters (no whitespace). * @param stream the end of a data buffer containing the html data to be parsed. * @param length the size, in bytes, of the data buffer to be parsed. * @return NULL on failure, or a managed string containing the printable contents of the nearest html tag on success. */ stringer_t * mail_extract_tag(chr_t *stream, size_t length) { chr_t *holder; size_t amount = 0; stringer_t *tag; if (*stream != '>') { return NULL; } // How long is this tag. while (length && *stream != '<') { length--; stream--; amount++; } // Did we hit the end of the stream or did we hit the beginning of the tag. if (!length) { return NULL; } else { amount++; } // Allocate a buffer. if (!(tag = st_alloc(amount))) { log_pedantic("Unable to allocate a stringer of %zu bytes to hold the HTML tag.", amount); return NULL; } // Get setup. length = 0; holder = st_char_get(tag); // QUESTION: Without folding whitespace?? Without any whitespace! // This returns the tag without folding whitespace. while (amount) { if (*stream <= '~' && *stream >= '!') { *holder = *stream; stream++; holder++; length++; } else { stream++; } amount--; } // This should never happen. if (!length) { st_free(tag); return NULL; } st_length_set(tag, length); return tag; }
/** * @brief Encode data in a human readable way, for debugging purposes. * @note All printable ASCII data will be displayed as-is; all other characters will be shown as formatted hex pairs. * @param input a pointer to a managed string containing the data to be formatted. * @param maxlen the maximum number of bytes to be displayed. If more characters are available, an ellipsis will be shown in the middle of the string * to represent the abridged data, with only the leading and trailing characters returned as part of the display string. * @return NULL on failure, or a pointer to a newly allocated managed string containing the encoded debug data on success. */ stringer_t *hex_encode_st_debug(stringer_t *input, size_t maxlen) { stringer_t *result; uchr_t *iptr; chr_t *rptr, *rstart; size_t total, pass_len; // Not the most efficient, but this is mostly for internal debugging purposes anyhow total = (maxlen * 4) + 16; if (!(result = st_alloc(total))) { log_error("Unable to allocate space for debug string."); return NULL; } rstart = rptr = st_char_get(result); iptr = st_uchar_get(input); *rptr++ = '['; if (maxlen >= st_length_get(input)) { pass_len = st_length_get(input); } else { pass_len = maxlen / 2; } // First pass happens regardless. for (size_t i = 0; i < pass_len; i++) { if (chr_printable(*iptr) || (*iptr == '\r') || (*iptr == '\n') || (*iptr == '\t')) { *rptr++ = (chr_t)*iptr++; } else { *rptr++ = '\\'; *rptr++ = 'x'; hex_encode_chr(*iptr++, (uchr_t *)rptr); rptr += 2; } } // Second pass only if there's another round. if (pass_len != st_length_get(input)) { *rptr++ = ' '; *rptr++ = '.'; *rptr++ = '.'; *rptr++ = '.'; *rptr++ = ' '; iptr = st_uchar_get(input) + st_length_get(input) - (maxlen / 2); for (size_t i = 0; i < pass_len; i++) { if (chr_printable(*iptr) || (*iptr == '\r') || (*iptr == '\n') || (*iptr == '\t')) { *rptr++ = (chr_t)*iptr++; } else { *rptr++ = '\\'; *rptr++ = 'x'; hex_encode_chr(*iptr++, (uchr_t *)rptr); rptr += 2; } } } *rptr++ = ']'; *rptr = 0; st_length_set(result, (size_t)(rptr - rstart)); return result; }
/** * @brief Decode a URL-encoded string into its original representation. * @param s a managed string containing the UR componentL to be decoded. * @return NULL on failure, or a freshly allocated managed string containing the original data represented by the URL-encoded input on success. */ stringer_t *url_decode(stringer_t *s) { uchr_t *p, *o; stringer_t *output; size_t len, written = 0; if (st_empty_out(s, &p, &len)) { log_pedantic("An empty string was passed in for decoding."); return NULL; } // Allocate one byte for printable characters and three bytes for non-printable characters. if (!(output = st_alloc(len))) { log_pedantic("Could not allocate a buffer large enough to hold decoded result. {requested = %zu}", len); return NULL; } // Get setup. o = st_data_get(output); #ifdef MAGMA_PEDANTIC // In pedantic mode we perform an extra check to make sure the loop doesn't loop past zero. while (len && len <= st_length_get(s)) { #else while (len) { #endif // Advance past the trigger. if (*p == '%') { len--; p++; // Valid hex pair. if (len >= 2 && hex_valid_chr(*p) && hex_valid_chr(*(p + 1))) { *o++ = hex_decode_chr(*p, *(p + 1)); written++; len -= 2; p += 2; } // Percent signs that aren't followed by a valid hex pair are invalid, but in the interest of compatibility we'll simply let // those characters through. else if (len >= 1) { *o++ = '%'; *o++ = *p++; written += 2; len--; } } // Characters not prefixed by a percent sign are simply copied into the output buffer. else { *o++ = *p++; written++; len--; } } // We allocated a default string buffer, which means the length is tracked so we need to set the data length. st_length_set(output, written); return output; }
/** * @brief Calculates the legacy symmetric encryption key and authentication token. * @param username a managed string holding the sanitzed username. * @param password a managed string holding the plain text user password. * @return an auth_legacy_t structure is returned upon success, and NULL upon failure. **/ auth_legacy_t * auth_legacy(stringer_t *username, stringer_t *password) { auth_legacy_t *legacy = NULL; stringer_t *input = NULL, *intermediate = MANAGEDBUF(64); // Make sure all three required inputs are valid pointers and hold at least one character. if (st_empty(username) || st_empty(password) || st_empty(magma.secure.salt)) { log_error("A variable needed to calculate the legacy authentication and encryption values was invalid."); return NULL; } else if (!(legacy = auth_legacy_alloc())) { log_error("We were unable to allocate a buffer to hold the legacy hash values."); return NULL; } // Combine the three inputs into a single buffer. else if (!(input = st_merge("sss", username, magma.secure.salt, password))) { log_error("Unable to combine the three input values needed to calculate the legacy authentication and encryption values."); auth_legacy_free(legacy); return NULL; } // Hash the inputs together and we'll get the legacy symmetric encryption key. else if (!(legacy->key = st_alloc_opts(MANAGED_T | CONTIGUOUS | SECURE, 64)) || !(hash_sha512(input, legacy->key))) { log_error("Unable to calculate the legacy hash values."); auth_legacy_free(legacy); st_free(input); return NULL; } // Free and reuse the holder variable. st_free(input); // Prepare the inputs for the intermediary hash. if (!(input = st_merge("ss", password, legacy->key))) { log_error("Failed to merge the legacy authentication inputs for the intermediate hash round."); auth_legacy_free(legacy); return NULL; } // Hash the password with the output of the first round. Note that if the hash function returns NULL and overwrites // the intermediate string pointer, the buffer will be freed automatically because it was allocated off the stack. else if (!(hash_sha512(input, intermediate))) { log_error("Unable to calculate the legacy hash values."); auth_legacy_free(legacy); st_free(input); return NULL; } // Free and reuse the holder variable. st_free(input); // Prepare the inputs for the intermediary hash. if (!(input = st_merge("ss", password, intermediate))) { log_error("Failed to merge the legacy authentication inputs for the final hash round."); auth_legacy_free(legacy); return NULL; } // Hash the inputs together and we'll get the legacy authentication token. if (!(legacy->token = st_alloc(64)) || !(hash_sha512(input, legacy->token))) { log_error("Failed to merge the legacy authentication inputs for the final hash round."); auth_legacy_free(legacy); st_free(input); return NULL; } // Free the inputs. st_free(input); // And return success. return legacy; }
stringer_t * hash_digest(digest_t *digest, stringer_t *s, stringer_t *output) { int_t olen; uint_t rlen; uint32_t opts = 0; EVP_MD_CTX ctx; stringer_t *result = NULL; // Ensure a digest pointer was passed in and that we can retrieve the output length. if (!digest || (olen = EVP_MD_size_d((const EVP_MD *)digest)) <= 0) { log_pedantic("The hash algorithm is missing or invalid."); return NULL; } else if (output && !st_valid_destination((opts = *((uint32_t *)output)))) { log_pedantic("An output string was supplied but it does not represent a buffer capable of holding a result."); return NULL; } else if (st_empty(s)) { log_pedantic("The input string does not appear to have any data ready for encoding. {%slen = %zu}", s ? "" : "s = NULL / ", s ? st_length_get(s) : 0); return NULL; } // Make sure the output buffer is large enough or if output was passed in as NULL we'll attempt the allocation of our own buffer. else if ((result = output) && ((st_valid_avail(opts) && st_avail_get(output) < olen) || (!st_valid_avail(opts) && st_length_get(output) < olen))) { log_pedantic("The output buffer supplied is not large enough to hold the result. {avail = %zu / required = %i}", st_valid_avail(opts) ? st_avail_get(output) : st_length_get(output), olen); return NULL; } else if (!output && !(result = st_alloc(olen))) { log_pedantic("The output buffer memory allocation request failed. {requested = %i}", olen); return NULL; } // Initialize the context. EVP_MD_CTX_init_d(&ctx); rlen = olen; // Setup the digest algorithm. if (EVP_DigestInit_ex_d(&ctx, (const EVP_MD *)digest, NULL) != 1) { log_pedantic("An error occurred while trying to initialize the hash context. {%s}", ssl_error_string(MEMORYBUF(256), 256)); EVP_MD_CTX_cleanup_d(&ctx); if (!output) { st_free(result); } return NULL; } // Process the input data. else if (EVP_DigestUpdate_d(&ctx, st_data_get(s), st_length_get(s)) != 1) { log_pedantic("An error occurred while trying to process the input data. {%s}", ssl_error_string(MEMORYBUF(256), 256)); EVP_MD_CTX_cleanup_d(&ctx); if (!output) { st_free(result); } return NULL; } // Retrieve the hash output. else if (EVP_DigestFinal_d(&ctx, st_data_get(result), &rlen) != 1) { log_pedantic("An error occurred while trying to retrieve the hash result. {%s}", ssl_error_string(MEMORYBUF(256), 256)); EVP_MD_CTX_cleanup_d(&ctx); if (!output) { st_free(result); } return NULL; } // Cleanup. EVP_MD_CTX_cleanup_d(&ctx); if (!output || st_valid_tracked(opts)) { st_length_set(result, rlen); } return result; }