Пример #1
0
/*
 *===========================================================================
 *                    ipnet_nat_proxy_h225_msg
 *===========================================================================
 * Description: Track H.225 packets.
 * This function handles the H.225/Q.931 call signalling packets.  NAT calls
 * this function after translating the address and port number in the IP and
 * TCP headers, and when the source or destination port is the H.323 port
 * registered to NAT during the H.323 ALG registration (the standard port for
 * H.323 protocol is 1720).  The H.225 call signalling protocol is used in H.323
 * to establish connection between two end-points.
 *
 * This function must look at both an outbound as well as an inbound packet.
 * H.323 connection can be attempted from local to global endpoint or vice
 * versa.
 *
 * NOTE 1:
 * The fields in the H.225 message are ASN.1 encoded. However, due to schedule,
 * constraint, rather than employing an ASN.1 decoder, this function uses a simple
 * strategy to look for the ip+port address in the H.225 payload by taking advantage
 * of the fact that the port number always follows immediately after the ip address.
 * Since the ALG can get the expected ip address from NAT, it can search byte by
 * byte for this ip address to locate where it is in the H.225 payload..
 *
 * Local host (L) <---------------> NAT <----------------> Global host (G)
 *
 * The tuple of IP address and TCP/UDP port number is sometimes called transport
 * address in some publications, and the same terminology will be used here.
 *
 * Case 1:  Local (L) endpoint to global (G) endpoint connection
 *
 * L starts a TCP connection to G and the H.225 call signaling session starts.
 * The establishment of this TCP connection is all handled by NAT, so by now,
 * NAT should have the bind entry for this connection.  During the H.225 session,
 * L will embed its sourceCallSignalAddress (ip/port) and G's transport address
 * in the H.225 payload to G.  Thus, this function must parse for each H.225
 * outbound packet, look for L's transport address, and substitute it with the
 * translated address obtained from NAT.
 *
 * In addition, this function must also observe the H.225 inbound packets from G
 * to look for the H.245 transport address (ip/port) in the connect message from
 * G.  The port number provided by G here will serve as the H.245 port number.
 * L will use this port number to open the H.245 TCP connection with G.  So upon
 * obtaining the H.245 port number, this function must also register the H.245
 * ALG agent to NAT .
 *
 * Case 2:  Global (G) endpoint to local (L) endpoint connection
 *
 * G starts a TCP connection to L via the NAT's H.323 static entry and the H.225
 * call signaling session starts.  As in case 1, the establishment of this TCP
 * connection is all handled by NAT, so by now, NAT should have the bind entry
 * for this connection.  During the H.225 session, G will embed its sourceCall-
 * SignalAddress and L's global transport address in the H.225 payload.  So, this
 * function must examine all inbound H.225 packets and substitute L's global
 * transport address with its real transport address.
 *
 * In addition, this function must also observe the H.225 outbound packets
 * to look for L's H.245 transport address which is embedded in the connect
 * message from L to G. The port number embedded in this message will serve as
 * the H.245 port number.
 *
 * G will use this port number to open the H.245 TCP connection with L.  So, this
 * function must also register the H.245 ALG agent to NAT upon obtaining the
 * H.245 port number.  Furthermore, since the H.245 TCP connection will be
 * started from G to L, this function must create a new TCP bind entry for the
 * impending H.245 port connection.
 *
 * NOTE 2:
 * TCP sequence adjustment is not required since it is a binary substitution of
 * IP address and port number in the payload.  However, the checksum in the TCP
 * header must be adjusted when the TCP payload is modified.
 *
 * Parameters:  appdata   - pointer to application data.
 *              applen    - pointer to length of application data.
 *              param     - pointer to proxy parameters.
 * Returns:     1 = Packet modified.
 *              0 = Packet untouched.
 *             -1 = Drop packet.
 */
