+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 */
+ silc_socket_dup(sock);
+ 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"));
+ silc_socket_free(sock);
+ 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);
+ silc_socket_free(sock);
+ 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));
+ silc_socket_free(sock);
+ 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 blen %dB seq %d] [%s] proto %d",
+ sock->hostname, sock->port,
+ silc_get_packet_name(header[3]),
+ header[3], paddedlen, sock->inbuf->len, sequence,
+ (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+ sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+ sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+ "Router"),
+ sock->protocol ? sock->protocol->protocol->type : -1));
+ memset(tmp, 0, sizeof(tmp));
+ silc_buffer_clear(sock->inbuf);
+ silc_socket_free(sock);
+ return FALSE;
+ }
+
+ SILC_UNSET_INBUF_PENDING(sock);
+ parse_ctx = silc_calloc(1, sizeof(*parse_ctx));
+ if (!parse_ctx) {
+ silc_socket_free(sock);
+ return FALSE;
+ }
+ parse_ctx->packet = silc_packet_context_alloc();
+ parse_ctx->packet->buffer = silc_buffer_alloc_size(paddedlen);
+ parse_ctx->packet->type = (SilcPacketType)header[3];
+ parse_ctx->packet->padlen = (SilcUInt8)header[4];
+ parse_ctx->packet->sequence = sequence++;
+ parse_ctx->sock = sock;
+ parse_ctx->context = parser_context;
+
+ /* Check whether this is normal or special packet */
+ if (local_is_router) {
+ if (header[3] == SILC_PACKET_PRIVATE_MESSAGE &&
+ (header[2] & SILC_PACKET_FLAG_PRIVMSG_KEY))
+ parse_ctx->normal = FALSE;
+ else if (header[3] != SILC_PACKET_CHANNEL_MESSAGE ||
+ (header[3] == SILC_PACKET_CHANNEL_MESSAGE &&
+ sock->type == SILC_SOCKET_TYPE_ROUTER))
+ parse_ctx->normal = TRUE;
+ } else {
+ if (header[3] == SILC_PACKET_PRIVATE_MESSAGE &&
+ (header[2] & SILC_PACKET_FLAG_PRIVMSG_KEY))
+ parse_ctx->normal = FALSE;
+ else if (header[3] != SILC_PACKET_CHANNEL_MESSAGE)
+ parse_ctx->normal = TRUE;