bool G2Packet::SkipCompound(quint32& nLength, quint32 nRemaining) { quint32 nStart = m_nPosition; quint32 nEnd = m_nPosition + nLength; while ( m_nPosition < nEnd ) { char nInput = ReadByte(); if ( nInput == 0 ) break; char nLenLen = ( nInput & 0xC0 ) >> 6; char nTypeLen = ( nInput & 0x38 ) >> 3; char nFlags = ( nInput & 0x07 ); Q_UNUSED(nFlags); if ( m_nPosition + nTypeLen + nLenLen + 1 > nEnd ) throw packet_error(); quint32 nPacket = 0; Read( &nPacket, nLenLen ); if ( m_nPosition + nTypeLen + 1 + nPacket > nEnd ) throw packet_error(); m_nPosition += nPacket + nTypeLen + 1; } nEnd = m_nPosition - nStart; if ( nEnd > nLength ) throw packet_error(); nLength -= nEnd; return nRemaining ? nLength >= nRemaining : true; }
bool G2Packet::ReadPacket(char* pszType, quint32& nLength, bool* pbCompound) { if ( GetRemaining() == 0 ) return false; char nInput = ReadByte(); if ( nInput == 0 ) return false; char nLenLen = ( nInput & 0xC0 ) >> 6; char nTypeLen = ( nInput & 0x38 ) >> 3; char nFlags = ( nInput & 0x07 ); if ( GetRemaining() < nTypeLen + nLenLen + 1 ) throw packet_error(); nLength = 0; Read( &nLength, nLenLen ); if ( GetRemaining() < (int)nLength + nTypeLen + 1 ) throw packet_error(); Read( pszType, nTypeLen + 1 ); pszType[ nTypeLen + 1 ] = 0; if ( pbCompound ) { *pbCompound = ( nFlags & G2_FLAG_COMPOUND ) == G2_FLAG_COMPOUND; } else { if ( nFlags & G2_FLAG_COMPOUND ) SkipCompound( nLength ); } return true; }
G2Packet* G2Packet::ReadBuffer(QByteArray* pBuffer) { if ( pBuffer == 0 ) return 0; if ( pBuffer->size() < 2 ) return 0; char nInput = *pBuffer[0]; if ( nInput == 0 ) { pBuffer->remove(0, 1); return 0; } char nLenLen = ( nInput & 0xC0 ) >> 6; char nTypeLen = ( nInput & 0x38 ) >> 3; char nFlags = ( nInput & 0x07 ); if ( (quint32)pBuffer->size() < (quint32)nLenLen + nTypeLen + 2u ) return 0; quint32 nLength = 0; if ( nFlags & G2_FLAG_BIG_ENDIAN ) { throw packet_error(); } else { char* pLenIn = pBuffer->data() + 1; char* pLenOut = (char*)&nLength; for ( char nLenCnt = nLenLen ; nLenCnt-- ; ) *pLenOut++ = *pLenIn++; } if ( (quint32)pBuffer->size() < (quint32)nLength + nLenLen + nTypeLen + 2 ) return 0; G2Packet* pPacket = G2Packet::New( pBuffer->data() ); pBuffer->remove(0, nLength + nLenLen + nTypeLen + 2 ); return pPacket; }
/* trigger a run of the send queue */ _PUBLIC_ void packet_queue_run(struct packet_context *pc) { while (pc->send_queue) { struct send_element *el = pc->send_queue; NTSTATUS status; size_t nwritten; DATA_BLOB blob = data_blob_const(el->blob.data + el->nsent, el->blob.length - el->nsent); status = socket_send(pc->sock, &blob, &nwritten); if (NT_STATUS_IS_ERR(status)) { packet_error(pc, status); return; } if (!NT_STATUS_IS_OK(status)) { return; } el->nsent += nwritten; if (el->nsent == el->blob.length) { DLIST_REMOVE(pc->send_queue, el); if (el->send_callback) { pc->busy = true; el->send_callback(el->send_callback_private); pc->busy = false; if (pc->destructor_called) { talloc_free(pc); return; } } talloc_free(el); } } /* we're out of requests to send, so don't wait for write events any more */ TEVENT_FD_NOT_WRITEABLE(pc->fde); }
G2Packet* G2Packet::New(char* pSource) { G2Packet* pPacket = New(); char nInput = *pSource++; char nLenLen = ( nInput & 0xC0 ) >> 6; char nTypeLen = ( nInput & 0x38 ) >> 3; char nFlags = ( nInput & 0x07 ); pPacket->m_bCompound = ( nFlags & G2_FLAG_COMPOUND ) ? true : false; bool bBigEndian = ( nFlags & G2_FLAG_BIG_ENDIAN ) ? true : false; quint32 nLength = 0; if ( bBigEndian ) { throw packet_error(); } else { char* pLenOut = (char*)&nLength; while ( nLenLen-- ) *pLenOut++ = *pSource++; } nTypeLen++; char* pszType = pPacket->m_sType; for ( ; nTypeLen-- ; ) { *pszType++ = *pSource++; } *pszType++ = 0; pPacket->Write( pSource, nLength ); return pPacket; }
/* call this when the socket becomes readable to kick off the whole stream parsing process */ _PUBLIC_ void packet_recv(struct packet_context *pc) { size_t npending; NTSTATUS status; size_t nread = 0; DATA_BLOB blob; bool recv_retry = false; if (pc->processing) { TEVENT_FD_NOT_READABLE(pc->fde); pc->processing++; return; } if (pc->recv_disable) { pc->recv_need_enable = true; TEVENT_FD_NOT_READABLE(pc->fde); return; } if (pc->packet_size != 0 && pc->num_read >= pc->packet_size) { goto next_partial; } if (pc->packet_size != 0) { /* we've already worked out how long this next packet is, so skip the socket_pending() call */ npending = pc->packet_size - pc->num_read; } else if (pc->initial_read != 0) { npending = pc->initial_read - pc->num_read; } else { if (pc->sock) { status = socket_pending(pc->sock, &npending); } else { status = NT_STATUS_CONNECTION_DISCONNECTED; } if (!NT_STATUS_IS_OK(status)) { packet_error(pc, status); return; } } if (npending == 0) { packet_eof(pc); return; } again: if (npending + pc->num_read < npending) { packet_error(pc, NT_STATUS_INVALID_PARAMETER); return; } if (npending + pc->num_read < pc->num_read) { packet_error(pc, NT_STATUS_INVALID_PARAMETER); return; } /* possibly expand the partial packet buffer */ if (npending + pc->num_read > pc->partial.length) { if (!data_blob_realloc(pc, &pc->partial, npending+pc->num_read)) { packet_error(pc, NT_STATUS_NO_MEMORY); return; } } if (pc->partial.length < pc->num_read + npending) { packet_error(pc, NT_STATUS_INVALID_PARAMETER); return; } if ((uint8_t *)pc->partial.data + pc->num_read < (uint8_t *)pc->partial.data) { packet_error(pc, NT_STATUS_INVALID_PARAMETER); return; } if ((uint8_t *)pc->partial.data + pc->num_read + npending < (uint8_t *)pc->partial.data) { packet_error(pc, NT_STATUS_INVALID_PARAMETER); return; } status = socket_recv(pc->sock, pc->partial.data + pc->num_read, npending, &nread); if (NT_STATUS_IS_ERR(status)) { packet_error(pc, status); return; } if (recv_retry && NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { nread = 0; status = NT_STATUS_OK; } if (!NT_STATUS_IS_OK(status)) { return; } if (nread == 0 && !recv_retry) { packet_eof(pc); return; } pc->num_read += nread; if (pc->unreliable_select && nread != 0) { recv_retry = true; status = socket_pending(pc->sock, &npending); if (!NT_STATUS_IS_OK(status)) { packet_error(pc, status); return; } if (npending != 0) { goto again; } } next_partial: if (pc->partial.length != pc->num_read) { if (!data_blob_realloc(pc, &pc->partial, pc->num_read)) { packet_error(pc, NT_STATUS_NO_MEMORY); return; } } /* see if its a full request */ blob = pc->partial; blob.length = pc->num_read; status = pc->full_request(pc->private_data, blob, &pc->packet_size); if (NT_STATUS_IS_ERR(status)) { packet_error(pc, status); return; } if (!NT_STATUS_IS_OK(status)) { return; } if (pc->packet_size > pc->num_read) { /* the caller made an error */ DEBUG(0,("Invalid packet_size %lu greater than num_read %lu\n", (long)pc->packet_size, (long)pc->num_read)); packet_error(pc, NT_STATUS_INVALID_PARAMETER); return; } /* it is a full request - give it to the caller */ blob = pc->partial; blob.length = pc->num_read; if (pc->packet_size < pc->num_read) { pc->partial = data_blob_talloc(pc, blob.data + pc->packet_size, pc->num_read - pc->packet_size); if (pc->partial.data == NULL) { packet_error(pc, NT_STATUS_NO_MEMORY); return; } /* Trunate the blob sent to the caller to only the packet length */ if (!data_blob_realloc(pc, &blob, pc->packet_size)) { packet_error(pc, NT_STATUS_NO_MEMORY); return; } } else { pc->partial = data_blob(NULL, 0); } pc->num_read -= pc->packet_size; pc->packet_size = 0; if (pc->serialise) { pc->processing = 1; } pc->busy = true; status = pc->callback(pc->private_data, blob); pc->busy = false; if (pc->destructor_called) { talloc_free(pc); return; } if (pc->processing) { if (pc->processing > 1) { TEVENT_FD_READABLE(pc->fde); } pc->processing = 0; } if (!NT_STATUS_IS_OK(status)) { packet_error(pc, status); return; } /* Have we consumed the whole buffer yet? */ if (pc->partial.length == 0) { return; } /* we got multiple packets in one tcp read */ if (pc->ev == NULL) { goto next_partial; } blob = pc->partial; blob.length = pc->num_read; status = pc->full_request(pc->private_data, blob, &pc->packet_size); if (NT_STATUS_IS_ERR(status)) { packet_error(pc, status); return; } if (!NT_STATUS_IS_OK(status)) { return; } tevent_add_timer(pc->ev, pc, timeval_zero(), packet_next_event, pc); }
/* tell the caller we have EOF */ static void packet_eof(struct packet_context *pc) { packet_error(pc, NT_STATUS_END_OF_FILE); }