📄 wtpsar.c
字号:
/*
* Unpack the header of a result PDU.
* Returns NULL in case of error,
* otherwise returns a segment representing the original PDU.
*/
static wtp_result_segment_t *
wtp_unpack_result_pdu (pdubuf *pdu)
{
wtp_result_segment_t *rseg = NULL;
BYTE *pb = pdubuf_getStart (pdu);
UINT16 length = pdubuf_getLength (pdu);
UINT8 pdu_type;
if (length < 3)
goto err_return;
rseg = (wtp_result_segment_t*)OSConnectorAlloc (sizeof (wtp_result_segment_t));
rseg->pdu = pdu;
pdu_type = (pb[0] >> 3) & 0xf;
rseg->flags = pb[0] & 0x7;
if (pdu_type == WTP_PDU_TYPE_SEGMENTED_RESULT) {
if (length < 4)
goto err_return;
rseg->segnum = pb[3];
}
else if (pdu_type == WTP_PDU_TYPE_RESULT) {
rseg->segnum = 0;
}
else {
goto err_return;
}
return rseg;
err_return:
pdubuf_release (pdu);
if (rseg)
OSConnectorFree (rseg);
return NULL;
}
/*
* Add a segment to a transaction's list of saved segments.
* Returns 0 if the segment could be added,
* 1 if the segment belongs to an old grop already completed,
* and 2 if the segment is a duplicate in the current group.
*/
static int
wtp_add_result_segment (wtp_transaction_t *tr,
wtp_result_segment_t *rseg)
{
wtp_result_segment_t *p, *q;
UINT16 size;
BYTE *pb;
if (rseg->segnum < tr->first_segnum_in_current_group) {
/* Old segment belonging to a group already completed. */
return 1;
}
for (q = NULL, p = tr->result_list; p != NULL; q = p, p = p->next) {
if (p->segnum == rseg->segnum) {
/* Duplicate segment. */
return 2;
}
if (p->segnum > rseg->segnum) {
break;
}
}
/* Insert before p */
rseg->next = p;
if (q)
q->next = rseg;
else
tr->result_list = rseg;
pb = pdubuf_getStart (rseg->pdu);
size = pdubuf_getLength (rseg->pdu) - WTP_PDU_RESULT_HEADERLEN;
if (((pb[0] >> 3) & 0xf) == WTP_PDU_TYPE_SEGMENTED_RESULT)
size--;
tr->recv_msg_size += size;
tr->recv_group_size += size;
tr->num_recv_segments += 1;
tr->flags &= ~WTP_FLAG_SENT_NACK;
return 0;
}
/*
* Check if we have all segments belonging to the current group.
* Returns NULL if there are segments missing, and
* a pointer to the last segment in the group otherwise.
*/
static wtp_result_segment_t *
wtp_check_group_complete (wtp_transaction_t *tr)
{
wtp_result_segment_t *rseg;
for (rseg = tr->result_list; rseg->next != NULL; rseg = rseg->next);
if ((rseg->flags & (WTP_FLAG_GTR | WTP_FLAG_TTR)) &&
(rseg->segnum - tr->first_segnum_in_current_group + 1 ==
tr->num_recv_segments)) {
return rseg;
}
return NULL;
}
/*
* Create a NACK PDU, holding a list of all missing segments
* in the current group.
* This routine is allowed to assume that the last segment
* in the list of segments has the GTR or TTR flag set; that is,
* the last segment in the list is also the last segment of
* the current group.
*/
static void
wtp_create_nack_pdu (wtp_transaction_t *tr, UINT8 resend,
pdubuf **nack_pdu_ptr)
{
pdubuf *pdu;
BYTE *pb;
UINT8 num_missing_segments;
wtp_result_segment_t *first_seg = NULL, *last_seg, *seg;
UINT8 i, exp_segnum;
/* Find first and last received segments in current group */
for (seg = tr->result_list; seg != NULL; seg = seg->next) {
if ((seg->segnum == tr->first_segnum_in_current_group) ||
((first_seg == NULL) &&
(seg->segnum > tr->first_segnum_in_current_group)))
first_seg = seg;
if (seg->next == NULL)
last_seg = seg;
}
num_missing_segments = last_seg->segnum
- tr->first_segnum_in_current_group + 1 - tr->num_recv_segments;
pdu = pdubuf_new ((UINT16)(4 + num_missing_segments));
pdubuf_setLength (pdu, (UINT16)(4 + num_missing_segments));
pb = pdubuf_getStart (pdu);
pb[0] = (WTP_PDU_TYPE_NACK << 3);
if (resend)
pb[0] |= 0x01;
pb[1] = (tr->tid >> 8);
pb[2] = (tr->tid & 0xff);
pb[3] = num_missing_segments;
i = 4;
exp_segnum = tr->first_segnum_in_current_group;
seg = first_seg;
while (seg) {
if (exp_segnum < seg->segnum) {
pb[i++] = exp_segnum;
}
else {
seg = seg->next;
}
exp_segnum += 1;
}
tr->flags |= WTP_FLAG_SENT_NACK;
*nack_pdu_ptr = pdu;
}
/*
* Create a result PDU-buffer, holding the contents of
* either all segments in the current group, or all segments
* from the very beginning.
*/
static pdubuf *
wtp_create_result_sdu (wtp_transaction_t *tr)
{
pdubuf *pdu;
UINT16 result_size, n, sn;
BYTE *pb;
wtp_result_segment_t *seg;
if (tr->result_list->segnum > 0)
result_size = (UINT16)tr->recv_group_size;
else
result_size = (UINT16)tr->recv_msg_size;
if (tr->result_list->next == NULL) {
/* Only one segment, re-use the PDU-buffer. */
seg = tr->result_list;
tr->result_list = NULL;
pdu = seg->pdu;
sn = seg->segnum;
OSConnectorFree (seg);
}
else {
pdu = pdubuf_new (result_size);
pdubuf_setLength (pdu, result_size);
pb = pdubuf_getStart (pdu);
while (tr->result_list != NULL) {
seg = tr->result_list;
tr->result_list = seg->next;
sn = seg->segnum;
n = pdubuf_getLength (seg->pdu);
memcpy (pb, pdubuf_getStart (seg->pdu), n);
pb += n;
pdubuf_release (seg->pdu);
OSConnectorFree (seg);
}
}
wtp_group_done (tr, sn);
return pdu;
}
/*
* Create an ACK PDU that serves as acknowledgement
* of segment number segnum.
*/
static void
wtp_sar_create_ack_pdu (wtp_transaction_t *tr,
UINT8 segnum, UINT8 resend,
pdubuf **ack_pdu_ptr)
{
pdubuf *pdu;
BYTE *pb;
UINT16 length = 5;
UINT16 add_tpis;
add_tpis = (tr->first_segnum_in_current_group == 0) ||
(resend && (tr->first_segnum_in_previous_group == 0));
if (add_tpis) {
/* This is the first ACK to a segmented result.
* We should add TPIs. */
length += 4;
if (tr->max_recv_group_size > 0xffff)
length += 3;
}
pdu = pdubuf_new (length);
pdubuf_setLength (pdu, length);
pb = pdubuf_getStart (pdu);
pb[0] = 0x80 | (WTP_PDU_TYPE_ACK << 3);
if (resend)
pb[0] |= 0x01;
pb[1] = (tr->tid >> 8);
pb[2] = (tr->tid & 0xff);
pb[3] = (WTP_TPI_PSN << 3) | 0x01;
pb[4] = segnum;
if (add_tpis) {
pb[3] |= 0x80;
pb += 5;
if (tr->max_recv_group_size > 0xffff) {
pb[0] = (WTP_TPI_OPTION << 3) | 0x04;
pb[1] = 5;
pb[2] = WTP_TPI_OPTION_MAX_GROUP_SIZE;
wtp_put_uintn (tr->max_recv_group_size, 4, pb + 3);
}
else {
pb[0] = (WTP_TPI_OPTION << 3) | 0x3;
pb[1] = WTP_TPI_OPTION_MAX_GROUP_SIZE;
wtp_put_uintn (tr->max_recv_group_size, 2, pb + 2);
}
}
*ack_pdu_ptr = pdu;
}
/*
* This routine is called when a result (segment) has arrived.
* The following actions are performed:
*
* Unpack the PDU header.
*
* Try to add this segment to the list of previously saved segments.
* If it is a duplicate,
* discard the segment,
* return WTP_RET_WAIT_RESULT, unless it is a resend of the last
* message with the GTR or TTR flag set, in which case return
* WTP_RET_SEND_ACK.
*
* If the total message size now exceeds our limit, OR
* if the total group size exceeds our limit, OR if the packet
* has the GTR or TTR flag set even though we have already received
* packets with higher sequence numbers, then
* return WTP_RET_ERROR.
*
* Update local parameters according to the TPIs.
*
* Check if all segments of the current group are now present.
*
* If all segments in the current group are NOT present, then
* if it is the last segment of a group, or last in message, then
* create a NACK pdu and return WTP_RET_SEND_ACK
* else
* return WTP_RET_WAIT_RESULT.
*
* If this is the last segment in the message, create a result pdu
* with the present segments, and return WTP_RET_MESSAGE_COMPLETED.
*
* If this is the end of a group (but not the last one), then
* if we are doing segmented result-delivery,
* create a result pdu and return WTP_RET_GROUP_COMPLETED.
* else
* create an ACK pdu
* return WTP_RET_SEND_ACK.
*
*/
SDL_Integer
wtp_process_result_pdu (void *trptr,
pdubuf *pdu,
pdubuf **ack_pdu_ptr,
TRResultIndType *result_ind,
SDL_Natural *abort_code)
{
wtp_transaction_t *tr = (wtp_transaction_t *)trptr;
wtp_result_segment_t *rseg, *last_seg;
int r, retval = WTP_RET_WAIT_RESULT;
*ack_pdu_ptr = NULL;
result_ind->UserData = NULL;
rseg = wtp_unpack_result_pdu (pdu);
if (rseg == NULL) {
wtp_delete_transaction (tr);
*abort_code = WTP_ERR_PROTOERR;
return WTP_RET_ERROR;
}
r = wtp_add_result_segment (tr, rseg);
if (r) {
if ((rseg->flags & WTP_FLAG_RID) &&
(rseg->flags & (WTP_FLAG_GTR | WTP_FLAG_TTR))) {
/* This packet is a duplicate, but since it has the GTR or TTR
* flag set, we may have to acknowledge it anyway. */
if ((r == 1)
&& (rseg->segnum + 1 == tr->first_segnum_in_current_group)
&& (tr->num_recv_segments == 0)) {
wtp_sar_create_ack_pdu (tr, rseg->segnum, 1, ack_pdu_ptr);
retval = WTP_RET_SEND_ACK;
}
else if (r == 2) {
wtp_create_nack_pdu (tr, (UINT8)(tr->flags & WTP_FLAG_SENT_NACK),
ack_pdu_ptr);
retval = WTP_RET_SEND_ACK;
}
}
wtp_delete_result_segment (rseg);
return retval;
}
if (tr->recv_group_size > tr->max_recv_group_size) {
wtp_delete_transaction (tr);
*abort_code = WTP_ERR_MESSAGETOOLARGE;
return WTP_RET_ERROR;
}
if ((rseg->flags & (WTP_FLAG_GTR | WTP_FLAG_TTR)) && (rseg->next != NULL)) {
wtp_delete_transaction (tr);
*abort_code = WTP_ERR_PROTOERR;
return WTP_RET_ERROR;
}
if (wtp_read_tpis (tr, rseg->pdu) < 0) {
wtp_delete_transaction (tr);
*abort_code = WTP_ERR_PROTOERR;
return WTP_RET_ERROR;
}
last_seg = wtp_check_group_complete (tr);
if (!last_seg) {
if (rseg->flags & (WTP_FLAG_GTR | WTP_FLAG_TTR)) {
wtp_create_nack_pdu (tr, 0, ack_pdu_ptr);
return WTP_RET_SEND_ACK;
}
return WTP_RET_WAIT_RESULT;
}
if (last_seg->flags & WTP_FLAG_TTR) {
result_ind->UserData = wtp_create_result_sdu (tr);
result_ind->Handle = tr->handle;
result_ind->MoreData = SDL_False;
result_ind->TotalSize = tr->total_recv_msg_size;
return WTP_RET_MESSAGE_COMPLETED;
}
if (tr->flags & WTP_FLAG_SPLIT_RESULT) {
wtp_sar_create_ack_pdu (tr, last_seg->segnum, 0, ack_pdu_ptr);
result_ind->UserData = wtp_create_result_sdu (tr);
result_ind->Handle = tr->handle;
result_ind->MoreData = SDL_True;
result_ind->TotalSize = tr->total_recv_msg_size;
return WTP_RET_GROUP_COMPLETED;
}
wtp_sar_create_ack_pdu (tr, last_seg->segnum, 0, ack_pdu_ptr);
wtp_group_done (tr, last_seg->segnum);
return WTP_RET_SEND_ACK;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -