/** * @brief Truncate a placer to start before any of the specified characters, and update the placer accordingly. * @param place a pointer to a placer that will be updated to be truncated before any of the specified characters. * @param shrinkchars a pointer to a buffer containing bytes that will be skipped when they are found at the end of the placer. * @param nchars the number of characters to be tested in the collection in shrinkchars. * @return true if the shrink operation completed before the end of the placer was reached, or false otherwise. */ bool_t pl_shrink_before_characters (placer_t *place, char *shrinkchars, size_t nchars) { char *ptr = pl_char_get(*place) + pl_length_get(*place) - 1; if (pl_empty(*place)) { return false; } for (int i = 0; i < place->length; i++, ptr--) { for (int j = 0; j <= nchars; j++) { // We went through all the skip characters without finding something... so this is where we return. if (j == nchars) { place->length -= i; return true; } if (*ptr == shrinkchars[j]) break; } } return false; }
stringer_t * imap_build_array_isliteral(placer_t data) { size_t length; chr_t *holder, buffer[32]; if (pl_empty(data)) { return NULL; } length = pl_length_get(data); holder = pl_data_get(data); // Look for illegal characters. for (size_t i = 0; i < length; i++) { if ((*holder >= '*' && *holder <= '~') || *holder == ' ' || *holder == '_' || *holder == '!' || (*holder >= '#' && *holder <= '&')) { holder++; } else { snprintf(buffer, 32, "{%zu}\r\n", length); return st_merge("ns", buffer, &data); } } return NULL; }
EC_KEY * deprecated_ecies_key_private(uint64_t format, placer_t data) { EC_KEY *key = NULL; BIGNUM *number = NULL; if (!(key = deprecated_ecies_key_alloc())) { log_info("Unable to allocate an empty key context."); return NULL; } // Process a key in binary format. if (format & ECIES_PRIVATE_BINARY) { if (!(number = BN_bin2bn_d(pl_data_get(data), pl_length_get(data), NULL))) { log_info("An error occurred while parsing the binary elliptical curve point data used to represent the private key. {%s}", ssl_error_string(MEMORYBUF(256), 256)); EC_KEY_free_d(key); return NULL; } } // Process a key in hex. else if (format & ECIES_PRIVATE_HEX) { if (!(BN_hex2bn_d(&number, pl_char_get(data)))) { log_info("An error occurred while parsing the binary elliptical curve point data used to represent the private key. {%s}", ssl_error_string(MEMORYBUF(256), 256)); EC_KEY_free_d(key); return NULL; } } // Invalid format! else { log_info("The private key data is using an unrecognized format."); EC_KEY_free_d(key); return NULL; } // Assign the point to our empty key instance. if (EC_KEY_set_private_key_d(key, number) != 1) { log_info("The provided point data does not represent a valid public key. {%s}", ssl_error_string(MEMORYBUF(256), 256)); EC_KEY_free_d(key); BN_free_d(number); return NULL; } // The above function call duplicates the point so the local copy is no longer needed. BN_free_d(number); return key; }
/** * @brief Count the number of times a string token is found in a specified block of memory. * @param block a pointer to a block of memory to be scanned. * @param length the length, in bytes, of the block of memory to be scanned. * @param token a pointer to the string token being used to split the input data. * @param toklen the length, in bytes, of the specified token. * @return the number of times the string token was found in the block of memory, or 0 on failure. */ uint64_t str_tok_get_count_bl(void *block, size_t length, chr_t *token, size_t toklen) { uint64_t count = 0; placer_t haystack, needle; size_t hptr, skipped = 0; // We can't search NULL pointers or empty strings. if (mm_empty(block, length) || mm_empty(token, toklen)) { return 0; } haystack = pl_init(block, length); needle = pl_init(token, toklen); while ((skipped < length) && st_search_cs(&haystack, &needle, &hptr)) { skipped += pl_length_get (needle) + hptr; haystack = pl_init((char *) block + skipped, length-skipped); count++; } return count; }
/** * @brief Retrieve a specified string-split token from a null-terminated string. * @param block a pointer to the block of memory to be tokenized. * @param length the maximum number of characters to be scanned from the input data. * @param token the token string that will be used to split the data. * @param toklen the length, in bytes, of the token string. * @param fragment the zero-indexed token number to be extracted from the data. * @param value a pointer to a placer that will receive the value of the extracted token on success, or pl_null() on failure. * @return -1 on failure, 0 on success, or 1 if the token was extracted successfully, but was the last one in the string. */ int str_tok_get_bl(char *block, size_t length, chr_t *token, size_t toklen, uint64_t fragment, placer_t *value) { placer_t haystack, needle; size_t hptr, skipped = 0; bool_t found; // We can't search NULL pointers or empty strings. if (!value || mm_empty(block, length) || mm_empty(token, toklen)) { *value = pl_null(); return -1; } haystack = pl_init(block, length); needle = pl_init(token, toklen); while (fragment) { if (!(found = st_search_cs(&haystack, &needle, &hptr))) { *value = pl_null(); return -1; } // Haystack becomes the entire block after the token. skipped += pl_length_get (needle) + hptr; haystack = pl_init(pl_char_get(haystack) + skipped, length-skipped); fragment--; } // If no more tokens are present, return everything we have left if (!st_search_cs(&haystack, &needle, &hptr)) { *value = haystack; return 1; } *value = pl_init(pl_char_get(haystack), hptr); return 0; }
EC_KEY * deprecated_ecies_key_public(uint64_t format, placer_t data) { EC_KEY *key = NULL; EC_POINT *point = NULL; if (!(key = deprecated_ecies_key_alloc())) { log_info("Unable to allocate an empty key context."); return NULL; } // Process a key in binary format. if (format & ECIES_PUBLIC_BINARY) { // Generate the empty point context we'll be assigning to below. if (!(point = EC_POINT_new_d(EC_KEY_get0_group_d(key)))) { log_info("An error occurred while allocate the elliptical curve point. {%s}", ssl_error_string(MEMORYBUF(256), 256)); EC_KEY_free_d(key); return NULL; } else if (EC_POINT_oct2point_d(EC_KEY_get0_group_d(key), point, pl_data_get(data), pl_length_get(data), NULL) != 1) { log_info("An error occurred while parsing the binary elliptical curve point data used to represent the public key. {%s}", ssl_error_string(MEMORYBUF(256), 256)); EC_POINT_free_d(point); EC_KEY_free_d(key); return NULL; } } // Process a key in hex. else if (format & ECIES_PUBLIC_HEX) { if (!(point = EC_POINT_hex2point_d(EC_KEY_get0_group_d(key), pl_char_get(data), NULL, NULL))) { log_info("An error occurred while parsing the binary elliptical curve point data used to represent the public key. {%s}", ssl_error_string(MEMORYBUF(256), 256)); EC_KEY_free_d(key); return NULL; } } // Invalid format! else { log_info("The public key data is using an unrecognized format."); EC_POINT_free_d(point); EC_KEY_free_d(key); return NULL; } // Assign the point to our empty key instance. if (EC_KEY_set_public_key_d(key, point) != 1) { log_info("The provided point data does not represent a valid public key. {%s}", ssl_error_string(MEMORYBUF(256), 256)); EC_POINT_free_d(point); EC_KEY_free_d(key); return NULL; } // The above function call duplicates the point so the local copy is no longer needed. EC_POINT_free_d(point); // Ensures the provided point is along the active elliptical curve and therefore represents a valid public key. if (EC_KEY_check_key_d(key) != 1) { log_info("The provided point data does not represent a valid public key. {%s}", ssl_error_string(MEMORYBUF(256), 256)); EC_KEY_free_d(key); return NULL; } return key; }
/** * @brief Get a placer pointing to the specified line ('\n' delimited) of another placer. * @param string a pointer to the placer to be scanned. * @param number the zero-based index of the line to be retrieved from the placer. * @return a null placer on failure, or a placer pointing to the specified line on success. */ placer_t line_pl_pl(placer_t string, uint64_t number) { return line_pl_bl(pl_char_get(string), pl_length_get(string), number); }
/** * @brief Read data from a network connection, and store the data in the connection context buffer. * @return -1 on general failure, -2 if the connection was reset, or the amount of data that was read. */ int64_t client_read(client_t *client) { int_t counter = 0; ssize_t bytes = 0; bool_t blocking = true; stringer_t *error = NULL; #ifdef MAGMA_PEDANTIC int_t local = 0; stringer_t *ip = NULL, *cipher = NULL; #endif if (!client || client->sockd == -1 || client_status(client) < 0) { return -1; } // Check for data past the current line buffer. else if (pl_length_get(client->line) && st_length_get(client->buffer) > pl_length_get(client->line)) { // Move the unused data to the front of the buffer. mm_move(st_data_get(client->buffer), st_data_get(client->buffer) + pl_length_get(client->line), st_length_get(client->buffer) - pl_length_get(client->line)); // Update the length. st_length_set(client->buffer, st_length_get(client->buffer) - pl_length_get(client->line)); // Clear the line buffer. client->line = pl_null(); } // Otherwise reset the buffer and line lengths to zero. else { st_length_set(client->buffer, 0); client->line = pl_null(); } // Loop until the buffer has data or we get an error. do { blocking = st_length_get(client->buffer) ? false : true; // Read bytes off the network. If data is already in the buffer this should be a non-blocking read operation so we can // return the already buffered data without delay. if (client->tls) { // If bytes is zero or below and the library isn't asking for another read, then an error occurred. bytes = tls_read(client->tls, st_char_get(client->buffer) + st_length_get(client->buffer), st_avail_get(client->buffer) - st_length_get(client->buffer), blocking); // If zero bytes were read, or a negative value was returned to indicate an error, call tls_erorr(), which will return // NULL if the error can be safely ignored. Otherwise log the output for debug purposes. if (bytes <= 0 && (error = tls_error(client->tls, bytes, MANAGEDBUF(512)))) { #ifdef MAGMA_PEDANTIC cipher = tls_cipher(client->tls, MANAGEDBUF(128)); ip = ip_presentation(client->ip, MANAGEDBUF(INET6_ADDRSTRLEN)); log_pedantic("TLS client read operation failed. { ip = %.*s / %.*s / result = %zi%s%.*s }", st_length_int(ip), st_char_get(ip), st_length_int(cipher), st_char_get(cipher), bytes, (error ? " / " : ""), st_length_int(error), st_char_get(error)); #endif client->status = -1; return -1; } // This will occur when the read operation results in a 0, or negative value, but TLS error returns NULL to // indicate it was a transient error. For transient errors we simply set bytes equal to 0 so the read call gets retried. else if (bytes <= 0) { bytes = 0; } } else { errno = 0; bytes = recv(client->sockd, st_char_get(client->buffer) + st_length_get(client->buffer), st_avail_get(client->buffer) - st_length_get(client->buffer), (blocking ? 0 : MSG_DONTWAIT)); // Check for errors on non-SSL reads in the traditional way. if (bytes <= 0 && tcp_status(client->sockd)) { #ifdef MAGMA_PEDANTIC local = errno; ip = ip_presentation(client->ip, MANAGEDBUF(INET6_ADDRSTRLEN)); log_pedantic("TCP client read operation failed. { ip = %.*s / result = %zi / error = %i / message = %s }", st_length_int(ip), st_char_get(ip), bytes, local, strerror_r(local, MEMORYBUF(1024), 1024)); #endif client->status = -1; return -1; } } // We actually read in data, so we need to update the buffer to reflect the amount of data it currently holds. if (bytes > 0) { st_length_set(client->buffer, st_length_get(client->buffer) + bytes); } } while (blocking && counter++ < 128 && !st_length_get(client->buffer) && status()); // If there is data in the buffer process it. Otherwise if the buffer is empty and the connection appears to be closed // (as indicated by a return value of 0), then return -1 to let the caller know the connection is dead. if (st_length_get(client->buffer)) { client->status = 1; } else if (!bytes) { client->status = 2; return -2; } return st_length_get(client->buffer); }
/** * @brief Read a line of input from a network connection. * @note This function handles reading data from both regular and ssl connections. * This function continually attempts to read incoming data from the specified connection until a \n terminated line of input is received. * If a new line is read, the length of that line is returned to the caller, including the trailing \n. * If the read returns -1 and wasn't caused by a syscall interruption or blocking error, -1 is returned, and the connection status is set to -1. * If the read returns 0 and wasn't caused by a syscall interruption or blocking error, -2 is returned, and the connection status is set to 2. * Once a \n character is reached, the length of the current line of input is returned to the user, and the connection status is set to 1. * @param con the network connection across which the line of data will be read. * @return -1 on general failure, -2 if the connection was reset, or the length of the current line of input, including the trailing new line character. */ int64_t con_read_line(connection_t *con, bool_t block) { ssize_t bytes = 0; int_t counter = 0; bool_t line = false; if (!con || con->network.sockd == -1 || con_status(con) < 0) { if (con) con->network.status = -1; return -1; } // Check for an existing network buffer. If there isn't one, try creating it. else if (!con->network.buffer && !con_init_network_buffer(con)) { con->network.status = -1; return -1; } // Check if we have received more data than just what is in the current line of input. else if (pl_length_get(con->network.line) && st_length_get(con->network.buffer) > pl_length_get(con->network.line)) { // If so, move the unused "new" data after the current line marker to the front of the buffer. mm_move(st_data_get(con->network.buffer), st_data_get(con->network.buffer) + pl_length_get(con->network.line), st_length_get(con->network.buffer) - pl_length_get(con->network.line)); // Update the buffer length. st_length_set(con->network.buffer, st_length_get(con->network.buffer) - pl_length_get(con->network.line)); // Check whether the data we just moved contains a complete line. if (!pl_empty((con->network.line = line_pl_st(con->network.buffer, 0)))) { con->network.status = 1; return pl_length_get(con->network.line); } } // Otherwise reset the buffer and line lengths to zero. else { st_length_set(con->network.buffer, 0); con->network.line = pl_null(); } // Loop until we get a complete line, an error, or the buffer is filled. do { // blocking = st_length_get(con->network.buffer) ? false : true; block = true; if (con->network.tls) { bytes = tls_read(con->network.tls, st_char_get(con->network.buffer) + st_length_get(con->network.buffer), st_avail_get(con->network.buffer) - st_length_get(con->network.buffer), block); } else { bytes = tcp_read(con->network.sockd, st_char_get(con->network.buffer) + st_length_get(con->network.buffer), st_avail_get(con->network.buffer) - st_length_get(con->network.buffer), block); } // We actually read in data, so we need to update the buffer to reflect the amount of unprocessed data it currently holds. if (bytes > 0) { st_length_set(con->network.buffer, st_length_get(con->network.buffer) + bytes); } else if (bytes == 0) { usleep(1000); } else { con->network.status = -1; return -1; } // Check whether we have a complete line before checking whether the connection was closed. if (!st_empty(con->network.buffer) && !pl_empty((con->network.line = line_pl_st(con->network.buffer, 0)))) { line = true; } } while (!line && block && counter++ < 128 && st_length_get(con->network.buffer) != st_avail_get(con->network.buffer) && status()); if (st_length_get(con->network.buffer) > 0) { con->network.status = 1; } return pl_length_get(con->network.line); }
/** * @brief Read a line of input from a network client session. * @return -1 on general failure, -2 if the connection was reset, or the length of the current line of input, including the trailing new line character. */ int64_t client_read_line(client_t *client) { ssize_t bytes = 0; int_t counter = 0; stringer_t *error = NULL; bool_t blocking = true, line = false; #ifdef MAGMA_PEDANTIC int_t local = 0; stringer_t *ip = NULL, *cipher = NULL; #endif if (!client || client->sockd == -1) { if (client) client->status = 1; return -1; } // Check for data past the current line buffer. else if (pl_length_get(client->line) && st_length_get(client->buffer) > pl_length_get(client->line)) { // Move the unused data to the front of the buffer. mm_move(st_data_get(client->buffer), st_data_get(client->buffer) + pl_length_get(client->line), st_length_get(client->buffer) - pl_length_get(client->line)); // Update the length. st_length_set(client->buffer, st_length_get(client->buffer) - pl_length_get(client->line)); // Check whether the data we just moved contains a complete line. if (!pl_empty((client->line = line_pl_st(client->buffer, 0)))) { client->status = 1; return pl_length_get(client->line); } } // Otherwise reset the buffer and line lengths to zero. else { st_length_set(client->buffer, 0); client->line = pl_null(); } // Loop until we get a complete line, an error, or the buffer is filled. do { // Read bytes off the network. Skip past any existing data in the buffer. if (client->tls) { // If bytes is zero or below and the library isn't asking for another read, then an error occurred. bytes = tls_read(client->tls, st_char_get(client->buffer) + st_length_get(client->buffer), st_avail_get(client->buffer) - st_length_get(client->buffer), blocking); // If zero bytes were read, or a negative value was returned to indicate an error, call tls_erorr(), which will return // NULL if the error can be safely ignored. Otherwise log the output for debug purposes. if (bytes <= 0 && (error = tls_error(client->tls, bytes, MANAGEDBUF(512)))) { #ifdef MAGMA_PEDANTIC cipher = tls_cipher(client->tls, MANAGEDBUF(128)); ip = ip_presentation(client->ip, MANAGEDBUF(INET6_ADDRSTRLEN)); log_pedantic("TLS client read operation failed. { ip = %.*s / %.*s / result = %zi%s%.*s }", st_length_int(ip), st_char_get(ip), st_length_int(cipher), st_char_get(cipher), bytes, (error ? " / " : ""), st_length_int(error), st_char_get(error)); #endif client->status = -1; return -1; } // This will occur when the read operation results in a 0, or negative value, but TLS error returns NULL to // indicate it was a transient error. For transient errors we simply set bytes equal to 0 so the read call gets retried. else if (bytes <= 0) { bytes = 0; } } else { errno = 0; bytes = recv(client->sockd, st_char_get(client->buffer) + st_length_get(client->buffer), st_avail_get(client->buffer) - st_length_get(client->buffer), (blocking ? 0 : MSG_DONTWAIT)); // Check for errors on non-SSL reads in the traditional way. if (bytes <= 0 && tcp_status(client->sockd)) { #ifdef MAGMA_PEDANTIC local = errno; ip = ip_presentation(client->ip, MANAGEDBUF(INET6_ADDRSTRLEN)); log_pedantic("TCP client read operation failed. { ip = %.*s / result = %zi / error = %i / message = %s }", st_length_int(ip), st_char_get(ip), bytes, local, strerror_r(local, MEMORYBUF(1024), 1024)); #endif client->status = -1; return -1; } } // We actually read in data, so we need to update the buffer to reflect the amount of data it currently holds. if (bytes > 0) { st_length_set(client->buffer, st_length_get(client->buffer) + bytes); } // Check whether we have a complete line before checking whether the connection was closed. if (!st_empty(client->buffer) && !pl_empty((client->line = line_pl_st(client->buffer, 0)))) { line = true; } } while (!line && counter++ < 128 && st_length_get(client->buffer) != st_avail_get(client->buffer) && status()); if (st_length_get(client->buffer) > 0) { client->status = 1; } return pl_length_get(client->line); }
/** * @brief Read data from a network connection, and store the data in the connection context buffer. * @note This function handles reading data from both regular and SSL connections. * If the connection's network buffer hasn't been allocated, it will be initialized. * @param con a pointer to the connection object from which the data will be read. * @return -1 on general failure, -2 if the connection was reset, or the amount of data that was read. */ int64_t con_read(connection_t *con) { ssize_t bytes = 0; int_t counter = 0; bool_t blocking = true; if (!con || con->network.sockd == -1 || con_status(con) < 0) { if (con) con->network.status = -1; return -1; } // Check for an existing network buffer. If there isn't one, try creating it. else if (!con->network.buffer && !con_init_network_buffer(con)) { con->network.status = -1; return -1; } // Check for data past the current line buffer. else if (pl_length_get(con->network.line) && st_length_get(con->network.buffer) > pl_length_get(con->network.line)) { // Move the unused data to the front of the buffer. mm_move(st_data_get(con->network.buffer), st_data_get(con->network.buffer) + pl_length_get(con->network.line), st_length_get(con->network.buffer) - pl_length_get(con->network.line)); // Update the length. st_length_set(con->network.buffer, st_length_get(con->network.buffer) - pl_length_get(con->network.line)); // Clear the line buffer. con->network.line = pl_null(); if (st_length_get(con->network.buffer)) { return st_length_get(con->network.buffer); } } // Otherwise reset the buffer and line lengths to zero. else { st_length_set(con->network.buffer, 0); con->network.line = pl_null(); } // Loop until the buffer has data or we get an error. do { // blocking = st_length_get(con->network.buffer) ? false : true; blocking = true; if (con->network.tls) { bytes = tls_read(con->network.tls, st_char_get(con->network.buffer) + st_length_get(con->network.buffer), st_avail_get(con->network.buffer) - st_length_get(con->network.buffer), blocking); } else { bytes = tcp_read(con->network.sockd, st_char_get(con->network.buffer) + st_length_get(con->network.buffer), st_avail_get(con->network.buffer) - st_length_get(con->network.buffer), blocking); } // We actually read in data, so we need to update the buffer to reflect the amount of unprocessed data it currently holds. if (bytes > 0) { st_length_set(con->network.buffer, st_length_get(con->network.buffer) + bytes); } else if (bytes == 0) { usleep(1000); } else { con->network.status = -1; return -1; } } while (blocking && counter++ < 128 && !st_length_get(con->network.buffer) && status()); // If there is data in the buffer process it. Otherwise if the buffer is empty and the connection appears to be closed // (as indicated by a return value of 0), then return -1 to let the caller know the connection is dead. if (st_length_get(con->network.buffer)) { con->network.status = 1; } return st_length_get(con->network.buffer); }
int64_t client_read(client_t *client) { ssize_t bytes; bool_t blocking; int sslerr; if (!client || client->sockd == -1) { client->status = -1; return -1; } // Check for data past the current line buffer. if (pl_length_get(client->line) && st_length_get(client->buffer) > pl_length_get(client->line)) { // Move the unused data to the front of the buffer. mm_move(st_data_get(client->buffer), st_data_get(client->buffer) + pl_length_get(client->line), st_length_get(client->buffer) - pl_length_get(client->line)); // Update the length. st_length_set(client->buffer, st_length_get(client->buffer) - pl_length_get(client->line)); // Clear the line buffer. client->line = pl_null(); } // Otherwise reset the buffer and line lengths to zero. else { st_length_set(client->buffer, 0); client->line = pl_null(); } // Loop until the buffer has data or we get an error. do { blocking = st_length_get(client->buffer) ? false : true; // Read bytes off the network. If data is already in the buffer this should be a non-blocking read operation so we can // return the already buffered data without delay. if (client->ssl) { bytes = ssl_read(client->ssl, st_char_get(client->buffer) + st_length_get(client->buffer), st_avail_get(client->buffer) - st_length_get(client->buffer), blocking); sslerr = SSL_get_error_d(client->ssl, bytes); } else { bytes = recv(client->sockd, st_char_get(client->buffer) + st_length_get(client->buffer), st_avail_get(client->buffer) - st_length_get(client->buffer), blocking ? 0 : MSG_DONTWAIT); } // Check for errors on SSL reads. if (client->ssl) { // If 0 bytes were read, and it wasn't related to a shutdown, or if < 0 was returned and there was no more data waiting, it's an error. if ((!bytes && sslerr != SSL_ERROR_NONE && sslerr != SSL_ERROR_ZERO_RETURN) || ((bytes < 0) && sslerr != SSL_ERROR_WANT_READ)) { client->status = -1; return -1; } // Check for errors on non-SSL reads in the traditional way. } else if (bytes < 0 && errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) { client->status = -1; return -1; } if (bytes > 0) { st_length_set(client->buffer, st_length_get(client->buffer) + bytes); // Or break out of the loop because we've been shutdown. } else if (!bytes) { break; } } while (status() && blocking && !st_length_get(client->buffer)); // If there is data in the buffer process it. Otherwise if the buffer is empty and the connection appears to be closed // (as indicated by a return value of 0), then return -1 to let the caller know the connection is dead. if (st_length_get(client->buffer)) { client->status = 1; } else if (!bytes) { client->status = 2; return -2; } return st_length_get(client->buffer); }
/** * @brief Read a line of input from a network client session. * * * @return * * */ int64_t client_read_line(client_t *client) { ssize_t bytes; bool_t line = false; int sslerr; if (!client || client->sockd == -1) { client->status = 1; return -1; } // Check for data past the current line buffer. if (pl_length_get(client->line) && st_length_get(client->buffer) > pl_length_get(client->line)) { // Move the unused data to the front of the buffer. mm_move(st_data_get(client->buffer), st_data_get(client->buffer) + pl_length_get(client->line), st_length_get(client->buffer) - pl_length_get(client->line)); // Update the length. st_length_set(client->buffer, st_length_get(client->buffer) - pl_length_get(client->line)); // Check whether the data we just moved contains a complete line. if (!pl_empty((client->line = line_pl_st(client->buffer, 0)))) { client->status = 1; return pl_length_get(client->line); } } // Otherwise reset the buffer and line lengths to zero. else { st_length_set(client->buffer, 0); client->line = pl_null(); } // Loop until we get a complete line, an error, or the buffer is filled. do { // Read bytes off the network. Skip past any existing data in the buffer. if (client->ssl) { bytes = ssl_read(client->ssl, st_char_get(client->buffer) + st_length_get(client->buffer), st_avail_get(client->buffer) - st_length_get(client->buffer), true); sslerr = SSL_get_error_d(client->ssl, bytes); } else { bytes = recv(client->sockd, st_char_get(client->buffer) + st_length_get(client->buffer), st_avail_get(client->buffer) - st_length_get(client->buffer), 0); } // Check for errors on SSL reads. if (client->ssl) { // If 0 bytes were read, and it wasn't related to a shutdown, or if < 0 was returned and there was no more data waiting, it's an error. if ((!bytes && sslerr != SSL_ERROR_NONE && sslerr != SSL_ERROR_ZERO_RETURN) || ((bytes < 0) && sslerr != SSL_ERROR_WANT_READ)) { client->status = -1; return -1; } } // Check for errors on non-SSL reads in the traditional way. else if (bytes < 0 && errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) { client->status = -1; return -1; } if (bytes > 0) { st_length_set(client->buffer, st_length_get(client->buffer) + bytes); } // Check whether we have a complete line before checking whether the connection was closed. if (!st_empty(client->buffer) && !pl_empty((client->line = line_pl_st(client->buffer, 0)))) { line = true; } // Otherwise if the connection has been closed (as indicated by a return value of 0) the line will never terminate. As such // the best course of action is to return an error code up the stack to indicate the disconnect. else if (!bytes) { client->status = 2; return -2; } } while (status() && !line && st_length_get(client->buffer) != st_avail_get(client->buffer)); if (st_length_get(client->buffer) > 0) { client->status = 1; } return pl_length_get(client->line); }
/** * @brief Retrieve a specified token from a placer. * @see token_get_ns() * @param string a pointer to the placer to be tokenized. * @param token the token character that will be used to split the placer. * @param fragment the zero-indexed token number to be extracted from the placer. * @param value a pointer to a placer that will receive the value of the extracted token on success, or pl_null() on failure. * @return -1 on failure, 0 on success, or 1 if the token was extracted successfully, but was the last one found in the placer. */ int tok_get_pl(placer_t string, char token, uint64_t fragment, placer_t *value) { return tok_get_bl(pl_data_get(string), pl_length_get(string), token, fragment, value); }
prime_org_signet_t * org_signet_set(stringer_t *org) { prime_field_t *field = NULL; prime_object_t *object = NULL; prime_org_signet_t *result = NULL; if (!(object = prime_unpack(org))) { log_pedantic("Unable to parse the PRIME organizational signet."); return NULL; } else if (object->type != PRIME_ORG_SIGNET) { log_pedantic("The object passed in was not an organizational signet."); prime_object_free(object); return NULL; } else if (!(result = org_signet_alloc())) { log_pedantic("Unable to allocate a PRIME organizational signet."); prime_object_free(object); return NULL; } // Public signing key, verify the length and import the ed25519 public key. else if (!(field = prime_field_get(object, 1)) || st_length_get(&(field->payload)) != 32 || !(result->signing = ed25519_public_set(&(field->payload)))) { log_pedantic("Unable to parse the PRIME organizational signing key."); prime_object_free(object); org_signet_free(result); return NULL; } // Public encryption key, verify the length and import the compressed secp256k1 public key. else if (!(field = prime_field_get(object, 3)) || st_length_get(&(field->payload)) != 33 || !(result->encryption = secp256k1_public_set(&(field->payload)))) { log_pedantic("Unable to parse the PRIME organizational encryption key."); prime_object_free(object); org_signet_free(result); return NULL; } // Self signature taken over the cryptographic fields, verify the length and import the ed25519 signature. else if (!(field = prime_field_get(object, 4)) || st_length_get(&(field->payload)) != 64 || !(result->signature = st_import(pl_data_get(field->payload), pl_length_get(field->payload)))) { log_pedantic("Unable to parse the PRIME organizational signet signature."); prime_object_free(object); org_signet_free(result); return NULL; } // We don't need the packed object context any more. prime_object_free(object); // Verify the signature. if (!org_signet_verify(result)) { log_pedantic("The PRIME organizational signet signature is invalid."); org_signet_free(result); return NULL; } return result; }
stringer_t * imap_parse_address_part(placer_t input) { chr_t *stream; int_t mode = 0; placer_t box, dom; stringer_t *address = NULL, *name = NULL, *output = NULL; size_t zero_len = 0, one_len = 0, two_len = 0, length; length = pl_length_get(input); stream = pl_data_get(input); while (length > 0) { if (mode == 0 && *stream == '\"') { mode = 1; } else if (mode == 0 && *stream == '<') { mode = 2; } else if (mode == 1 && *stream == '\"') { mode = 0; } else if (mode == 2 && *stream == '>') { mode = 0; } else if (mode == 0 && *stream != ' ') { zero_len++; } else if (mode == 1) { one_len++; } else if (mode == 2) { two_len++; } length--; stream++; } /*if (two_len != 0) { if ((address = st_alloc(two_len)) == NULL) { return NULL; } } if (one_len != 0) { if ((name = st_alloc(one_len + zero_len)) == NULL) { return NULL; } } if (zero_len != 0 && two_len == 0 && one_len == 0) { if ((address = st_alloc(zero_len)) == NULL) { return NULL; } }*/ if ((address = st_alloc(pl_length_get(input))) == NULL) { return NULL; } else if ((name = st_alloc(pl_length_get(input))) == NULL) { if (address) st_free(address); return NULL; } mode = 0; length = pl_length_get(input); stream = pl_data_get(input); while (length > 0) { if (mode == 0 && *stream == '\"') { mode = 1; } else if (mode == 0 && *stream == '<') { mode = 2; } else if (mode == 1 && *stream == '\"') { mode = 0; } else if (mode == 2 && *stream == '>') { mode = 0; } // No quoted part, no <> part. else if ((mode == 0 && one_len == 0 && two_len == 0) || // If there is a quoted part, but no <>, use the quoted part for name, and the <> for address, and ignore everything outside that. (mode == 0 && one_len != 0 && two_len == 0) || // Or the data is inside the <>. mode == 2) { // Email addresses shouldn't have spaces. if (*stream != ' ') imap_parse_address_put(address, *stream); } else if (mode == 0 || mode == 1) { // Keeps any leading spaces from being added. if (st_length_get(name) != 0 || (st_length_get(name) == 0 && *stream != ' ')) imap_parse_address_put(name, *stream); } length--; stream++; } // A simple trimmer. while (st_length_get(name) != 0 && *(st_char_get(name) + st_length_get(name) - 1) == ' ') { st_length_set(name, st_length_get(name) - 1); } // In situations where a name couldn't be parsed, reset the pointer to NULL so that "NIL" is returned. if (st_length_get(name) == 0) { st_free(name); name = NULL; } tok_get_st(address, '@', 0, &box); tok_get_st(address, '@', 1, &dom); output = imap_build_array("snss", name, NULL, &box, &dom); if (name != NULL) { st_free(name); } if (address != NULL) { st_free(address); } 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; }
/** * @brief Read data from a connection, and store it in its internal buffer. * @note This function handles reading data from both regular and ssl connections. * If the connection's network buffer hasn't been allocated, it will be initialized. * @param con a pointer to the connection object from which the data will be read. * @return -1 on internal error, or on a read error. * * * */ int64_t con_read(connection_t *con) { ssize_t bytes; bool_t blocking; if (!con || con->network.sockd == -1) { con->network.status = -1; return -1; } // Check for an existing network buffer. If there isn't one, try creating it. if (!con->network.buffer && !con_init_network_buffer(con)) { con->network.status = -1; return -1; } // Check for data past the current line buffer. if (pl_length_get(con->network.line) && st_length_get(con->network.buffer) > pl_length_get(con->network.line)) { // Move the unused data to the front of the buffer. mm_move(st_data_get(con->network.buffer), st_data_get(con->network.buffer) + pl_length_get(con->network.line), st_length_get(con->network.buffer) - pl_length_get(con->network.line)); // Update the length. st_length_set(con->network.buffer, st_length_get(con->network.buffer) - pl_length_get(con->network.line)); // Clear the line buffer. con->network.line = pl_null(); } // Otherwise reset the buffer and line lengths to zero. else { st_length_set(con->network.buffer, 0); con->network.line = pl_null(); } // Loop until the buffer has data or we get an error. do { blocking = st_length_get(con->network.buffer) ? false : true; // Read bytes off the network. If data is already in the buffer this should be a non-blocking read operation so we can // return the already buffered data without delay. if (con->network.ssl) { bytes = ssl_read(con->network.ssl, st_char_get(con->network.buffer) + st_length_get(con->network.buffer), st_avail_get(con->network.buffer) - st_length_get(con->network.buffer), blocking); if (!bytes && ssl_shutdown_get(con->network.ssl)) { con->network.status = -1; return -1; } } else { bytes = recv(con->network.sockd, st_char_get(con->network.buffer) + st_length_get(con->network.buffer), st_avail_get(con->network.buffer) - st_length_get(con->network.buffer), blocking ? 0 : MSG_DONTWAIT); if (bytes < 0 && errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) { con->network.status = -1; return -1; } } if (bytes > 0) { st_length_set(con->network.buffer, st_length_get(con->network.buffer) + bytes); // Or break out of the loop because we've been shutdown. } else if (!bytes) { break; } } while (status() && blocking && !st_length_get(con->network.buffer)); // If there is data in the buffer process it. Otherwise if the buffer is empty and the connection appears to be closed // (as indicated by a return value of 0), then return -1 to let the caller know the connection is dead. if (st_length_get(con->network.buffer)) { con->network.status = 1; } else if (!bytes) { con->network.status = 2; return -2; } return st_length_get(con->network.buffer); }
/** * @brief Read a line of input from a network connection. * @note This function handles reading data from both regular and ssl connections. * This function continually attempts to read incoming data from the specified connection until a \n terminated line of input is received. * If a new line is read, the length of that line is returned to the caller, including the trailing \n. * If the read returns -1 and wasn't caused by a syscall interruption or blocking error, -1 is returned, and the connection status is set to -1. * If the read returns 0 and wasn't caused by a syscall interruption or blocking error, -2 is returned, and the connection status is set to 2. * Once a \n character is reached, the length of the current line of input is returned to the user, and the connection status is set to 1. * @param con the network connection across which the line of data will be read. * @return -1 on general failure, -2 if the connection was reset, or the length of the current line of input, including the trailing \n. */ int64_t con_read_line(connection_t *con, bool_t block) { ssize_t bytes; bool_t line = false; if (!con || con->network.sockd == -1) { con->network.status = -1; return -1; } // Check for an existing network buffer. If there isn't one, try creating it. if (!con->network.buffer && !con_init_network_buffer(con)) { con->network.status = -1; return -1; } // Check if we have received more data than just what is in the current line of input. if (pl_length_get(con->network.line) && st_length_get(con->network.buffer) > pl_length_get(con->network.line)) { // If so, move the unused "new" data after the current line marker to the front of the buffer. mm_move(st_data_get(con->network.buffer), st_data_get(con->network.buffer) + pl_length_get(con->network.line), st_length_get(con->network.buffer) - pl_length_get(con->network.line)); // Update the buffer length. st_length_set(con->network.buffer, st_length_get(con->network.buffer) - pl_length_get(con->network.line)); // Check whether the data we just moved contains a complete line. if (!pl_empty((con->network.line = line_pl_st(con->network.buffer, 0)))) { con->network.status = 1; return pl_length_get(con->network.line); } } // Otherwise reset the buffer and line lengths to zero. else { st_length_set(con->network.buffer, 0); con->network.line = pl_null(); } // Loop until we get a complete line, an error, or the buffer is filled. do { // Read bytes off the network. Skip past any existing data in the buffer. if (con->network.ssl) { // If bytes is zero or below and the library isn't asking for another read, then an error occurred. bytes = ssl_read(con->network.ssl, st_char_get(con->network.buffer) + st_length_get(con->network.buffer), st_avail_get(con->network.buffer) - st_length_get(con->network.buffer), block); if (bytes <= 0 && bytes != SSL_ERROR_WANT_READ) { con->network.status = -1; return -1; } else if (bytes <= 0) { return 0; } } else { bytes = recv(con->network.sockd, st_char_get(con->network.buffer) + st_length_get(con->network.buffer), st_avail_get(con->network.buffer) - st_length_get(con->network.buffer), (block ? 0 : MSG_DONTWAIT)); // Check for errors on non-SSL reads in the traditional way. if (bytes <= 0 && errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) { con->network.status = -1; return -1; } else if (!bytes) { con->network.status = 2; return -2; } } if (bytes > 0) { st_length_set(con->network.buffer, st_length_get(con->network.buffer) + bytes); } // Check whether we have a complete line before checking whether the connection was closed. if (!st_empty(con->network.buffer) && !pl_empty((con->network.line = line_pl_st(con->network.buffer, 0)))) { line = true; } } while (status() && !line && st_length_get(con->network.buffer) != st_avail_get(con->network.buffer)); if (st_length_get(con->network.buffer) > 0) { con->network.status = 1; } return pl_length_get(con->network.line); }