Esempio n. 1
0
/**
 * @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;
}
Esempio n. 2
0
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;
}
Esempio n. 3
0
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;
}
Esempio n. 4
0
/**
 * @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;
}
Esempio n. 5
0
/**
 * @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;
}
Esempio n. 6
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;
}
Esempio n. 7
0
/**
 * @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);
}
Esempio n. 8
0
File: read.c Progetto: lavabit/magma
/**
 * @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);
}
Esempio n. 9
0
File: read.c Progetto: lavabit/magma
/**
 * @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);
}
Esempio n. 10
0
File: read.c Progetto: lavabit/magma
/**
 * @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);
}
Esempio n. 11
0
File: read.c Progetto: lavabit/magma
/**
 * @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);
}
Esempio n. 12
0
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);
}
Esempio n. 13
0
/**
 * @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);
}
Esempio n. 14
0
/**
 * @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);
}
Esempio n. 15
0
File: orgs.c Progetto: lavabit/magma
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;
}
Esempio n. 16
0
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;
}
Esempio n. 17
0
/**
 * @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;
}
Esempio n. 18
0
/**
 * @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);
}
Esempio n. 19
0
/**
 * @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);
}