I have this code and I want for each char[] inside message struct will be null terminated in order to avoid print all the memory values. if I create a single element it poses no problem. but when I have several elements in a msg array when I null terminate the char and when I add a new element it seems to overwrite and the null terminate will disapear!
typedef unsigned char byte;
enum msgType {
tree = 2
};
struct header {
msgType _type;
unsigned _value;
};
struct message {
uint16_t x;
bool b;
char c[5];
};
void allocateHeader(int value, byte* buffer, int size) {
header* t = (header*)buffer;
memset(buffer, 0, size);
t->_type = tree;
t->_value = value;
}
int main() {
byte buffer[1000];
std::string str = "hello";
std::string str2 = "fooo";
allocateHeader(8, buffer, 20);
message msg1;
msg1.x = 18;
msg1.b = false;
memset(msg1.c, 0, 5);
memcpy(msg1.c, str.c_str(), 5);
msg1.c[5] = '\0';
message msg2;
msg2.x = 1;
msg2.b = true;
memset(msg2.c, 0, 4);
memcpy(msg2.c, str2.c_str(), 4);
msg2.c[4] = '\0';
message msg[2];
msg[0] = msg1;
msg[0].c[5] = '\0';
msg[1] = msg2;
msg[1].c[4] = '\0';
memcpy(&buffer[sizeof(header)], &msg, sizeof(msg));
message msgrecp[2];
message* msgp = (message*)&buffer[8];
for(auto i=0; i < 2; i++){
msgrecp[i] = *msgp;
std::string str(msgrecp[i].c);
std::cout << msgrecp[i].b << " " << str << " " << msgrecp[i].x << std::endl;
//print
//0 hello[|[#{[ 18
//1 fooo 1
msgp = (message*)&buffer[8 + sizeof(message)];
}
return 0;
}
message::c is char[5]
so c[5]='\0' access outside of bound
c_str does contain the null terminate char, you can simply copy length()+1 given long enough buffer (not true in the code).
btw memset(msg.c, 0, 4) followed by memcpy(msg.c, str.c_str(), 4) doesn't make much sense, why you fill it if you're going to overwrite it immediately?
Related
I am receiving data following a certain format over a TCP server by serializing them.
the class of the object:
class Command {
private:
char letter;
int x;
int y;
std::string button;
public:
Command(char _letter, int _x, int _y, std::string _button) {
letter = _letter;
x = _x;
y = _y;
button = _button;
}
Command(std::string serializedCmd)
{
size_t delimPos = 0;
std::vector<std::string> parts;
while ((delimPos = serializedCmd.find(SERIALIZE_DELIM)) != std::string::npos)
{
parts.push_back(serializedCmd.substr(0, delimPos));
serializedCmd.erase(0, delimPos + 1);
}
if (parts.empty()) {
this->letter = '$';
this->x = -1;
this->y = -1;
this->button = "nan";
return;
}
this->letter = (char)atoi(parts.at(0).data());
this->x = atoi(parts.at(1).data());
this->y = atoi(parts.at(2).data());
this->button = parts.at(3);
}
Command() {}
~Command() {}
std::string serialize()
{
return std::to_string(letter) + SERIALIZE_DELIM + std::to_string(x) + SERIALIZE_DELIM + std::to_string(y) + SERIALIZE_DELIM + button + SERIALIZE_DELIM;
}
char getLetter() { return letter; }
int getX() { return x; }
int getY() { return y; }
std::string getButton() { return button; }
bool isEmpty() {
return((this->letter == '$') && (this->x == -1) && (this->y == -1) && (this->button == "nan"));
}
void printCommand() {
std::cout << "letter: " << letter << std::endl;
std::cout << "x : " << x << std::endl;
std::cout << "y : " << y << std::endl;
std::cout << "button: " << button << std::endl;
std::cout << "================" << std::endl;
}
};
The data after being DeSerialized at the clients end follows this format:
||{key}|{x}|{y}|{button}||
Example: ||$|20|40|nan||
The problem is that when using recv to get the data, it seems that I'm picking up some noise around the command.
Example:
Sending:
||$|301|386|nan||
Reciving:
(¿ⁿ8T√|301|386|╠╠↕▼
The command is there although it's crowded with noise for some reason.
The code I'm using to receive the data:
char buf[4096];
Command c;
std::string commandTemp = "";
while (true) {
memset(buf, '\0', 4096);
const int size = recv(sock, buf, sizeof(buf), 0);
std::string s(buf,size);
std::cout << s << std::endl;
buf[size] = 0;
commandTemp = buf;
if (!commandTemp.empty()) {
try {
c = Command(commandTemp);
exe(c); //executes command (unrelated)
}
catch (const std::exception& err) {
std::cout << "Couldn't execute!!!!!!!!" << std::endl;
}
}
else {
std::cout << "Error empty command!\n";
}
}
If I am missing any information I will happily provide it.
Can someone maybe tell what the problem is?
You have to loop on the recv till you get the entire message
This may not be the immediate cause of you problem but you will hit it eventually.
TCP is a stream protocol, not a message protocol. All that TCP guarantees is that the bytes you send are received once and in order. But you might send one 100 byte message and receive 20 5 byte messages. You will say "but it works now", true if on the same machine or the messages are small but not true with larger message over a real netwrok so you must do this
char buf[4096];
Command c;
std::string commandTemp = "";
while (true) {
memset(buf, '\0', 4096);
int offset = 0;
int len = ??;
while(len > 0){
const int size = recv(sock, buf + offset, sizeof(buf) - offset, 0);
if (size == 0)
break; // record that we got incomplete message
offset += size;
len -= size;
}
....
Note that you need to know the length in advance too. So either send fixed length message or prepend a fixed size length to each message and read that first
I am trying to make a program that reads a string from a file in SPIFFS with 4 tab-separated things and then processes it into four char arrays to be used in another function. However, I get the error cannot convert 'char*' to 'char**' in assignment. Is there any idea why? Here's my code:
#include <string.h>
#include "FS.h"
#include "AdafruitIO_WiFi.h"
char *ssid;
char *pass;
char *aiduser;
char *aidkey;
// comment out the following two lines if you are using fona or ethernet
#include "AdafruitIO_WiFi.h"
//AdafruitIO_WiFi io(IO_USERNAME, IO_KEY, WIFI_SSID, WIFI_PASS);
void setupWifi(char* *aiduser, char* *aidkey, char* *ssid, char* *pass){
#define WIFIFILE "/config.txt"
int addr = 0;
bool spiffsActive = false;
if (SPIFFS.begin()) {
spiffsActive = true;
}
File f = SPIFFS.open(WIFIFILE, "r");
String str;
while (f.position()<f.size())
{
str=f.readStringUntil('\n');
str.trim();
}
// Length (with one extra character for the null terminator)
int str_len = str.length() + 1;
// Prepare the character array (the buffer)
char char_array[str_len];
// Copy it over
str.toCharArray(char_array, str_len);
const char s[2] = {9, 0};
/* get the first token */
aiduser = strtok(char_array, s);
aidpass = strtok(NULL, s);
ssid = strtok(NULL, s);
pass = strtok(NULL, s);
/* walk through other tokens
while( token != NULL ) {
printf( " %s\n", token );
token = strtok(NULL, s);
}*/
// RESULT: A thingy
}
void setup(){
setupWifi(&aiduser, &aidkey, &ssid, &pass);
AdafruitIO_WiFi io(aiduser, aidkey, ssid, pass);}
Also, I can't run the setupWifi function unless it is in setup or loop, but I can't make it in another setup because this is #included into another main file.
You get this error because of this:
void setupWifi(char* *aiduser, char* *aidkey, char* *ssid, char* *pass)
{
...
aiduser = strtok(char_array, s);
aidpass = strtok(NULL, s);
ssid = strtok(NULL, s);
pass = strtok(NULL, s);
}
This variables are double pointers, strtok returns a pointer to char, those
are not compatible types.
Because strtok returns char_array + some_offset and char_array is a local
variable in setupWifi, you need to do a copy for each of them and return the
copy instead. You can do it with strdup.
*aiduser = strdup(strtok(char_array, s));
*aidpass = strdup(strtok(NULL, s));
*ssid = strdup(strtok(NULL, s));
*pass = strdup(strtok(NULL, s));
I encourage you to always check the return value of strdup, because it can
return NULL.1
If your system does not have strdup, then you can write your own:
char *strdup(const char *text)
{
if(text == NULL)
return NULL;
char *copy = calloc(strlen(text) + 1, 1);
if(copy == NULL)
return NULL;
return strcpy(copy, text);
}
One last thing:
void setupWifi(char* *aiduser, char* *aidkey, char* *ssid, char* *pass);
It looks really awkward, never seen declaring double pointer this way. Much
easier to read would be
void setupWifi(char **aiduser, char **aidkey, char **ssid, char **pass);
Fotenotes
1While the syntax is correct, I still consider this bad practice,
because you should always check the return values of functions that return
pointers. If they return NULL, you cannot access the memory. This adds a
little bit of more code, but your program will not die of segfaults and it can
recover from the errors.
I'd also change your function to return 1 on success, 0 otherwise:
int parse_and_set(char *txt, const char *delim, char **var)
{
if(delim == NULL || var == NULL)
return 0;
char *token = strtok(txt, delim);
if(token == NULL)
return 0;
token = strdup(token);
if(token == NULL)
return NULL;
*var = token;
return 1;
}
void init_parse(char ***vars, size_t len)
{
for(size_t i = 0; i < len; ++i)
**(vars + i) = NULL;
}
int cleanup_parse(char ***vars, size_t len, int retval)
{
for(size_t i = 0; i < len; ++i)
{
free(**(vars + i));
**(vars + i) = NULL;
}
}
int setupWifi(char **aiduser, char **aidkey, char **ssid, char **pass)
{
if(aiduser == NULL || aidkey == NULL || ssid == NULL || pass == NULL)
return 0;
...
/* get the token token */
char **vars[] = { aiduser, aidkey, ssid, pass };
size_t len = sizeof vars / sizeof *vars;
init_parse(vars, len);
if(parse_and_set(char_array, s, aiduser) == 0)
return cleanup_parse(vars, len, 0);
if(parse_and_set(NULL, s, aidpass) == 0)
return cleanup_parse(vars, len, 0);
if(parse_and_set(NULL, s, ssid) == 0)
return cleanup_parse(vars, len, 0);
if(parse_and_set(NULL, s, pass) == 0)
return cleanup_parse(vars, len, 0);
...
return 1;
}
I copy a char* to a char* and get it back and coutit.
char buffer[100] = { 0 };
char* name = "foo";
int bar = 12345;
memcpy(buffer, &(bar), 4);
memcpy(buffer + 4, name, 3);
int result = 0;
memcpy(&result, buffer, 4);
char backname[3];
memcpy(backname, buffer + 4, 3);
std::cout << result << std::endl;
std::cout << backname << std::endl;
Why does it show this:
12345
foo╠╠╠╠╠╠╠╠╠90
but strcmp(backname,"foo")does let me know that they match?
Edit:
Things I would fix:
Use sizeof(int) instead of 4. This is most likely not the real issue.
backname is not a null-terminated string the way you are using it. Use:
char backname[4] = {0};
memcpy(backname, buffer + sizeof(int), 3);
I'm using C++ library PolarSSL for RSA encryption and decryption. But I'm not able to decrypt an encrypted string unless it's an output from encryption. Following code doesn't work (it's not refactored). It encrypts text and encodes the output to the Base64 and back. Condition on strcmp works (strings are the same).
AsymetricCipher::encrypt(const std::string &pathToPublicKey, std::istream &inputData, std::ostream &encryptedData) {
if(initServerPublicCtx(pathToPublicKey, 512)) {
std::cout << "Encryption error: Can't load public key from file: " << pathToPublicKey << std::endl;
return false;
}
entropy_context entropy;
ctr_drbg_context ctr_drbg;
char *pers = "rsa_encrypt";
entropy_init(&entropy);
if(ctr_drbg_init(&ctr_drbg, entropy_func, &entropy, (unsigned char*)pers, strlen(pers)) != 0) {
std::cout << "Encryption error: ctr_drbg_init failed" << std::endl;
return false;
}
size_t inputSize = ::getStreamSize(inputData);
char *input = new char[inputSize];
memset(input, 0, inputSize);
inputData.read(input, inputSize);
input[inputSize] = '\0';
unsigned char *buffer = new unsigned char[ctx.len];
memset(buffer, 0, ctx.len);
memcpy(buffer, input, inputSize);
// This has to be rewritten
size_t MAX_OUTPUT_LENGTH = ctx.len;
unsigned char *outputBuffer = new unsigned char[MAX_OUTPUT_LENGTH];
memset(outputBuffer, 0, MAX_OUTPUT_LENGTH);
if(rsa_pkcs1_encrypt(&ctx, ctr_drbg_random, &ctr_drbg, RSA_PUBLIC, inputSize, buffer, outputBuffer) != 0) {
std::cout << "Encryption error: rsa_pkcs1_encrypt failed" << std::endl;
return false;
}
initServerPrivateCtx("data/private.key", 512);
size_t outputSize = 0;
std::string copyBuffer = "";
std::stringstream encStream;
std::string base64 = "";
Base64Wrapper::encode(outputBuffer, strlen((char*)outputBuffer), base64);
for(size_t i = 0; i < base64.length();) {
encStream << base64[i++];
}
unsigned char *encBuffer = new unsigned char[MAX_OUTPUT_LENGTH+10];
memset(encBuffer, 0, MAX_OUTPUT_LENGTH);
encStream.read((char *)encBuffer, MAX_OUTPUT_LENGTH+10);
copyBuffer.append((char *)encBuffer);
unsigned char *decoded = NULL;
size_t decodedSize = 0;
Base64Wrapper::decode(copyBuffer, &decoded, &decodedSize);
decoded[decodedSize] = '\0';
if(strcmp((char*)outputBuffer, (char*)decoded) != 0) {
std::cout << "Different";
}
memset(buffer, 0, ctx.len);
if(rsa_pkcs1_decrypt(&ctx, RSA_PRIVATE, &outputSize, decoded, buffer, MAX_OUTPUT_LENGTH) != 0) {
std::cout << "Decryption error: rsa_pkcs1_decrypt failed" << std::endl;
return false;
}
::cleanMemory(outputBuffer, MAX_OUTPUT_LENGTH);
::cleanMemory(buffer, ctx.len);
delete [] outputBuffer;
delete [] buffer;
delete [] encBuffer;
delete [] decoded;
//delete [] input;
return true;
}
However, if I call rsa_pkcs1_decrypt with outputBuffer from encryption, everything works fine.
I need to encrypt text, send it and decrypt on another place in code.
Any suggestions what am I doing wrong?
Classic memory overflow
char *input = new char[inputSize];
input[inputSize] = '\0';
I've finally found a solution.
The problem was strlen((char*)outputBuffer) becuase it was always 0 since output from rsa_pkcs1_encrypt starts with \0.
The right solution is
bool AsymetricCipher::encrypt(const std::string &pathToPublicKey, std::istream &inputData, std::ostream &encryptedData) {
if(initServerPublicCtx(pathToPublicKey, 512)) {
std::cout << "Encryption error: Can't load public key from file: " << pathToPublicKey << std::endl;
return false;
}
entropy_context entropy;
ctr_drbg_context ctr_drbg;
char *pers = "rsa_encrypt";
entropy_init(&entropy);
if(ctr_drbg_init(&ctr_drbg, entropy_func, &entropy, (unsigned char*)pers, strlen(pers)) != 0) {
std::cout << "Encryption error: ctr_drbg_init failed" << std::endl;
return false;
}
size_t inputSize = ::getStreamSize(inputData);
unsigned char *buffer = new unsigned char[inputSize];
memset(buffer, 0, inputSize);
inputData.read((char *)buffer, inputSize);
size_t MAX_OUTPUT_LENGTH = ctx.len;
unsigned char *outputBuffer = new unsigned char[MAX_OUTPUT_LENGTH];
memset(outputBuffer, 0, MAX_OUTPUT_LENGTH);
bool retVal = true;
if(rsa_pkcs1_encrypt(&ctx, ctr_drbg_random, &ctr_drbg, RSA_PUBLIC, inputSize, buffer, outputBuffer) != 0) {
std::cout << "Encryption error: rsa_pkcs1_encrypt failed" << std::endl;
retVal = false;
}
if(retVal) {
std::string base64;
Base64Wrapper::encode(outputBuffer, MAX_OUTPUT_LENGTH, base64);
encryptedData << base64;
::cleanMemory(base64);
}
::cleanMemory(outputBuffer, MAX_OUTPUT_LENGTH);
::cleanMemory(buffer, ctx.len);
delete [] outputBuffer;
delete [] buffer;
return retVal;
}
And for decryption
bool AsymetricCipher::decrypt( const std::string &pathToPrivateKey, std::istream &encryptedData, std::ostream &decryptedData ) {
if(initServerPrivateCtx(pathToPrivateKey, 512)) {
std::cout << "Decrypt error: Can't load private key from file: " << pathToPrivateKey << std::endl;
return false;
}
size_t inputSize = ::getStreamSize(encryptedData);
size_t outputSize = 0;
unsigned char* buffer = NULL;
std::string base64;
size_t bufferSize = 0;
encryptedData >> base64;
Base64Wrapper::decode(base64, &buffer, &bufferSize);
::cleanMemory(base64);
size_t MAX_OUTPUT_LENGTH = ctx.len;
unsigned char *outputBuffer = new unsigned char[MAX_OUTPUT_LENGTH];
bool retVal = true;
if(rsa_pkcs1_decrypt(&ctx, RSA_PRIVATE, &outputSize, buffer, outputBuffer, MAX_OUTPUT_LENGTH) != 0) {
std::cout << "Decryption error: rsa_pkcs1_decrypt failed" << std::endl;
retVal = false;
}
if(retVal) {
outputBuffer[outputSize] = '\0';
decryptedData << outputBuffer;
}
::cleanMemory(buffer, bufferSize);
::cleanMemory(outputBuffer, outputSize);
delete [] outputBuffer;
delete [] buffer;
return retVal;
}
Below is my code for creating a hash in openssl.
I was wondering why is my output always partially diffrent.
int main()
{
char alg[] = "MD5";
char buf[EVP_MAX_KEY_LENGTH] = "5";
unsigned char *testKey;
unsigned int olen;
testKey = simple_digest(alg, buf, EVP_MAX_KEY_LENGTH,&olen);
std::cout << "Printing key : >>";
print_hex(testKey,EVP_MAX_KEY_LENGTH);
std::cout << "<<" << std::endl;
}
unsigned char *simple_digest(char *alg, char *buf, unsigned int len,unsigned int *olen)
{
const EVP_MD *m;
EVP_MD_CTX ctx;
unsigned char *ret;
OpenSSL_add_all_digests();
if (!(m = EVP_get_digestbyname(alg)))
return NULL;
if (!(ret = (unsigned char *)malloc(EVP_MAX_MD_SIZE)))
return NULL;
EVP_DigestInit(&ctx, m);
EVP_DigestUpdate(&ctx, buf, len);
EVP_DigestFinal(&ctx, ret, olen);
// std::cout << "computed key" << ret << std::endl;
return ret;
}
void print_hex(unsigned char *bs, unsigned int n)
{
int i;
for (i = 0; i < n; i++)
printf("%02x", bs[i]);
printf("\n");
}
Output :
3afb8ebc9c93bf6d40285736f210b7856af8bab4d040ca090043ca09f840ca09
3afb8ebc9c93bf6d40285736f210b7856af8bab490a5f909c0a7f909b8a5f909
As you can see only the back few characters differ.
Thanks in advance! :D
Update (correct working version) :
int main()
{
char alg[] = "MD5";
char buf[EVP_MAX_KEY_LENGTH] = "5";
unsigned char *testKey;
unsigned int olen;
testKey = simple_digest(alg, buf,strlen(buf),&olen);
std::cout << "Printing key : >>";
print_hex(testKey,olen);
std::cout << "<<" << std::endl;
}
unsigned char *simple_digest(char *alg, char *buf, unsigned int len,unsigned int *olen)
{
const EVP_MD *m;
EVP_MD_CTX ctx;
unsigned char *ret;
OpenSSL_add_all_digests();
if (!(m = EVP_get_digestbyname(alg)))
return NULL;
if (!(ret = (unsigned char *)malloc(EVP_MAX_KEY_LENGTH)))
return NULL;
EVP_DigestInit(&ctx, m);
EVP_DigestUpdate(&ctx, buf, len);
EVP_DigestFinal(&ctx, ret, olen);
return ret;
}
void print_hex(unsigned char *bs, unsigned int n)
{
int i;
for (i = 0; i < n; i++)
printf("%02x", bs[i]);
}
output :
Printing key : >>e4da3b7fbbce2345d7772b0674a318d5<<
You are trying to hash a one-character string, "5", but you are telling EVP_Digest_Update that your buffer length is EVP_MAX_KEY_LENGTH. You need to pass the actual length of your buffer, not the maximum length it could be.
For completeness, here is what your main function should look like:
int main()
{
char alg[] = "MD5";
char buf[EVP_MAX_KEY_LENGTH] = "5";
unsigned char *testKey;
unsigned int olen;
# Pass the true length of but
testKey = simple_digest(alg, buf, strlen(buf), &olen);
std::cout << "Printing key : >>";
# Pass the true length of testKey
print_hex(testKey,olen);
std::cout << "<<" << std::endl;
}
One other caveat: you probably don't want to use EVP_MAX_KEY_LENGTH for your buffer, as that would limit the size of the message you could hash.