I have a function definition like this in an Arduino library:
bool RHReliableDatagram::sendtoWait(uint8_t * buf, uint8_t len, uint8_t address)
When I use:
uint8_t timePacket[] = "time\n";
sendtoWait(timePacket, sizeof(timePacket), SERVER_ADDRESS)
I get a perfectly fine string with "time" at the other end (SERVER_ADDRESS) in pyserial's readline() function which is decoded perfectly using utf-8 encoding.
Now I need to send some bytes using the same sendtoWait() function. I first define a char array of 60 bytes
char packetBuff[60] = "";
Then I get every byte and cast it as a char
char value = (char)flash.readByte(dumpCounter++);
Then put each value in the char array like this:
packetBuff[charNo] = value;
After I find a new line character I try to send the char array again using the sendtoWait() function:
if (value == '\n') {
Serial.println(packetBuff);
uint8_t buff[charNo];
for (int i = 0; i < charNo; i++) {
buff[i] = packetBuff[i];
}
sendtoWait(buff, charNo, SERVER_ADDRESS))
charNo++;
But although Serial.println(packetBuff) shows the proper characters e.g. something like "1559105487\n" (unix timestamp), buff array is just not working correctly and shows random characters something like a semi-colon, random single digit numbers etc.
How do I properly cast the char array and send it properly using the sendtoWait() function like "time\n" was sent?
EDIT:
Full code:
int charNo = 0;
char packetBuff[60] = "";
if (dumpCounter != 0) {
dumpCounter--;
}
while(dumpCounter < currentFlashLoc) {
char value = (char)flash.readByte(dumpCounter++);
packetBuff[charNo] = value;
if (value == '\n') {
Serial.println(packetBuff);
uint8_t buff[charNo];
for (int i = 0; i < charNo; i++) {
buff[i] = packetBuff[i];
}
if (manager.sendtoWait(buff, charNo, SERVER_ADDRESS)) {
// packet sent successfully
}
charNo = -1;
}
charNo++;
}
Casting the char array like this:
sendtoWait((uint8_t*)packetBuff, sizeof(packetBuff), SERVER_ADDRESS)
works correctly. buff is not required.
Related
int computeHMACSHA1Hash(const char * unhashedcstr, char * hashedcstr, const char * key, int returncode)
{
string hashed;
size_t unhashlength = strlen(unhashedcstr);
char * nonconstunhashcstr = new char[unhashlength];
strcpy_s(nonconstunhashcstr, unhashlength + 1, unhashedcstr);
unsigned char* pixels = reinterpret_cast<unsigned char*>(nonconstunhashcstr);
returncode = 0;
HMAC_CTX* context = HMAC_CTX_new();
size_t unhashedstrlength = sizeof(unhashedcstr);
if (context != NULL)
{
if (HMAC_Init_ex(context, key, strlen(key), EVP_sha1(), NULL))
{
if (HMAC_Update(context, pixels, unhashedstrlength))
{
unsigned char hash[EVP_MAX_MD_SIZE];
unsigned int lengthOfHash = 0;
if (HMAC_Final(context, hash, &lengthOfHash))
{
std::stringstream ss;
for (unsigned int i = 0; i < lengthOfHash; ++i)
{
ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
}
hashed = ss.str();
size_t outputSize = hashed.length() + 1; // +1 for null terminator
strcpy_s(hashedcstr, outputSize, hashed.c_str());
returncode = 0;
}
else
{
returncode = 7;
}
}
else
{
returncode = 6;
}
}
else
{
returncode = 5;
}
HMAC_CTX_free(context);
}
else
{
returncode = 4;
}
return returncode;
}
int main()
{
const char * unhashedcstr = "a=services&l=v1&p=open&k=SD58292829&i=20200918125249803&n=2124&t=1600404769&f={\"invoiceCode\": \"11111\",\"invoiceNo\": \"2222\",\"inTaxAmount\": \"\",\"exTaxAmount\": \"\"}";
char * hashedcstr = new char[100];
int returncode = 0;
const char * key = "SD886A11B0EE428F";
int result = computeHMACSHA1Hash(unhashedcstr, hashedcstr, key, returncode);
return 0;
}
I tried the code above to calculating the HMAC SHA1 hash value for a content, but compared the results on https://www.freeformatter.com/hmac-generator.html#before-output
it looks like I didn't do it right. I'm not sure what I have done wrong though. Any help would be appreciated.
It turned out the result was "d916b4c2d277319bbf18076c158f0cbcf6c3bc57", while on the website https://www.freeformatter.com/hmac-generator.html#before-output, the result was "71482b292f2b2a47b3eca6dad5e7350566d60963". Even when I tried using the string "a=services&l=v1&p=open&k=SD58292829&i=20200918125249803&n=2124&t=1600404769&f={"invoiceCode": "11111","invoiceNo": "2222","inTaxAmount": "","exTaxAmount": ""}" which removed the escape characters, the result was "09be98b6129c149e685ed57a1d19651a602cda0d". It didn't match the correct one.
Is there anything wrong with my code?
Your hash is calculated over the bytes a=se, which are the first four bytes of the whole input string. Thus, you get d916b4c2d277319bbf18076c158f0cbcf6c3bc57 instead of the 09be98b6129c149e685ed57a1d19651a602cda0d that would correspond to the whole string.
The reason is this:
size_t unhashedstrlength = sizeof(unhashedcstr);
Here, sizeof(unhashedcstr) is the size of the unhashedcstr pointer itself (which is of type const char*), not the size of the null-terminated C-style string this unhashedcstr pointer is pointing to. You are compiling a 32-bit program, so the size of a pointer is 4 bytes. Thus, unhashedstrlength is 4.
To get the length of the C-style string, you can do this instead:
size_t unhashedstrlength = strlen(unhashedcstr);
But just as a comment, in modern C++, you should avoid using raw pointers (such as const char*, char*, unsigned char*), C functions (like strlen(), strcpy_s()) and manual memory management (new / delete and new[] / delete[]). You should prefer to use std::string and/or std::vector<unsigned char> instead, wherever possible. When you need to pass a buffer's address to an API function, you can use std::string::data(), std::vector::data(), or more generally, std::data().
By the way, you currently leak memory: you dynamically allocate buffers using new[], but you never deallocate those (using delete[]). So that memory is released by the OS only after the program exits. This is called a memory leak.
At first, I would like to point out that despite using C ++ I cannot use strings or vectors. It is like C with objects.
Ok I have class A with char* test() method:
char* A::test()
{
char to_return[3*this->some_value+3];
for (int i = 0; i < this->some_value; i++)
{
to_return[3*i] = '♥';
to_return[3*i+1] = 'b';
to_return[3*i+2] = ' ';
}
char* dto_return = to_return;
return std::move(dto_return);
}
next in object of class B I have:
ofstream file;
file.open("myfile.txt", ofstream::out | ofstream::trunc);
file.close();
file.open("myfile.txt");
char* to_write = a_obj->test();
size_t len = strlen(to_write);
file.write((char*)&len, sizeof(len));
file.write(to_write, len);
file.close();
(based on this answer)
but the content of the file is:
¥b ¥b ¥b m
and it is definitely not what I'm looking for. The content should be:
♥b ♥b ♥b
How to fix that?
The problems are:
to_return is a local array that ends its lifetime on returning from the function, so returning its pointer is a bad idea.
'♥' may differ from what you want, especially when ♥ cannot be represented by one byte in your character code.
To overcome this problems:
Allocate a dynamic array that persists after returning from the function.
Use string literal to represent the characters to add.
char* A::test()
{
const char *part = "♥b ";
size_t part_len = strlen(part);
char *to_return = new char[part_len*this->some_value+1];
for (int i = 0; i < this->some_value; i++)
{
strcpy(to_return + part_len * i, part);
}
return to_return;
}
The dynamic array returned should be freed via delete[] after completed to use.
char* to_write = a_obj->test();
// ...
delete[] to_write;
I have an Arduino that controls timers. The settings for timers are stored in byte arrays. I need to convert the arrays to strings to SET a string on an external Redis server.
So, I have many arrays of bytes of different lengths that I need to convert to strings to pass as arguments to a function expecting char[]. I need the values to be separated by commas and terminated with '\0'.
byte timer[4] {1,5,23,120};
byte timer2[6] {0,0,0,0,0,0}
I have succeeded to do it manually for each array using sprintf() like this
char buf[30];
for (int i=0;i<5;i++){ buf[i] = (int) timer[i]; }
sprintf(buf, "%d,%d,%d,%d,%d",timer[0],timer[1],timer[2],timer[3],timer[4]);
That gives me an output string buf: 1,5,23,120
But I have to use a fixed number of 'placeholders' in sprintf().
I would like to come up with a function to which I could pass the name of the array (e.g. timer[]) and that would build a string, probably using a for loop of 'variable lengths' (depending of the particular array to to 'process') and many strcat() functions. I have tried a few ways to do this, none of them making sense to the compiler, nor to me!
Which way should I go looking?
Here is the low tech way you could do it in normal C.
char* toString(byte* bytes, int nbytes)
{
// Has to be static so it doesn't go out of scope at the end of the call.
// You could dynamically allocate memory based on nbytes.
// Size of 128 is arbitrary - pick something you know is big enough.
static char buffer[128];
char* bp = buffer;
*bp = 0; // means return will be valid even if nbytes is 0.
for(int i = 0; i < nbytes; i++)
{
if (i > 0) {
*bp = ','; bp++;
}
// sprintf can have errors, so probably want to check for a +ve
// result.
bp += sprintf(bp, "%d", bytes[i])
}
return buffer;
}
an implementation, assuming that timer is an array (else, size would have to be passed as a parameter) with the special handling of the comma.
Basically, print the integer in a temp buffer, then concatenate to the final buffer. Pepper with commas where needed.
The size of the output buffer isn't tested, mind.
#include <stdio.h>
#include <strings.h>
typedef unsigned char byte;
int main()
{
byte timer[4] = {1,5,23,120};
int i;
char buf[30] = "";
int first_item = 1;
for (i=0;i<sizeof(timer)/sizeof(timer[0]);i++)
{
char t[10];
if (!first_item)
{
strcat(buf,",");
}
first_item = 0;
sprintf(t,"%d",timer[i]);
strcat(buf,t);
}
printf(buf);
}
I want to print MD5 for some string. For this I have done the the function
std::string generateHashMD5(std::string text)
{
unsigned char * resultHash;
resultHash = MD5((const unsigned char*)text.c_str(), text.size(), NULL);
std::string result;
result += (char *) resultHash;
return result;
}
Mow I want to print the result of this function. I try to version of such function.
void printHash(std::string hash)
{
for (unsigned i = 0; i < str.size(); i++)
{
int val = (short) hash[i];
std::cout<<std::hex<<val<<':';
}
std::cout<<std::endl;
}
std::string printHash(std::string hash)
{
char arrayResult[200];
for(int i = 0; i < 16; i++)
sprintf(&arrayResult[i*2], "%02x", (unsigned short int)hash[i]);
std::string result;
result += arrayResult;
return result;
}
The problem is that unfortunately none of it does not show correct result. What should be changed in this function or where is the mistakes?
You improperly use std::string as a buffer:
result += (char *) resultHash;
treats resultHash as a c-string, so if there is \0 byte in middle it would not get enough data. If there is no \0 byte you would copy too much and get UB. You should use constructor with size:
std::string result( static_cast<const char *>( resultHash ), blocksize );
where block size probably is 16. But I would recommend to use std::array<uint8_t,blocksize> or std::vector<uint8_t> instead os std::string, as std::string for buffer is very confusing.
in case if MD5 returns byte array
result += (char *) resultHash;
return result;
conversion to string will lose numbers after 0 because string constructor interprets input as null-terminated string
so vector can be used or string construction with explicit number of characters.
Still, there are not enough information to say exactly
I wrote this code to reverse strings. It works well, but when I enter short strings like "american beauty," it actually prints "ytuaeb nacirema2." This is my code. I would like to know what is wrong with my code that prints a random 2 at the end of the string. Thanks
// This program prompts the user to enter a string and displays it backwards.
#include <iostream>
#include <cstdlib>
using namespace std;
void printBackwards(char *strPtr); // Function prototype
int main() {
const int SIZE = 50;
char userString[SIZE];
char *strPtr;
cout << "Please enter a string (up to 49 characters)";
cin.getline(userString, SIZE);
printBackwards(userString);
}
//**************************************************************
// Definition of printBackwards. This function receives a *
// pointer to character and inverts the order of the characters*
// within it. *
//**************************************************************
void printBackwards(char *strPtr) {
const int SIZE = 50;
int length = 0;
char stringInverted[SIZE];
int count = 0;
char *strPtr1 = 0;
int stringSize;
int i = 0;
int sum = 0;
while (*strPtr != '\0') {
strPtr++; // Set the pointer at the end of the string.
sum++; // Add to sum.
}
strPtr--;
// Save the contents of strPtr on stringInverted on inverted order
while (count < sum) {
stringInverted[count] = *strPtr;
strPtr--;
count++;
}
// Add '\0' at the end of stringSize
stringInverted[count] == '\0';
cout << stringInverted << endl;
}
Thanks.
Your null termination is wrong. You're using == instead of =. You need to change:
stringInverted[count] == '\0';
into
stringInverted[count] = '\0';
// Add '\0' at the end of stringSize
stringInverted[count] == '\0';
Should use = here.
What is wrong with your code is that you do not even use strlen for counting the length of the string and you use fixed size strings (no malloc, or, gasp new[]), or the std::string (this is C++)! Even in plain C, not using strlen is always wrong because it is hand-optimized for the processor. What is worst, you have allocated the string to be returned (stringInverted) from the stack frame, which means when the function exits, the pointer is invalid and any time the code "works" is purely accidental.
To reverse a string on c++ you do this:
#include <iostream>
#include <string>
int main() {
std::string s = "asdfasdf";
std::string reversed (s.rbegin(), s.rend());
std::cout << reversed << std::endl;
}
To reverse a string in C99 you do this:
char *reverse(const char *string) {
int length = strlen(string);
char *rv = (char*)malloc(length + 1);
char *end = rv + length;
*end-- = 0;
for ( ; end >= rv; end --, string ++) {
*end = *string;
}
return rv;
}
and remember to free the returned pointer after use. All other answers so far are blatantly wrong :)