I'm going a bit crazy with a simple boost asio TCP conversation.
I have a server and a client. I use length-prefixed messges. The client sends "one" and the server responds with "two". So this is what I see happen:
The client sends, and the server receives, 00 00 00 03 6F 6E 65 (== 0x0003 one).
The server responds by sending 00 00 00 03 74 77 6F (== 0x0003 two).
Now here is where it is very strange (code below). If the client reads four bytes, I expect it to get 00 00 00 03. If it reads seven, I expect to see 00 00 00 03 74 77 6F. (In fact, it will read four (the length header), then three (the body).)
But what I actually see is that, while if I read seven at once I do see 00 00 00 03 74 77 6F, if I only ask for four, I see 74 77 6F 03. This doesn't make any sense to me.
Here is the code I'm using to receive it (minus some print statements and such):
const int kTcpHeaderSize = 4;
const int kTcpMessageSize = 2048;
std::array<char, kTcpMessageSize + kTcpHeaderSize> receive_buffer_;
void TcpConnection::ReceiveHeader() {
boost::asio::async_read(
socket_, boost::asio::buffer(receive_buffer_, kTcpHeaderSize),
[this](boost::system::error_code error_code,
std::size_t received_length) {
if (error_code) {
LOG_WARNING << "Header read error: " << error_code;
socket_.close(); // TODO: Recover better.
return;
}
if (received_length != kTcpHeaderSize) {
LOG_ERROR << "Header length " << received_length
<< " != " << kTcpHeaderSize;
socket_.close(); // TODO: Recover better.
return;
}
uint32_t read_length_network;
memcpy(&read_length_network, receive_buffer_.data(),
kTcpHeaderSize);
uint32_t read_length = ntohl(read_length_network);
// Error: read_length is in the billions.
ReceiveBody(read_length);
});
}
Note that kTcpHeaderSize is 4. If I change it to 7 (which makes no sense, but just for the experiment) I see the stream of 7 bytes I expect. When it is 4, I see a stream that is not the first four bytes of what I expect.
Any pointers what I am doing wrong?
From what I can see in your code it should work according to the async_read documentation:
The asynchronous operation will continue until one of the following conditions is true:
The supplied buffers are full. That is, the bytes transferred is equal to the sum of the buffer sizes.
An error occurred.
However see the remark at the bottom:
This overload is equivalent to calling:
boost::asio::async_read(
s, buffers,
boost::asio::transfer_all(),
handler);
It looks like the transfer_all condition might be the only thing checked.
Try using the transfer_exactly condition and if it does work report an issue on https://github.com/boostorg/asio/issues.
The suggestion by #sergiopm to use transfer_all was good, and I'm pretty sure it helped. The other issue involved buffer lifetimes in the asynchronous send/receive functions. I got a bit confused, apparently, about how long certain things would live and how long I needed them to live, and so I was overwriting things from time to time. That may have been more important than transfer_all, but I'm still happy to give #sergiopm credit for helping getting me on my way.
The intent has just been to have a simple tcp client or server that I can declare, hand it a callback, and then go on my way knowing that I can only pay attention to those callbacks.
I'm pretty sure something like this must exist (thousands of times over). Do feel free to comment below, both for me and for those who come after, if you think there are better libraries than asio for this task (i.e., that would involve substantially less code on my part). The principle constraint is that, due to multiple languages and services, we need to own the wire protocol. Otherwise we get into things like "does library X have a module for language Y?".
As an aside, it's interesting to me that essentially every example I've found does length-prefix encoding rather than beginning/end of packet encoding. Length prefix is really easy to implement but, unless I'm quite mistaken, suffers from re-sync hell: if a stream is interrupted ("I'm going to send you 100 bytes, here are the first 50 but then I died") it's not clear to me that there aren't scenarios where I'm unable to resync properly.
Anyway, I learned a lot along the way, I recommend the exercise.
Related
I made a simple code for streaming & transferring a file over 8 bytes CAN message via the CAN Bus,
my code in c is as follows, however, my question is how to merge the fragmented file without any sequence controller?
how do I check the CRC of the receiving file?
since the CAN standard has its own acknowledgment, would that be sufficient for such huge streaming of a file?
typedef struct {
union {
struct {
uint32_t extd: 1;
uint32_t rtr: 1;
uint32_t ss: 1;
uint32_t self: 1;
uint32_t dlc_non_comp: 1;
uint32_t reserved: 27;
};
uint32_t flags;
};
uint32_t identifier;
uint8_t data_length_code;
uint8_t data[TWAI_FRAME_MAX_DLC];
} CAN_message_t;
#define destinationNode 20000x
CAN_message_t msg;
msg.identifier=destinationNode;
msg.data_length_code=8
File date = file.open("/data.bin");
uint8_t *p=date;
while(p){
char buffer[8];
memcpy(buffer, (p+8), 8);
CAN_transmit(&msg);
p+=8;
}
=========================================================
edit code
open the file and send the size and start point to the following function and then close the file
#define SEQUENCE_START 1000000
bool stream(size_t filesize,uint8_t *p){
uint32_t identifer=SEQUENCE_START;
twai_message_t message;
while(filesize<8) {
memcpy(message.data, (p+8), 8);
message.identifier=identifer;
message.data_length_code=8;
if( twai_transmit(&messageOutRPM, pdMS_TO_TICKS(1)) == ESP_OK){
p+=8;
identifer++;
filesize-=8;
}
}
if(filesize>0) {
memcpy(message.data, (p+filesize), filesize);
message.identifier=identifer;
message.data_length_code=filesize;
if( twai_transmit(&messageOutRPM, pdMS_TO_TICKS(1)) == ESP_OK) return true;
}
return true;
}
how to merge the fragmented file without any sequence controller?
There is absolutely no guarantee by CAN bus that the sent frames will be received. They might have CAN errors on the bus preventing some frames to be sent out.
Automotive engineers need to send files over the CAN network in order to implement software updates. To do that, they need to send frames which are way larger than 8 bytes. They defined a small Transport Protocol on the top of CAN: ISO-15765, usually named ISO-TP.
In this protocol, the frames are sent by group. The number of elements in the group is defined during the exchange and can possibility change during the frame transfer.
To give you an example of the communication flow:
SENDER -> RECEIVER: request to send a 800 bytes frame
SENDER <- RECEIVER: accepted, please group the frames by 4
SENDER -> RECEIVER: send part 1
SENDER -> RECEIVER: send part 2
SENDER -> RECEIVER: send part 3
SENDER -> RECEIVER: send part 4
SENDER <- RECEIVER: well-received, continue
SENDER -> RECEIVER: send part 5
SENDER -> RECEIVER: send part 6
SENDER -> RECEIVER: send part 7
SENDER -> RECEIVER: send part 8
SENDER <- RECEIVER: well-received, continue but please group by 8
SENDER -> RECEIVER: send part 9
SENDER -> RECEIVER: send part 10
SENDER -> RECEIVER: send part 11
SENDER -> RECEIVER: send part 12
SENDER -> RECEIVER: send part 13
SENDER -> RECEIVER: send part 14
SENDER -> RECEIVER: send part 15
SENDER -> RECEIVER: send part 16
SENDER <- RECEIVER: well-received, continue
In order to identify which part of the frame is being transmitted, a byte is used as a frame counter. It's a rolling counter, the point is to make sure the completeness of the data. If the frames are not received in the correct order, it does not matter much, as the software is able to determine that no frame has been lost.
[...] long exchange
SENDER -> RECEIVER: FD 00 00 00 00 00 00 00 part N+0
SENDER -> RECEIVER: FE 00 00 00 00 00 00 00 part N+1
SENDER -> RECEIVER: FF 00 00 00 00 00 00 00 part N+2
SENDER -> RECEIVER: 00 00 00 00 00 00 00 00 part N+3
^^
Rolling counter, just 1 byte
This transport layer is usually quite generic, it's frequent to see it available as a library provided by the CAN tool provider. You can also find some Open Source implementations.
since the CAN standard has its own acknowledgment, would that be sufficient for such huge streaming of a file
Actually, CAN bus has its own CRC at physical level, it should be enough for most cases. But if one want to add a custom checksum, one just need to define its length and prepend or append it to the data. Then, the receiver can re-calculate the CRC just after the completion of the transfer.
This code is questionable for several reasons.
First of all, bit-fields are poorly standardized and the bit order may not be the one you expect.
Second, the struct as you posted it will very likely contain padding after data_length_code so writing/reading it to some binary file will be problematic and non-portable.
At any rate I doubt p+8 will ever be correct, because even if there is no padding sizeof(uint32_t)+sizeof(uint32_t) puts us at the data_length_code member, not the data. Why would you want to copy the DLC and 7 bytes into some buffer? This is a bug.
since the CAN standard has its own acknowledgment, would that be sufficient for such huge streaming of a file?
You may want something as CRC32 to ensure there are no corruptions of the file. For the CAN transfer itself you don't need CRC since CAN comes with CRC-15 built-in.
But note that a CRC in the CAN data may be necessary in case of ugly hardware solutions with external CAN controllers. Such legacy solutions involve an exposed SPI bus which has no built-in error control what so ever. Modern electronics only use external CAN controllers in case one is stuck with some exotic MCU that must be used for other reasons, but it doesn't come with CAN on-chip.
Consider a device in the system, something under /dev/hdd[sg][nvme]xx
Open the device, get the file descriptor and start working with it (read(v)/write(v)/lseek, etc), at some point you may get EIO. How do you retrieve the underlying error reported by the device driver?
EDIT001: in case it is impossible using unistd functions, maybe there is other ways to work with block devices which can provide more low-level information like sg_scsi_sense_hdr?
You can't get any more error detail out of the POSIX functions. You're onto the right track with the SCSI generic stuff though. But, boy, it's loaded with hair. Check out the example in sg3_utils of how to do a SCSI READ(16). This will let you look at the sense data when it comes back:
https://github.com/hreinecke/sg3_utils/blob/master/examples/sg_simple16.c
Of course, this technique doesn't work with NVMe drives. (At least, not to my knowledge).
One concept I've played with in the past is to use normal POSIX/libc block I/O functions like pread and pwrite until I get an EIO out. At that point, you can bring in the SCSI-generic versions to try to figure out what happened. In the ideal case, a pread or lseek/read fails with EIO. You then turn around and re-issue it using a SG READ (10) or (16). If it's not just a transient failure, this may return sense data that your application can use.
Here's an example, using the command-line sg_read program. I have an iSCSI attached disk that I'm reading and writing. On the target, I remove its LUN mapping. dd reports EIO:
# dd if=/dev/sdb of=/tmp/output bs=512 count=1 iflag=direct
dd: error reading ‘/dev/sdb’: Input/output error
but sg_read reports some more useful information:
[root#localhost src]# sg_read blk_sgio=1 bs=512 cdbsz=10 count=512 if=/dev/sdb odir=1 verbose=10
Opened /dev/sdb for SG_IO with flags=0x4002
read cdb: 28 00 00 00 00 00 00 00 80 00
duration=9 ms
reading: SCSI status: Check Condition
Fixed format, current; Sense key: Illegal Request
Additional sense: Logical unit not supported
Raw sense data (in hex):
70 00 05 00 00 00 00 0a 00 00 00 00 25 00 00 00
00 00
sg_read: SCSI READ failed
Some error occurred, remaining block count=512
0+0 records in
You can see the Logical unit not supported additional sense code in the above output, indicating that there's no such LU at the target.
Possible? Yes. But as you can see from the code in sg_simple16.c, it's not easy!
I want to send TCP messages through boost::asio raw sockets.
My packets are based on this source: https://github.com/pfpacket/SYN-flood
I checked the binary output of IP und TCP packet generators and these messages look valid. Also the raw socket function for TCP looks fine.
45 10 00 28 00 00 40 00
40 06 A5 18 08 08 08 08
0B 0B 07 01 11 AD 15 B3
00 00 17 AA 00 00 00 00
50 02 10 00 3E BD 00 00
Anyway if I send this to the socket by the send_to function I receive the error:
send_to: Ein ungültiges Argument wurde angegeben
English translation: "Used a non valid argument"
I have no more idea, where there is a problem. I need more details to the relating problem.
How can I receive more specific error messages from send_to()? What can I do next?
int main() {
this->port = "5555";
this->target = "11.11.7.1";
try {
boost::asio::io_service io_service;
raw_tcp::socket socket(io_service, raw_tcp::v4());
socket.set_option(boost::asio::ip::ip_hdrincl(true));
raw_tcp::resolver resolver(io_service);
raw_tcp::resolver::query query( this->target , boost::lexical_cast< std::string >( this->port ));
raw_tcp::endpoint destination = *resolver.resolve( query );
boost::asio::streambuf request_buffer;
std::ostream os(&request_buffer);
//creates the ipheader and tcp packet and forward it to buffer os
set_syn_segment(os);
socket.send_to(request_buffer.data(), destination);
} catch (std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
}
Asio's error detection and reporting is a fairly thin layer, often propagating all information it has been provided by the underlying system call. On failure Asio will populate a boost::system::error_code if the application is capable of receiving it, such as for asynchronous operations or synchronous operation overloads with the error_code parameter; otherwise it will throw an exception containing the error_code. The documentation explicitly mentions asynchronous operations, but the same "as if" behavior is often applicable to synchronous operations:
Unless otherwise noted, when the behaviour of an asynchronous operation is defined "as if" implemented by a POSIX function, the handler will be invoked with a value of type error_code that corresponds to the failure condition described by POSIX for that function, if any. Otherwise the handler will be invoked with an implementation-defined error_code value that reflects the operating system error.
Asynchronous operations will not fail with an error condition that indicates interruption by a signal (POSIX EINTR). Asynchronous operations will not fail with any error condition associated with non-blocking operations (POSIX EWOULDBLOCK, EAGAIN or EINPROGRESS; Windows WSAEWOULDBLOCK or WSAEINPROGRESS).
To further investigate an error, consider using the BSD API mapping documentation to determine which OS calls are being made. One can then use the appropriate OS documentation to determine the conditions for which an error occurs and the values. The mapping between error codes and Boost.Asio error codes is located within asio/error.hpp, but the mapping is normally fairly straight forward.
In the case of sendto(), a return value of EINVAL may not provide insightful information. One could further diagnose the problem via attaching a debugger and examining the arguments for validity when Asio makes the system call to sendto().
I'm using C++ to send post-request with binary information. The code looks like:
int binary[4] = { 1, 2, 3, 4 };
std::stringstream out;
out << "POST /address HTTP/1.1\r\n";
out << "Host: localhost\r\n";
out << "Connection: Keep-Alive\r\n";
out << "Content-Type: application/octet-stream\r\n";
out << "Content-Transfer-Encoding: binary\r\n";
out << "Content-Length: " << 4*sizeof(int) << "\r\n\r\n"; // 4 elements of integer type
And sending data into opened connection in socket:
std::string headers = out.str();
socket.send(headers.c_str(), headers.size()); // Send headers first
socket.send(reinterpret_cast<char*>(&binary[0]), bufferLength*sizeof(int)); // And array of numbers
But I was told, that sending pure bytes through http-protocol is wrong. Is that right? For example, I can't send 0 (zero), it's used by protocol.
If that's right (because I can't handle that post-request and get the data I've sent) what could I use instead? Maybe, convert array into hex or base64url?
Thanks.
The problem people saying it's wrong are addressing is about the endianness. You can transfer binary data with http of course, but when the other end receives them, it must be able to interpret them correctly. Let's suppose your machine is a little endian machine; your integers will be, in memory, stored as (32 bit int)
01 00 00 00
02 00 00 00
03 00 00 00
04 00 00 00
and you send these 16 bytes as they "are". Now, suppose the receiving machine get the data naively disregarding who and how they are sent, and suppose that machine is a big endian machine; in such machine, the memory layout for 1, 2, 3, 4 intergers would be
00 00 00 01
00 00 00 02
00 00 00 03
00 00 00 04
This means that for the receiving machine the first integer is 0x01000000 which is not 0x00000001 as the sender wanted.
If you decide that your integers must be sent always as big endian integer, then if the sender is a little endian machine, it needs to "re-arrange" properly the integers before sending. There are functions like hton* (host to net) that "transforms" host 32/16 bit integers to the "net byte order" that is big endian (and viceversa, with ntoh* net to host)
Note that data are not scrambled, they are send as they "are", so to say. What changes is the way you store them in memory, and the way you interpret them when reading. Usually it's not an issue, since data are sent according to a format that, if needed, specifies the endianness of non-single-byte data (e.g. see PNG format spec, sec 2.1, integers byte order: PNG uses net byte order i.e. big endian)
But I was told, that sending pure bytes through http-protocol is
wrong. Is that right?
No, it is fine in the body, depending on the Content-Type of course. "Octet-stream" should be fine in this regard, and yes it can contain zero bytes.
There is nothing wrong to send binaries via HTTP.
This happens all the time with images and with file upload
I try to access a SmartCard via C++.
I got already the Connection and the CardHandle.
But when I send an APDU Command via SCardTransmit, i'll get 6E 00 as the answer from the card.
No matter which APDU Command i send.
Everytime 6E 00.
For Example:
FF CA FA 00 00 (Card's ATR - Answer To Reset) or
FF CA FF 82 00 (Product name in ASCII)
The same thing when i send the Command with an PC/SC Testtootl like "PC/SC Diag".
Has anybody an Idea what the meaning of this Error-Code and how to solve the problem?
Please help me !!!! ;-)
According to ISO 7816-4 0x6E00 means "Class not supported".
Are you using the correct CLA value in the APDU?
The class (CLA) byte is usually 0x00, 0xA0, 0xC0 or 0xF0 and sometimes masked with 0x0C that indicates Secure Messaging on some cards. AFAIK, the only invalid CLA value is 0xFF.
But this varies from one card to another, do you have the card specification from the vendor?
It means "Wrong Instruction Class". Maybe it's just the wrong type of card?
https://datatracker.ietf.org/doc/html/draft-urien-eap-smartcard-05
The BasicCard PDF manual has a list of error codes on page 152-153.
The one you got they describe as "CLA byte of command not recognized".
"6A 86" is likely the response to a card specific command and I dont see it in the BasicCard list.