+ /* Allocate more space if needed */
+ if ((sock->outbuf->end - sock->outbuf->tail) < (totlen + mac_len)) {
+ SILC_LOG_DEBUG(("Reallocating outgoing data buffer"));
+ sock->outbuf = silc_buffer_realloc(sock->outbuf,
+ sock->outbuf->truelen + (totlen * 2));
+ if (!sock->outbuf)
+ return FALSE;
+ }
+
+ /* Pull data area for the new packet, and return pointer to the start of
+ the data area and save the pointer in to the `packet'. */
+ oldptr = silc_buffer_pull_tail(sock->outbuf, totlen + mac_len);
+ silc_buffer_set(packet, oldptr, totlen + mac_len);
+ silc_buffer_push_tail(packet, mac_len);
+
+ return TRUE;
+}
+
+/******************************************************************************
+
+ Packet Reception Routines
+
+******************************************************************************/
+
+static int silc_packet_decrypt(SilcCipher cipher, SilcHmac hmac,
+ SilcUInt32 sequence, SilcBuffer buffer,
+ bool normal);
+static bool silc_packet_check_mac(SilcHmac hmac,
+ const unsigned char *data,
+ SilcUInt32 data_len,
+ const unsigned char *packet_mac,
+ SilcUInt32 sequence);
+
+/* Receives packet from network and reads the data into connection's
+ incoming data buffer. If the data was read directly this returns the
+ read bytes, if error occured this returns -1, if the data could not
+ be read directly at this time this returns -2 in which case the data
+ should be read again at some later time, or If EOF occured this returns
+ 0. */
+
+int silc_packet_receive(SilcSocketConnection sock)
+{
+ int ret;
+
+ SILC_LOG_DEBUG(("Receiving packet from %s:%d [%s]", sock->hostname,
+ sock->port,
+ (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+ sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+ sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+ "Router")));
+
+ /* Read some data from connection */
+ ret = silc_socket_read(sock);
+
+ return ret;
+}
+
+/* Processes and decrypts the incmoing data, and calls parser callback
+ for each received packet that will handle the actual packet parsing.
+ If more than one packet was received this calls the parser multiple
+ times. The parser callback will get context SilcPacketParserContext
+ that includes the packet and the `parser_context' sent to this
+ function.
+
+ The `local_is_router' indicates whether the caller is router server
+ in which case the receiving process of a certain packet types may
+ be special. Normal server and client must set it to FALSE. The
+ SilcPacketParserContext will indicate also whether the received
+ packet was normal or special packet. */
+
+bool silc_packet_receive_process(SilcSocketConnection sock,
+ bool local_is_router,
+ SilcCipher cipher, SilcHmac hmac,
+ SilcUInt32 sequence,
+ SilcPacketParserCallback parser,
+ void *parser_context)
+{
+ SilcPacketParserContext *parse_ctx;
+ SilcUInt16 packetlen;
+ SilcUInt32 paddedlen, mac_len = 0, block_len;
+ int ret;
+ bool cont = TRUE;
+ unsigned char tmp[SILC_PACKET_MIN_HEADER_LEN], *header;
+ unsigned char iv[SILC_CIPHER_MAX_IV_SIZE];
+
+ /* Do not process for disconnected connection */
+ if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock))
+ return TRUE;
+
+ if (sock->inbuf->len < SILC_PACKET_MIN_HEADER_LEN)
+ return TRUE;
+
+ if (hmac)
+ mac_len = silc_hmac_len(hmac);
+
+ /* Parse the packets from the data */
+ while (sock->inbuf->len > 0 && cont) {
+
+ if (sock->inbuf->len < SILC_PACKET_MIN_HEADER_LEN) {
+ SILC_LOG_DEBUG(("Partial packet in queue, waiting for the rest"));
+ return TRUE;
+ }
+
+ /* Decrypt first block of the packet to get the length field out */
+ if (cipher) {
+ block_len = silc_cipher_get_block_len(cipher);
+ memcpy(iv, silc_cipher_get_iv(cipher), block_len);
+ silc_cipher_decrypt(cipher, sock->inbuf->data, tmp, block_len, iv);
+ header = tmp;
+ } else {
+ block_len = SILC_PACKET_MIN_HEADER_LEN;
+ header = sock->inbuf->data;
+ }
+
+ /* Get packet lenght and full packet length with padding */
+ SILC_PACKET_LENGTH(header, packetlen, paddedlen);
+
+ /* Sanity checks */
+ if (packetlen < SILC_PACKET_MIN_LEN) {
+ SILC_LOG_ERROR(("Received too short packet"));
+ memset(header, 0, sizeof(header));
+ silc_buffer_clear(sock->inbuf);
+ return FALSE;
+ }
+
+ if (sock->inbuf->len < paddedlen + mac_len) {
+ SILC_LOG_DEBUG(("Received partial packet, waiting for the rest "
+ "(%d bytes)", paddedlen + mac_len - sock->inbuf->len));
+ SILC_SET_INBUF_PENDING(sock);
+ memset(tmp, 0, sizeof(tmp));
+ return TRUE;
+ }
+
+ /* Check MAC of the packet */
+ if (!silc_packet_check_mac(hmac, sock->inbuf->data, paddedlen,
+ sock->inbuf->data + paddedlen, sequence)) {
+ SILC_LOG_WARNING(("Packet MAC check failed %s:%d "
+ "[%s type %d len %dB seq %d] [%s]",
+ sock->hostname, sock->port,
+ silc_get_packet_name(header[3]),
+ header[3], paddedlen, sequence,
+ (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+ sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+ sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+ "Router")));
+ memset(tmp, 0, sizeof(tmp));
+ silc_buffer_clear(sock->inbuf);
+ return FALSE;
+ }