IP_STATIC int
ipnet_nat_proxy_h225_msg(Ip_u8 *appdata,
                         int    applen,
                         Ipnet_nat_proxy_param *param)
{
    Ipnet_nat_proxy_tuple proxy_tuple;
    Ip_u8   *data, *data_start;
    Ip_u16  local_port, port;
    Ip_u32  remote_address, local_address, ip_addr;
    int     data_length, tmp;
    Ip_bool mod = IP_FALSE;

    data_length = applen;       /* length of TCP payload */
    if (data_length <= 0)
    {
        /* no payload to look at */
        return 0;
    }

    /* go to start of TCP payload */
    data = appdata;
    data_start = data;
    local_address  = param->tuple.private_addr;
    local_port     = param->tuple.private_port;
    remote_address = param->tuple.public_addr;

    if (param->incoming == IP_FALSE)    /* outbound packet */
    {
        /* get the H.225 TCP bind descriptor using L's translated source transport
           address from the source address in the TCP/IP header.  From the bind
           descriptor, obtain L's real transport address and the session flow (i.e.
           who starts the connection first) of this connection.
        */
        if (param->inbound == IP_FALSE)     /* session started by L */
        {
            IPCOM_LOG0(DEBUG, "ipnet_nat_proxy_h225_msg() :: outbound packet, outbound session. "
                              "Look for sourceCallSignalAddress from local host.");

            while ((data + 6) <= (data_start + data_length))
            {
                ip_addr = IP_GET_NTOHL(data);

                /* look for L's sourceCallSignalAddress match */
                if (ip_addr == local_address)
                {
                    port = IP_GET_NTOHS(data + 4);

                    if (port == local_port)
                    {
                        /* replace with L's translated sourceCallSignalAdress */
                        IPCOM_LOG2(DEBUG, "ipnet_nat_proxy_h225_msg() :: "
                                          "Replace local address and port (0x%08x:%d) in H225 payload.",
                                           ip_addr, port);

                        IP_SET_HTONL(data, param->nat_addr);
                        IP_SET_HTONS(data + 4, param->nat_port);
                        data += 6;
                        mod = IP_TRUE;

                        IPCOM_LOG2(DEBUG, "ipnet_nat_proxy_h225_msg() :: "
                                          "Translated local address and port are (0x%08x:%d)",
                                           param->nat_addr, param->nat_port);
                    }
                    else    /* port != bind_info.local_transport */
                    {
                        data++;

                        IPCOM_LOG0(DEBUG, "ipnet_nat_proxy_h225_msg() :: matched local address found, "
                                          "but not the port number.");
                    }
                }
                else    /* ip_addr != bind_info.local_addr */
                {
                    data++;
                }
            }
        }
        else    /* session started by G */
        {
            IPCOM_LOG0(DEBUG, "ipnet_nat_proxy_h225_msg() :: outbound packet, inbound session. "
                              "Look for H245Address from local host.");

            while ((data + 6) <= (data_start + data_length))
            {
                ip_addr = IP_GET_NTOHL(data);

                /* look for L's H245Address port */
                if (ip_addr == local_address)
                {
                    port = IP_GET_NTOHS(data + 4);

                    IPCOM_LOG2(DEBUG, "ipnet_nat_proxy_h225_msg() :: "
                                      "Found H245Address (0x%08x:%d) in H225 payload.",
                                       ip_addr, port);

                    /* register H.245 ALG to NAT */
                    /* if NAPT, create a H.245 TCP bind entry to prepare for H.245
                       connection request from G */

                    ipcom_memset(&proxy_tuple, 0, sizeof(proxy_tuple));
                    proxy_tuple.protocol     = param->tuple.protocol;
                    proxy_tuple.private_addr = local_address;
                    proxy_tuple.private_port = port;
                    proxy_tuple.public_addr  = remote_address;

                    tmp = ipnet_nat_proxy_add_mapping(&proxy_tuple,
                                                      0,
                                                      param->mapping,
                                                      IP_TRUE,          /* Use port translation */
                                                      IP_TRUE,          /* Inbound session */
                                                      ipnet_nat_proxy_h245,
                                                      IP_NULL);
                    if (tmp < 0)
                    {
                        IPCOM_LOG2(ERR, "ipnet_nat_proxy_h225_msg() :: Failed to add mapping for address = 0x%08x, port = %d",
                                         local_address, port);
                    }
                    else
                    {
                        IPCOM_LOG2(DEBUG, "ipnet_nat_proxy_h225_msg() :: Added mapping for address = 0x%08x, port = %d",
                                           local_address, port);
                    }

                    IP_SET_HTONL(data, param->nat_addr);
                    IP_SET_HTONS(data + 4, (Ip_u16)tmp);
                    data += 6;
                    mod = IP_TRUE;

                    IPCOM_LOG2(DEBUG, "ipnet_nat_proxy_h225_msg() :: "
                                      "Translated local address and port are (0x%08x:%d)",
                                       param->nat_addr, tmp);

                }
                else    /* ip_addr != bind_info.local_addr */
                {
                    data++;
                }
            }/* while */
        }   /* else: session started by G */
    }
    else    /* inbound packet */
    {
        if (param->inbound == IP_FALSE)     /* session started by L */
        {

            IPCOM_LOG0(DEBUG, "ipnet_nat_proxy_h225_msg() :: inbound packet, outbound session. "
                              "Look for H245Address from remote host.");

            while ((data + 6) <= (data_start + data_length))
            {
                ip_addr = IP_GET_NTOHL(data);

                /* look for G's H245Address match */
                if (ip_addr == remote_address)
                {
                    port = IP_GET_NTOHS(data + 4);

                    IPCOM_LOG2(DEBUG, "ipnet_nat_proxy_h225_msg() :: "
                                      "H245Address from remote host found (0x%08x:%d).",
                                       ip_addr, port);

                    /* register H.245 ALG to NAT */
                    ipcom_memset(&proxy_tuple, 0, sizeof(proxy_tuple));
                    proxy_tuple.protocol     = param->tuple.protocol;
                    proxy_tuple.public_addr  = ip_addr;
                    proxy_tuple.public_port  = port;
                    proxy_tuple.private_addr = param->tuple.private_addr;

                    if (ipnet_nat_proxy_add_mapping(&proxy_tuple,
                                                    0,
                                                    param->mapping,
                                                    IP_TRUE,          /* Use port translation */
                                                    IP_FALSE,         /* Outbound session */
                                                    ipnet_nat_proxy_h245,
                                                    IP_NULL) < 0)
                    {
                        IPCOM_LOG2(ERR, "ipnet_nat_proxy_h225_msg() :: Failed to add mapping for address = 0x%08x, port = %d",
                                         remote_address, port);
                    }
                    else
                    {
                        IPCOM_LOG2(DEBUG, "ipnet_nat_proxy_h225_msg() :: Added mapping for address = 0x%08x, port = %d",
                                           remote_address, port);
                    }
                }
                data++;
            }
        }
        else    /* session started by G */
        {
            /* search for L's transport address in the H225 payload.
               If found, translate it to its real transport address. */
            IPCOM_LOG0(DEBUG, "ipnet_nat_proxy_h225_msg() :: inbound packet, inbound session."
                              "Translate global transport to its local transport.");

            while ((data + 6) <= (data_start + data_length))
            {
                ip_addr = IP_GET_NTOHL(data);

                /* look for L's translated address match */
                if (ip_addr == param->nat_addr)
                {
                    port = IP_GET_NTOHS(data + 4);

                    IPCOM_LOG2(DEBUG, "ipnet_nat_proxy_h225_msg() :: "
                                      "global ip match found in H.225 payload (0x%08x:%d).",
                                       ip_addr, port);

                    if (port == param->nat_port)
                    {
                        IP_SET_HTONL(data, local_address);
                        IP_SET_HTONS(data + 4, local_port);
                        data += 6;
                        mod = IP_TRUE;

                        IPCOM_LOG2(DEBUG, "ipnet_nat_proxy_h225_msg() :: "
                                          "After translation local_address and port are (0x%08x:%d).",
                                           local_address, local_port);
                    }
                    else    /* port != bind_info.global_transport */
                    {
                        data++;
                    }
                }
                else    /* ip_addr != bind_info.global_addr */
                {
                    data++;
                }
            }   /* while */
        }   /* else (session started by G) */
    }

    return mod == IP_TRUE ? 1 : 0;
}
/*
 *===========================================================================
 *                    ipnet_nat_proxy_dns_parse_answers
 *===========================================================================
 * Description: Parses and modifies DNS answers in a DNS packet.
 * Parameters:  buf     - pointer to buffer with the DNS questions.
 *              buflen  - length of buffer with DNS questions.
 *              offset  - offset in the buffer where the DNS questions begin.
 *              newlen  - pointer to the length of the message if modified.
 *              dns_hdr - pointer to the DNS protocol header.
 *              param   - pointer to NAT proxy parameters.
 * Returns:     The number of bytes parsed or -1 if failed.
 */
