Comments (7)
Can you confirm this issue?
Can you give an estimation when an update may be available?
Many thanks!
from p-net.
Yes, you are completely right, seel also issue #42
This is a high priority for us.
from p-net.
Thanks for your feedback. Can you already make a prediction when a new version could be available?
from p-net.
Hello, I create a fix for this issue and want to share it with you for review:
The function pf_cmrpc_dce_packet must look like this:
static int pf_cmrpc_dce_packet(
pnet_t *net,
uint32_t ip_addr,
uint16_t port,
uint8_t *p_req,
uint32_t req_len,
uint8_t *p_res_final,
uint16_t *p_res_len_final,
bool *p_is_release)
{
int ret = -1;
pf_rpc_header_t rpc_req;
pf_rpc_header_t rpc_res;
uint16_t start_pos = 0;
uint16_t length_of_body_pos = 0;
uint16_t req_pos = 0;
uint16_t res_pos = 0;
uint16_t res_len;
pf_get_info_t get_info;
pf_session_info_t *p_sess = NULL;
bool is_new_session = false;
get_info.result = PF_PARSE_OK;
get_info.p_buf = p_req;
get_info.len = req_len;
/* This function also sets get_info.is_big_endian */
pf_get_dce_rpc_header(&get_info, &req_pos, &rpc_req);
/* Find the session or allocate a new one */
pf_session_locate_by_uuid(net, &rpc_req.activity_uuid, &p_sess);
if (p_sess == NULL)
{
(void)pf_session_allocate(net, &p_sess);
is_new_session = true;
}
if (p_sess == NULL)
{
/* Unavailable */
LOG_ERROR(PF_RPC_LOG, "CMRPC(%d): Out of session resources.\n", __LINE__);
}
else
{
p_sess->get_info = get_info;
memset(&p_sess->rpc_result, 0, sizeof(p_sess->rpc_result));
/* Handle incoming RPC fragments: */
if (rpc_req.packet_type == PF_RPC_PT_FRAG_ACK)
{
/* Re-route the parser to use the session buffer */
req_pos = 0;
p_sess->get_info.p_buf = p_sess->buffer;
p_sess->get_info.len = p_sess->buf_len;
}
else
if (rpc_req.flags.fragment == false)
{
/* This is the normal path where the incoming request is contained entirely in one frame */
p_sess->buf_len = 0;
p_sess->ip_addr = ip_addr;
p_sess->port = port; /* Source port on incoming message */
p_sess->from_me = false;
p_sess->activity_uuid = rpc_req.activity_uuid;
p_sess->is_big_endian = p_sess->get_info.is_big_endian;
p_sess->fragment_nbr = 0;
}
else
{
/*
* It is a fragment of an incoming request.
* Collect all fragments into the session buffer and
* proceed only when the last fragment has been received.
*/
if (rpc_req.fragment_nmb == 0)
{
/* This is the first incoming fragment. */
/* Initialize the session */
p_sess->buf_len = 0;
p_sess->ip_addr = ip_addr;
p_sess->port = port;
p_sess->from_me = false;
p_sess->activity_uuid = rpc_req.activity_uuid;
p_sess->is_big_endian = p_sess->get_info.is_big_endian;
p_sess->fragment_nbr = 0;
}
else
{
/* All fragments must have same endianness in this implementation */
if (p_sess->is_big_endian != rpc_req.is_big_endian)
{
/*
* Endianness differs.
*/
LOG_ERROR(PF_RPC_LOG, "RPC(%d): Endianness differs in fragments\n", __LINE__);
pf_set_error(&p_sess->rpc_result, PNET_ERROR_CODE_CONNECT, PNET_ERROR_DECODE_PNIO, PNET_ERROR_CODE_1_CMRPC, PNET_ERROR_CODE_2_CMRPC_STATE_CONFLICT);
}
}
/* Enter here _even_if_ an error is already detected because we need to generate an error response. */
if (p_sess->fragment_nbr != rpc_req.fragment_nmb)
{
LOG_ERROR(PF_RPC_LOG, "CMRPC(%d): Unexpected fragment number %u != %u (exp)\n",
__LINE__, (unsigned)rpc_req.fragment_nmb, (unsigned)p_sess->fragment_nbr + 1);
pf_set_error(&p_sess->rpc_result, PNET_ERROR_CODE_CONNECT, PNET_ERROR_DECODE_PNIO, PNET_ERROR_CODE_1_CMRPC, PNET_ERROR_CODE_2_CMRPC_STATE_CONFLICT);
}
else if ((p_sess->buf_len + rpc_req.length_of_body) > sizeof(p_sess->buffer))
{
LOG_ERROR(PF_RPC_LOG, "CMRPC(%d): Fragments exceed max buffer\n", __LINE__);
pf_set_error(&p_sess->rpc_result, PNET_ERROR_CODE_CONNECT, PNET_ERROR_DECODE_PNIO, PNET_ERROR_CODE_1_CMRPC, PNET_ERROR_CODE_2_CMRPC_STATE_CONFLICT);
}
else
{
/* Copy to session buffer */
memcpy(&p_sess->buffer[p_sess->buf_len], &p_req[req_pos], rpc_req.length_of_body);
p_sess->buf_len += rpc_req.length_of_body;
p_sess->fragment_nbr++;
if (rpc_req.flags.last_fragment == true)
{
/* Re-route the parser to use the session buffer */
req_pos = 0;
p_sess->get_info.p_buf = p_sess->buffer;
p_sess->get_info.len = p_sess->buf_len;
}
}
}
/* Enter here _even_if_ an error is already detected because may we need to generate an error response. */
if ((rpc_req.flags.fragment == false) ||
(rpc_req.flags.last_fragment == true))
{
switch (rpc_req.packet_type)
{
case PF_RPC_PT_REQUEST:
pf_get_ndr_data(&p_sess->get_info, &req_pos, &p_sess->ndr_data);
p_sess->get_info.is_big_endian = true; /* From now on all is big-endian */
/* Prepare the response */
rpc_res = rpc_req;
rpc_res.packet_type = PF_RPC_PT_RESPONSE;
rpc_res.flags.last_fragment = false;
rpc_res.flags.fragment = false;
rpc_res.flags.no_fack = true;
rpc_res.flags.maybe = false;
rpc_res.flags.idempotent = true;
rpc_res.flags.broadcast = false;
rpc_res.flags2.cancel_pending = false;
rpc_res.fragment_nmb = 0;
rpc_res.is_big_endian = p_sess->get_info.is_big_endian;
res_len = sizeof(p_sess->res_buffer);
/* Insert the response header to get pos of rpc response body. */
pf_put_dce_rpc_header(&rpc_res, res_len, p_sess->res_buffer, &res_pos, &length_of_body_pos);
start_pos = res_pos; /* Save for later */
if (rpc_req.opnum == PF_RPC_DEV_OPNUM_RELEASE)
{
p_sess->release_in_progress = true; /* Tell everybody */
*p_is_release = true; /* Tell caller */
}
/* Handle the RPC request */
ret = pf_cmrpc_rpc_request(net, p_sess, req_pos, &rpc_req, res_len, p_sess->res_buffer, &res_pos);
/*
* FROM HERE ON:
* Do not use p_sess after this line.
* The RPC may have been a release. In that case the session does no longer exist.
*/
if (res_pos <= *p_res_len_final)
{ /* copy all data to final buffer */
memcpy (p_res_final, p_sess->res_buffer, res_pos);
}
else
{ /* send fragmented response */
/* mark buffer as fragment */
p_sess->res_buffer [2] |= (1u << PF_RPC_F_FRAGMENT);
p_sess->res_buffer [2] &= ~(1u << PF_RPC_F_NO_FACK);
/* store length of the data within the response buffer */
p_sess->res_buf_len = res_pos;
/* copy first fragment to final buffer */
res_pos = start_pos + (((*p_res_len_final - start_pos) / 64) * 64);
memcpy (p_res_final, p_sess->res_buffer, res_pos);
/* store response buffer position */
p_sess->res_buf_pos = res_pos;
}
/* Insert the real value of length_of_body in the rpc header */
pf_put_uint16(rpc_res.is_big_endian, (uint16_t)(res_pos - start_pos), *p_res_len_final, p_res_final, &length_of_body_pos);
break;
case PF_RPC_PT_FRAG_ACK:
if (p_sess->res_buf_len <= p_sess->res_buf_pos)
{ /* nothing else to send */
break;
}
pf_get_ndr_data(&p_sess->get_info, &req_pos, &p_sess->ndr_data);
p_sess->get_info.is_big_endian = true; /* From now on all is big-endian */
/* Prepare the response */
rpc_res = rpc_req;
rpc_res.packet_type = PF_RPC_PT_RESPONSE;
rpc_res.flags.last_fragment = false;
rpc_res.flags.fragment = true;
rpc_res.flags.no_fack = false;
rpc_res.flags.maybe = false;
rpc_res.flags.idempotent = true;
rpc_res.flags.broadcast = false;
rpc_res.flags2.cancel_pending = false;
rpc_res.fragment_nmb = 0;
rpc_res.is_big_endian = p_sess->get_info.is_big_endian;
/* Insert the response header to get pos of rpc response body. */
pf_put_dce_rpc_header(&rpc_res, *p_res_len_final, p_res_final, &res_pos, &length_of_body_pos);
start_pos = res_pos; /* Save for later */
/* increase fragment counter */
p_sess->res_buffer [length_of_body_pos+3]++;
/* copy original header */
memcpy (p_res_final, p_sess->res_buffer, start_pos);
if ((res_pos + (p_sess->res_buf_len - p_sess->res_buf_pos)) <= *p_res_len_final)
{ /* copy all data to final buffer */
memcpy (&p_res_final[res_pos], &p_sess->res_buffer [p_sess->res_buf_pos], p_sess->res_buf_len - p_sess->res_buf_pos);
res_pos += p_sess->res_buf_len - p_sess->res_buf_pos;
/* mark frame as last fragment */
p_res_final[2] |= (1u << PF_RPC_F_LAST_FRAGMENT);
/* reset response buffer length/pos */
p_sess->res_buf_len = 0;
p_sess->res_buf_pos = 0;
}
else
{ /* send next fragment */
size_t len;
len = (((*p_res_len_final - start_pos) / 64) * 64);
memcpy (&p_res_final[res_pos], &p_sess->res_buffer [p_sess->res_buf_pos], len);
/* store response buffer length */
p_sess->res_buf_pos += len;
res_pos += len;
}
/* Insert the real value of length_of_body in the rpc header */
pf_put_uint16(rpc_res.is_big_endian, (uint16_t)(res_pos - start_pos), *p_res_len_final, p_res_final, &length_of_body_pos);
break;
case PF_RPC_PT_RESPONSE:
pf_get_ndr_data(&p_sess->get_info, &req_pos, &p_sess->ndr_data);
p_sess->get_info.is_big_endian = true; /* From now on all is big-endian */
ret = pf_cmrpc_rpc_response(net, p_sess, req_pos, &rpc_req);
break;
case PF_RPC_PT_PING:
LOG_INFO(PF_RPC_LOG, "CMRPC(%d): Incoming DCE RPC ping on UDP\n", __LINE__);
/* Prepare the response */
rpc_res = rpc_req;
rpc_res.packet_type = PF_RPC_PT_RESP_PING;
rpc_res.flags.last_fragment = false;
rpc_res.flags.fragment = false;
rpc_res.flags.no_fack = true;
rpc_res.flags.maybe = false;
rpc_res.flags.idempotent = true;
rpc_res.flags.broadcast = false;
rpc_res.flags2.cancel_pending = false;
rpc_res.fragment_nmb = 0;
rpc_res.is_big_endian = p_sess->is_big_endian;
pf_put_dce_rpc_header(&rpc_res, *p_res_len_final, p_res_final, &res_pos, &length_of_body_pos);
if (is_new_session == true)
{
pf_session_release(p_sess);
}
break;
default:
LOG_ERROR(PF_RPC_LOG, "CMRPC(%d): Unknown packet_type %" PRIu8 "\n", __LINE__, rpc_req.packet_type);
break;
}
}
else
{
if ((rpc_req.flags.fragment == true) &&
(rpc_req.flags.no_fack == false))
{
/* Create Fragment ACK */
/* Send ACK */
rpc_res = rpc_req;
rpc_res.packet_type = PF_RPC_PT_FRAG_ACK;
rpc_res.flags.last_fragment = false;
rpc_res.flags.fragment = false;
rpc_res.flags.no_fack = false;
rpc_res.flags.maybe = false;
rpc_res.flags.idempotent = true;
rpc_res.flags.broadcast = false;
rpc_res.flags2.cancel_pending = false;
rpc_res.length_of_body = 0;
rpc_res.is_big_endian = p_sess->is_big_endian;
pf_put_dce_rpc_header(&rpc_res, *p_res_len_final, p_res_final, &res_pos, &length_of_body_pos);
}
else
{
/* Waiting for the next fragment. */
}
ret = 0;
}
}
/* Size of result data */
*p_res_len_final = res_pos;
return ret;
}
In pf_cmrpc_periodic the limitation of the response length should look like this (2 times):
dcerpc_resp_len = sizeof(net->cmrpc_dcerpc_rsp_frame) - (8 /*UDP header */ + 20 /* IP header */);
And the struct pf_session_info_t should get the following additional fields:
uint8_t res_buffer[20000]; /* Send/Receive buffer */
uint16_t res_buf_len;
uint16_t res_buf_pos;
Btw: The existing filed buffer should be increased. I get errors under some circumstances when this field is just 4500 bytes big. With 20000 everything works fine for me.
from p-net.
Can you confirm this fix?
from p-net.
Hi, great!
I will forward it to my colleague working on this part of the stack.
from p-net.
We have implemented a fix for this, see #62
Please review.
from p-net.
Related Issues (20)
- Using wlan0 as network interface doesn't work
- Sending a bunch of bytes of data to plc HOT 2
- closed
- pnet_get_station_name function need HOT 1
- Pnet port for rt-kernel by-passes lwip locking
- Feature request - IM5
- Feature request - Startup Mode Legacy
- Question - How to read/write multiple modules/submodules which have the same structure? HOT 3
- Possible issue: incorrect decoding of DCE/RPC lookup request HOT 1
- IM Data Cannot Set HOT 1
- buffer overflow bug when run `./pn_dev`
- build fail: use openwrt-22.03.3 toolchain for arm64
- Some PLCS cannot use GSD configuration
- Failing Behavior - Scenario 8 in newest ART release 2.44.1.2 Multiple AR Establishment Test HOT 3
- link in readme.md is broken HOT 3
- Question: Gathering Data From Profinet PLC's HOT 2
- MRP and Profisafe HOT 1
- Minimal C source code and Makefile
- The smart200 plc cannot be connected
- Error code: 0x05 Device missed cyclic data deadline
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from p-net.