⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 wtpsar.c

📁 是一个手机功能的模拟程序
💻 C
📖 第 1 页 / 共 3 页
字号:
/*
 * 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 + -