IP_STATIC Ip_s32
ipnet_nat_proxy_dns_parse_answers(Ip_u8 *buf,
                              int buflen,
                              int offset,
                              int *newlen,
                              Ipnet_nat_dns_hdr *dns_hdr,
                              Ipnet_nat_proxy_param *param)
{
    int i, count, origoffset, numa;
    Ip_u16 type, cla, rlen;
    int newbuflen = sizeof(dnsbuf);
    Ipnet_nat_dns_transaction *trans;

    numa = IP_GET_NTOHS(&dns_hdr->no_answ);
    origoffset = offset;
    for (i=0; i<numa; i++)
    {
        /* Get the name */
        count = ipnet_nat_proxy_dns_decode_name(buf, buflen, offset, dnsname, sizeof(dnsname));
        if (count < 0)
        {
            IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_answers() :: could not decode dns name");
            return -1;
        }

        /* Copy the name */
        if (newbuflen - *newlen < count)
        {
            IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_answers() :: no space left in modified buffer");
            return -1;
        }
        ipcom_memcpy(&dnsbuf[*newlen], buf+offset, count);
        *newlen += count;
        offset  += count;

        /* Check space for type, class, ttl and record length */
        if (buflen - offset < 10)
        {
            IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_answers() :: message too short to parse answer");
            return -1;
        }

        /* Get the type */
        type = (Ip_u16)(IP_GET_NTOHS(buf+offset));
        switch(type)
        {
            case IPNET_NAT_DNS_QTYPE_A:
                type = IPNET_NAT_DNS_QTYPE_AAAA;
                break;
            case IPNET_NAT_DNS_QTYPE_PTR:
                trans = ipnet_nat_proxy_dns_find_transaction(IPNET_NAT_DNS_QTYPE_PTR, dns_hdr, param);
                if (trans != IP_NULL)
                {
                    IPCOM_LOG4(DEBUG, "ipnet_nat_proxy_dns_parse_answers() :: found transaction:"
                                      "id=%d port=%d addr=0x%08x type=%d",
                                       trans->id, trans->srcport, trans->dstaddr, trans->type);
                    *newlen -= count;   /* Move index back to before the name */
                    count = ipnet_nat_proxy_dns_encode_name(dnsbuf, newbuflen, *newlen, trans->ptrname);
                    if (count < 0)
                    {
                        ipnet_nat_proxy_dns_remove_transaction(trans);
                        IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_questions() :: could not encode dns name");
                        return -1;
                    }
                    *newlen += count;
                    if (i+1 == numa)
                    {
                        /* Remove transaction for last answer */
                        ipnet_nat_proxy_dns_remove_transaction(trans);
                    }
                }
                break;
            default:
                break;
        }
        /* Copy the type */
        if (newbuflen - *newlen < 2)
        {
            IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_answers() :: no space left in modified buffer");
            return -1;
        }
        IP_SET_HTONS(&dnsbuf[*newlen], type);
        *newlen += 2;
        offset  += 2;

        /* Get the class */
        cla = (Ip_u16)(IP_GET_NTOHS(buf+offset));
        if (cla != IPNET_NAT_DNS_QCLASS_INET)
        {
            IPCOM_LOG1(WARNING, "ipnet_nat_proxy_dns_parse_answers() :: unhandled class: %d", cla);
            return -1;
        }
        /* Copy the class */
        if (newbuflen - *newlen < 2)
        {
            IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_answers() :: no space left in modified buffer");
            return -1;
        }
        IP_SET_HTONS(&dnsbuf[*newlen], cla);
        *newlen += 2;
        offset  += 2;

        /* Copy the ttl */
        if (newbuflen - *newlen < 4)
        {
            IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_answers() :: no space left in modified buffer");
            return -1;
        }
        ipcom_memcpy(&dnsbuf[*newlen], buf+offset, 4);
        *newlen += 4;
        offset  += 4;

        /* Get the record length */
        rlen = (Ip_u16)(IP_GET_NTOHS(buf+offset));
        if (buflen - offset < rlen)
        {
            IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_answers() :: message too short to parse answer");
            return -1;
        }

        /* Copy the record length */
        if (newbuflen - *newlen < 2)
        {
            IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_answers() :: no space left in modified buffer");
            return -1;
        }
        if (type == IPNET_NAT_DNS_QTYPE_AAAA && rlen == 4)
        {
            /* Modify record length and make space for the AAAA record */
            IP_SET_HTONS(&dnsbuf[*newlen], 16);
            *newlen += 2;
            /* Insert AAAA record data  */
            if (newbuflen - *newlen < 12)
            {
                IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_answers() :: no space left in modified buffer");
                return -1;
            }
            ipcom_memcpy(&dnsbuf[*newlen], param->prefix, 12);
            *newlen += 12;
        }
        else
        {
            IP_SET_HTONS(&dnsbuf[*newlen], rlen);
            *newlen += 2;
        }
        offset += 2;

        /* Copy the record */
        if (newbuflen - *newlen < rlen)
        {
            IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_answers() :: no space left in modified buffer");
            return -1;
        }
        ipcom_memcpy(&dnsbuf[*newlen], buf+offset, rlen);
        *newlen += rlen;
        offset  += rlen;
    }

    return offset - origoffset;
}
/*
 *===========================================================================
 *                    ipnet_nat_proxy_dns_reply
 *===========================================================================
 * Description: Parse and modify DNS reply.
 * Parameters:  appdata   - pointer to application data.
 *              applen    - pointer to length of application data.
 *              growspace - space available to extend application data.
 *              param     - pointer to proxy parameters.
 *              newdata   - pointer to pointer to new application data.
 * Returns:     1 = Packet modified.
 *              0 = Packet untouched.
 */
IP_STATIC int
ipnet_nat_proxy_dns_reply(Ip_u8 *appdata,
                          int   *applen,
                          int    growspace,
                          Ipnet_nat_proxy_param *param,
                          Ip_u8 **newdata)
{
    Ipnet_nat_dns_hdr *dns_hdr;
    Ip_u16 flags;
    int offset, diff, qlen;
    int newbuflen = sizeof(dnsbuf);
    int newlen    = 0;

    /* Check that at least a header is included */
    if (*applen < (int)sizeof(*dns_hdr))
    {
        IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_reply() :: message too short for dns header");
        return 0;
    }
    dns_hdr = (Ipnet_nat_dns_hdr *)appdata;
    flags = IP_GET_NTOHS(&dns_hdr->flags);
    if((flags & IPNET_NAT_DNS_QR_FLAG)     != IPNET_NAT_DNS_QR_FLAG || /* Not a reply */
       (flags & IPNET_NAT_DNS_OPCODE_FLAG) != 0 ||                     /* Not a standard query */
       (flags & IPNET_NAT_DNS_TC_FLAG)     != 0)                       /* Truncated */
    {
        IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_reply() :: invalid header");
        return 0;
    }

    /* Copy the header */
    if (newbuflen - newlen < (int)sizeof(*dns_hdr))
    {
        IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_reply() :: no space left in modified buffer");
        return 0;
    }
    ipcom_memcpy(&dnsbuf[newlen], dns_hdr, sizeof(*dns_hdr));
    newlen += sizeof(*dns_hdr);

    /* Get the questions */
    offset = sizeof(*dns_hdr);
    qlen = ipnet_nat_proxy_dns_parse_questions(appdata, *applen, offset, &newlen, dns_hdr, param);
    if (qlen < 0)
        return 0;
    offset += qlen;

    /* Get the answers */
    qlen = ipnet_nat_proxy_dns_parse_answers(appdata, *applen, offset, &newlen, dns_hdr, param);
    if (qlen < 0)
        return 0;
    offset += qlen;

    /* Skip authority and additional records */
    dns_hdr = (Ipnet_nat_dns_hdr *)&dnsbuf[0];
    IP_SET_HTONS(&dns_hdr->no_auth, 0);
    IP_SET_HTONS(&dns_hdr->no_addi, 0);

    /* Update application data with the modified buffer */
    diff = newlen - *applen;
    if (diff > growspace)
    {
        /* Must allocate a new buffer */
        *newdata = ipcom_malloc(*applen + diff);
        if (*newdata == IP_NULL)
        {
            IPCOM_LOG1(ERR, "ipnet_nat_proxy_dns_reply() :: ipcom_malloc(%d) failed",
                            *applen + diff);
            return -1;
        }
        ipcom_memcpy(*newdata, dnsbuf, newlen);
    }
    else
    {
        /* Let the current buffer grow */
        ipcom_memcpy(appdata, dnsbuf, newlen);
    }
    *applen = newlen;
    return 1;
}
/*
 *===========================================================================
 *                    ipnet_nat_proxy_dns_parse_questions
 *===========================================================================
 * Description: Parses and modifies DNS questions in a DNS packet.
 * Parameters:  buf     - pointer to buffer with the DNS questions.
 *              buflen  - length of buffer with DNS questions.
 *              offset  - offset in the buffer where the DNS questions begin.
 *              newlen  - pointer to the length of the message if modified.
 *              dns_hdr - pointer to the DNS protocol header.
 *              param   - pointer to NAT proxy parameters.
 * Returns:     The number of bytes parsed or -1 if failed.
 */
IP_STATIC Ip_s32
ipnet_nat_proxy_dns_parse_questions(Ip_u8 *buf,
                                int buflen,
                                int offset,
                                int *newlen,
                                Ipnet_nat_dns_hdr *dns_hdr,
                                Ipnet_nat_proxy_param *param)
{
    int i, j, k, count, origoffset, numq, numa, request;
    Ip_u16 type, cla, flags;
    Ip_u8 addr[4];
    Ipnet_nat_dns_transaction *trans;
    int newbuflen = sizeof(dnsbuf);
    Ip_u8 c, *zone;

    numq = IP_GET_NTOHS(&dns_hdr->no_ques);
    if (numq != 1)
    {
        IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_questions() :: supports only one question per message");
        return -1;
    }
    numa = IP_GET_NTOHS(&dns_hdr->no_answ);
    flags = IP_GET_NTOHS(&dns_hdr->flags);
    request = (flags & IPNET_NAT_DNS_QR_FLAG) != 0 ? 0 : 1;
    origoffset = offset;
    for (i=0; i<numq; i++)
    {
        /* Get the name */
        count = ipnet_nat_proxy_dns_decode_name(buf, buflen, offset, dnsname, sizeof(dnsname));
        if (count < 0)
        {
            IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_questions() :: could not decode dns name");
            return -1;
        }

        /* Copy the name */
        if (newbuflen - *newlen < count)
        {
            IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_questions() :: no space left in modified buffer");
            return -1;
        }
        ipcom_memcpy(&dnsbuf[*newlen], buf+offset, count);
        *newlen += count;
        offset += count;

        /* Check space for type and class */
        if (buflen - offset < 4)
        {
            IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_questions() :: message too short to parse question");
            return -1;
        }

        /* Get the type */
        type = (Ip_u16)(IP_GET_NTOHS(buf+offset));
        switch(type)
        {
            case IPNET_NAT_DNS_QTYPE_AAAA:
                if (request)
                {
                    trans = ipnet_nat_proxy_dns_add_transaction(IPNET_NAT_DNS_QTYPE_A, dns_hdr, param, IP_NULL);
                    if (trans == IP_NULL)
                    {
                        IPCOM_LOG0(ERR, "ipnet_nat_proxy_dns_parse_questions() :: could not add transaction to list");
                        return -1;
                    }
                    else
                    {
                        IPCOM_LOG4(DEBUG, "ipnet_nat_proxy_dns_parse_questions() :: added transaction:"
                                          "id=%d port=%d addr=0x%08x type=%d",
                                           trans->id, trans->srcport, trans->dstaddr, trans->type);

                    }
                    type = IPNET_NAT_DNS_QTYPE_A;      /* Change type to A */
                }
                break;
            case IPNET_NAT_DNS_QTYPE_A:
                if (!request)
                {
                    trans = ipnet_nat_proxy_dns_find_transaction(IPNET_NAT_DNS_QTYPE_A, dns_hdr, param);
                    if (trans != IP_NULL)
                    {
                        IPCOM_LOG4(DEBUG, "ipnet_nat_proxy_dns_parse_questions() :: found transaction:"
                                          "id=%d port=%d addr=0x%08x type=%d",
                                           trans->id, trans->srcport, trans->dstaddr, trans->type);
                        ipnet_nat_proxy_dns_remove_transaction(trans);
                    }
                    else
                    {
                        return -1;
                    }
                    type = IPNET_NAT_DNS_QTYPE_AAAA;   /* Change type back to AAAA */
                }
                break;
            case IPNET_NAT_DNS_QTYPE_PTR:
                if (request)
                {
                    zone = (Ip_u8 *)ipcom_strstr((char *)dnsname, "ip6.int");
                    if (zone == IP_NULL)
                        zone = (Ip_u8 *)ipcom_strstr((char *)dnsname, "ip6.arpa");
                    if (zone == IP_NULL)
                    {
                        IPCOM_LOG0(DEBUG, "ipnet_nat_proxy_dns_parse_questions() :: unhandled zone in PTR request");
                        return -1;
                    }
                    /* Extract IPv4 part */
                    if (ipcom_strlen((char *)dnsname) != 64 + ipcom_strlen((char *)zone))
                    {
                        IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_questions() :: invalid name in PTR request");
                        return -1;
                    }
                    k=0;
                    for (j=3; j>=0; j--)
                    {
                        c = ipcom_tolower(dnsname[k]);
                        c = c > '9' ? c - 'a' + 10 : c - '0';
                        addr[j] = c;
                        k += 2;
                        c = ipcom_tolower(dnsname[k]);
                        c = c > '9' ? c - 'a' + 10 : c - '0';
                        c <<= 4;
                        addr[j] += c;
                        k += 2;
                    }

                    trans = ipnet_nat_proxy_dns_add_transaction(IPNET_NAT_DNS_QTYPE_PTR, dns_hdr, param, dnsname);
                    if (trans == IP_NULL)
                    {
                        IPCOM_LOG0(ERR, "ipnet_nat_proxy_dns_parse_questions() :: could not add transaction to list");
                        return -1;
                    }
                    else
                    {
                        IPCOM_LOG4(DEBUG, "ipnet_nat_proxy_dns_parse_questions() :: added transaction:"
                                          "id=%d port=%d addr=0x%08x type=%d",
                                           trans->id, trans->srcport, trans->dstaddr, trans->type);
                    }

                    /* Convert address to PTR name */
                    if (ipnet_nat_proxy_dns_ptr_name(dnsname, sizeof(dnsname), addr, IP_AF_INET, (Ip_u8 *)"in-addr.arpa") < 0)
                    {
                        ipnet_nat_proxy_dns_remove_transaction(trans);
                        IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_questions() :: could not encode PTR name");
                        return -1;
                    }
                    *newlen -= count;   /* Move index back to before the name */
                    count = ipnet_nat_proxy_dns_encode_name(dnsbuf, newbuflen, *newlen, dnsname);
                    if (count < 0)
                    {
                        ipnet_nat_proxy_dns_remove_transaction(trans);
                        IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_questions() :: could not encode dns name");
                        return -1;
                    }
                    *newlen += count;
                }
                else
                {
                    trans = ipnet_nat_proxy_dns_find_transaction(IPNET_NAT_DNS_QTYPE_PTR, dns_hdr, param);
                    if (trans != IP_NULL)
                    {
                        IPCOM_LOG4(DEBUG, "ipnet_nat_proxy_dns_parse_questions() :: found transaction:"
                                          "id=%d port=%d addr=0x%08x type=%d",
                                           trans->id, trans->srcport, trans->dstaddr, trans->type);
                        *newlen -= count;   /* Move index back to before the name */
                        count = ipnet_nat_proxy_dns_encode_name(dnsbuf, newbuflen, *newlen, trans->ptrname);
                        if (count < 0)
                        {
                            ipnet_nat_proxy_dns_remove_transaction(trans);
                            IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_questions() :: could not encode dns name");
                            return -1;
                        }
                        *newlen += count;
                        if (numa == 0)
                        {
                            /* Remove the transaction if there are no answers */
                            ipnet_nat_proxy_dns_remove_transaction(trans);
                        }
                    }
                    else
                    {
                        return -1;
                    }
                }
                break;
            default:
                return -1;
        }
        /* Copy the type */
        if (newbuflen - *newlen < 2)
        {
            IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_questions() :: no space left in modified buffer");
            return -1;
        }
        IP_SET_HTONS(&dnsbuf[*newlen], type);
        *newlen += 2;
        offset += 2;

        /* Get the class */
        cla = (Ip_u16)(IP_GET_NTOHS(buf+offset));
        if (cla != IPNET_NAT_DNS_QCLASS_INET)
        {
            IPCOM_LOG1(WARNING, "ipnet_nat_proxy_dns_parse_questions() :: unhandled class: %d", cla);
            return -1;
        }
        /* Copy the class */
        if (newbuflen - *newlen < 2)
        {
            IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_questions() :: no space left in modified buffer");
            return -1;
        }
        IP_SET_HTONS(&dnsbuf[*newlen], cla);
        *newlen += 2;
        offset += 2;
    }

    return offset - origoffset;
}