#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>

#include "../src/pub.h"
#include "../src/dns.h"

#define ERROR(str)   \
    {                \
        printf(str); \
        exit(1);     \
    }

typedef struct
{
    int len;
    const char *data;
} str_t;

typedef struct
{
    const char *desc;
    str_t req;
    str_t rsp;
} package_t;

static package_t package_arry[] = {
    {
        "dig @127.0.0.1 www.baidu.com a",
        {56, "TDwBIAABAAAAAAABA3d3dwViYWlkdQNjb20AAAEAAQAAKRAAAAAAAAAA"},
        {136, "TDyBgAABAAMAAAABA3d3dwViYWlkdQNjb20AAAEAAcAMAAUAAQAAACoADwN3d3cBYQZzaGlmZW7AFsArAAEAAQAAACoABLY9yAfAKwABAAEAAAAqAAS2PcgGAAApEAAAAAAAAAA="},
    },
    {
        "dig @127.0.0.1 baidu.com ns",
        {52, "M00BIAABAAAAAAABBWJhaWR1A2NvbQAAAgABAAApEAAAAAAAAAA="},
        {172, "M02BgAABAAUAAAABBWJhaWR1A2NvbQAAAgABwAwAAgABAAAcJwAGA25zNMAMwAwAAgABAAAcJwAGA25zMsAMwAwAAgABAAAcJwAGA25zN8AMwAwAAgABAAAcJwAGA2Ruc8AMwAwAAgABAAAcJwAGA25zM8AMAAApEAAAAAAAAAA="},
    },
    {
        "dig @127.0.0.1 www.baidu.com cname",
        {56, "LAMBIAABAAAAAAABA3d3dwViYWlkdQNjb20AAAUAAQAAKRAAAAAAAAAA"},
        {92, "LAOBgAABAAEAAAABA3d3dwViYWlkdQNjb20AAAUAAcAMAAUAAQAAAGYADwN3d3cBYQZzaGlmZW7AFgAAKRAAAAAAAAAA"},
    },
    {
        "dig @127.0.0.1 www.baidu.com soa",
        {56, "9D8BIAABAAAAAAABA3d3dwViYWlkdQNjb20AAAYAAQAAKRAAAAAAAAAA"},
        {168, "9D+BgAABAAEAAQABA3d3dwViYWlkdQNjb20AAAYAAcAMAAUAAQAAAhcADwN3d3cBYQZzaGlmZW7AFsAvAAYAAQAAAmIALQNuczHALxBiYWlkdV9kbnNfbWFzdGVywBB3hAIjAAAABQAAAAUAJ40AAAAOEAAAKRAAAAAAAAAA"},
    },
    {
        "dig @127.0.0.1 -x 192.30.252.153",
        {76, "G+4BIAABAAAAAAABAzE1MwMyNTICMzADMTkyB2luLWFkZHIEYXJwYQAADAABAAApEAAAAAAAAAA="},
        {136, "G+6BgAABAAEAAAABAzE1MwMyNTICMzADMTkyB2luLWFkZHIEYXJwYQAADAABwAwADAABAAAOEAAiFWxiLTE5Mi0zMC0yNTItMTUzLWlhZAZnaXRodWIDY29tAAAAKRAAAAAAAAAA"},
    },
    {
        "dig @127.0.0.1 163.com mx",
        {48, "iRMBIAABAAAAAAABAzE2MwNjb20AAA8AAQAAKRAAAAAAAAAA"},
        {196, "iROBgAABAAQAAAABAzE2MwNjb20AAA8AAcAMAA8AAQAADnQAGwAKBzE2M214MDEGbXhtYWlsB25ldGVhc2XAEMAMAA8AAQAADnQADAAyBzE2M214MDDAL8AMAA8AAQAADnQADAAKBzE2M214MDPAL8AMAA8AAQAADnQADAAKBzE2M214MDLALwAAKRAAAAAAAAAA"},
    },
    {
        "dig @127.0.0.1 baidu.com txt",
        {52, "A6kBIAABAAAAAAABBWJhaWR1A2NvbQAAEAABAAApEAAAAAAAAAA="},
        {296, "A6mBgAABAAIAAAABBWJhaWR1A2NvbQAAEAABwAwAEAABAAAIKgBFRGdvb2dsZS1zaXRlLXZlcmlmaWNhdGlvbj1HSGI5OC02bXNxeXhfcXFqR2w1ZVJhdEQzUVRIeVZCNi14UTNnSkI1VXdNwAwAEAABAAAIKgBaWXY9c3BmMSBpbmNsdWRlOnNwZjEuYmFpZHUuY29tIGluY2x1ZGU6c3BmMi5iYWlkdS5jb20gaW5jbHVkZTpzcGYzLmJhaWR1LmNvbSBhIG14IHB0ciAtYWxsAAApEAAAAAAAAAA="},
    },
    {
        "dig @127.0.0.1 www.taobao.com aaaa",
        {60, "0qQBIAABAAAAAAABA3d3dwZ0YW9iYW8DY29tAAAcAAEAACkQAAAAAAAAAA=="},
        {640, "0qSBgAABAA8AAAABA3d3dwZ0YW9iYW8DY29tAAAcAAHADAAFAAEAAAG2ACEDd3d3BnRhb2JhbwNjb20HZGFudW95aQd0YmNhY2hlwBfALAAcAAEAAAA8ABAkDgCxmAEEAAADAAAAAAP5wCwAHAABAAAAPAAQJA4AsZgBBAkAAwAAAAAD+cAsABwAAQAAADwAECQOALGYAQQAAAMAAAAAA/rALAAcAAEAAAA8ABAkDglA4AEAAQADAAAAAAP5wCwAHAABAAAAPAAQJA4AsZgBBAgAAwAAAAAD+sAsABwAAQAAADwAECQOCUDgAQABAAMAAAAAA/rALAAcAAEAAAA8ABAkDglA4AEABQADAAAAAAP5wCwAHAABAAAAPAAQJA4AsaggAAAAAwAAAAAD9cAsABwAAQAAADwAECQOALGoIAAAAAMAAAAAA/bALAAcAAEAAAA8ABAkDgCxmAEECQADAAAAAAP6wCwAHAABAAAAPAAQJA4JQOABAAQAAwAAAAAD+cAsABwAAQAAADwAECQOCUDgAQAEAAMAAAAAA/rALAAcAAEAAAA8ABAkDgCxmAEECAADAAAAAAP5wCwAHAABAAAAPAAQJA4JQOABAAUAAwAAAAAD+gAAKRAAAAAAAAAA"},
    },
    {
        "dig @127.0.0.1 com ds",
        {44, "94UBIAABAAAAAAABA2NvbQAAKwABAAApEAAAAAAAAAA="},
        {108, "94WBgAABAAEAAAABA2NvbQAAKwABwAwAKwABAAB/zgAkeL0IAuLTyRb23urHMpToJo+1iFBEqDP8VFlYj0qRhM/EGldmAAApEAAAAAAAAAA="},
    },
    {
        "dig @127.0.0.1 com rrsig",
        {44, "ETMBIAABAAAAAAABA2NvbQAALgABAAApEAAAAAAAAAA="},
        {948, "ETOBgAABAAMAAAABA2NvbQAALgABwAwALgABAAABjgC3AAYIAQAAA4RexfcOXrysJpukA2NvbQAJCiOw6MPHup5mJVrn65fULwvBJoy5mR1IYTJSEzv7PXsT+B/3lp+5Gnfds89MvligsnUWkQvOUiCOkY5ahPwO5fQSB4dDPZJXahG44IR2QtNDHLfFNxcRROwdLmmSe9F+sU5ZL4YtmXu4+trwUycbo34f19o3xRS5D6UBV1clWPp0yK9rQafhMY5UBIZz83txHKumVVjwlnsA2d7w1Qi7wDMALgABAAEBrwETACsIAQABUYBezYNQXrxRwL8HAF4VVK323NSvS//r44sD6QQDSa9aNBCPlimQWZUbjmAh2Z/RkwTAuK0T3ngOw5flW8rHaopbNfIQ4UZSNqzcbzXtJ/F2u2DG4/Xo26LRbF1jHBZOGChGMeug4kVVepPuQvxEoSBUY9FgdGXMZ551kxO/xf3kK6xMhmjQjOQn1xJzZEGtcqV+5pKymT4MNdfPdhpJIDQRnN33bvk8su7wPiaJmGsKGdGHBKlsyMJgLCoJrMvSQIEiQj560Rwy1n1qq3Mlk6CkydQ62mZp2u32d4wBTE3UTs+aDBRfTSQmg6Dofh9x2DJgK5mFISt7rYgvacxpRfqBkD4tDdKUQlrszbjAMwAuAAEAAk6gALcAAggBAAKjAF7AwkJet3dam6QDY29tAJK2SHQgAOJ0UOxt+9f55rJHx5vzvSuuuTsaM06kaK8zhK6Xo0IRqWtu12EHehn3UiPoTGEdUK/KK2ZXmdb8yJ/H+L4BRG9AafN7xaHPAf4GbXr0pV4yOl+HoTEstZmL3JgXHPFMRLl66UYbNGBDFE6B1f1MNZlBvk6c3NjeMSN29DOIZRoajZlDKv3IRhOrRJghwLr5vhlODIwm58lTbI0AACkQAAAAAAAAAA=="},
    },
    {
        "dig @127.0.0.1 com dnskey",
        {44, "FJwBIAABAAAAAAABA2NvbQAAMAABAAApEAAAAAAAAAA="},
        {648, "FJyBgAABAAIAAAABA2NvbQAAMAABwAwAMAABAAAL5wEGAQEDCAEDw85XTZjL2RV+DXDSdLhJyg4O7Zr/xdzMkEdJaQZlXDXLCLM8TRcbAXyjVvSWAmKqYpPN+uixO1WyHDUc36dofTjvB0Zfh/hNPM2rivJO3r1hJrv+qHftm6IID6IhHxjcrzT2kiOxTiK6A7J8P7WoIMx0V9We0jojoj1jzSMElMljme/VZnENRi5AujZWLxtx8GJsp0L+qBcBr/yhC0sO2Una200NB172W6jFCOwWjLJJr4JtRu6CmdWIhezvYqFTXNPuwEm6pmTe2ffBBlP0IdivwYFHvB7NF1XHTyq7cmJ6EB3dspyj3DDJUxIodv9hwx40TydmssCKSjZ7+KD6P8AMADAAAQAAC+cAqAEAAwgDAQABtsVzV+OQEJLJyG3tqH155FqW2csMraxXKjGSMLrMHOb/lIaB8Mb8fpcS6lPty2AK+ep7FqKdMh9ryecFgI60B5zsZ3W9voT/dJeSCINoxQleZ23wTuhu1VmqMWU3ZA2dOWN8yIQKfXqtleW9ciLbnlPXAWRa2nn4tQKK5+wtN4pz3FtDDvLS5yxH4YrPjIRK1tiL3x+cHmc7PNaFFRyV/QAAKRAAAAAAAAAA"},
    },
    {
        "dig @127.0.0.1 com nsec3param",
        {44, "s48BIAABAAAAAAABA2NvbQAAMwABAAApEAAAAAAAAAA="},
        {68, "s4+BgAABAAEAAAABA2NvbQAAMwABwAwAMwABAABFmwAFAQAAAAAAACkQAAAAAAAAAA=="},
    },
    {
        "dig @127.0.0.1 _sip._udp.sip.voice.google.com srv",
        {80, "9OoBIAABAAAAAAABBF9zaXAEX3VkcANzaXAFdm9pY2UGZ29vZ2xlA2NvbQAAIQABAAApEAAAAAAAAAA="},
        {212, "9OqBgAABAAIAAAABBF9zaXAEX3VkcANzaXAFdm9pY2UGZ29vZ2xlA2NvbQAAIQABwAwAIQABAAAAxAAmAAoAARPEDXNpcC1hbnljYXN0LTEFdm9pY2UGZ29vZ2xlA2NvbQDADAAhAAEAAADEACYAFAABE8QNc2lwLWFueWNhc3QtMgV2b2ljZQZnb29nbGUDY29tAAAAKRAAAAAAAAAA"},
    },
    {
        "dig @127.0.0.1 . nsec",
        {40, "3HUBIAABAAAAAAABAAAvAAEAACkQAAAAAAAAAA=="},
        {72, "3HWBgAABAAEAAAABAAAvAAEAAC8AAQABUSUADgNhYWEAAAciAAAAAAOAAAApEAAAAAAAAAA="},
    }};

static int package_arry_size = sizeof(package_arry) / sizeof(package_t);

static int dns_print(dns_info_t *dns_info, const char *filename)
{
    int i = 0, j = 0;
    int used_len = 0, len = 0;
    FILE *fp = NULL;
    char dns_header[256];
    char question[1024];
    char rr_buf[4096];
    char ip_str[128];
    char tmp_buf[2048];
    char maps[2048];
    char salt_value[2048];
    dns_rr_t *dns_rr = NULL;
    char *buf = NULL;
    int buflen = sizeof(rr_buf);
    buf = rr_buf;
    cJSON *dns_info_object = cJSON_CreateObject();
    cJSON *dns_hdr_object = cJSON_CreateObject();
    cJSON *dns_flags_object = cJSON_CreateObject();
    cJSON *dns_question_array = cJSON_CreateArray();
    cJSON *dns_rr_array = cJSON_CreateArray();

    memset(dns_header, 0, sizeof(dns_header));

    fp = fopen(filename, "a+");
    if (fp == NULL)
    {
        return 0;
    }

    used_len = snprintf(dns_header, sizeof(dns_header),
                        "hdr_info: <id=0X%02x, QR=%d, OPCODE=%d, AA=%d, TC=%d, RD=%d, RA=%d,Z=%d, RCODE=%d, qdcount=%d, ancount=%d, aucount=%d, adcount=%d>\n",
                        dns_info->hdr_info.id,
                        dns_info->hdr_info.qr,
                        dns_info->hdr_info.opcode,
                        dns_info->hdr_info.aa,
                        dns_info->hdr_info.tc,
                        dns_info->hdr_info.rd,
                        dns_info->hdr_info.ra,
                        dns_info->hdr_info.z,
                        dns_info->hdr_info.rcode,
                        dns_info->hdr_info.qdcount,
                        dns_info->hdr_info.ancount,
                        dns_info->hdr_info.aucount,
                        dns_info->hdr_info.adcount);

    cJSON_AddNumberToObject(dns_hdr_object, "id", dns_info->hdr_info.id);
    cJSON_AddNumberToObject(dns_hdr_object, "qdcount", dns_info->hdr_info.qdcount);
    cJSON_AddNumberToObject(dns_hdr_object, "ancount", dns_info->hdr_info.ancount);
    cJSON_AddNumberToObject(dns_hdr_object, "aucount", dns_info->hdr_info.aucount);
    cJSON_AddNumberToObject(dns_hdr_object, "adcount", dns_info->hdr_info.adcount);

    cJSON_AddNumberToObject(dns_flags_object, "qr", dns_info->hdr_info.qr);
    cJSON_AddNumberToObject(dns_flags_object, "opcode", dns_info->hdr_info.opcode);
    cJSON_AddNumberToObject(dns_flags_object, "aa", dns_info->hdr_info.aa);
    cJSON_AddNumberToObject(dns_flags_object, "tc", dns_info->hdr_info.tc);
    cJSON_AddNumberToObject(dns_flags_object, "rd", dns_info->hdr_info.rd);
    cJSON_AddNumberToObject(dns_flags_object, "ra", dns_info->hdr_info.ra);
    cJSON_AddNumberToObject(dns_flags_object, "z", dns_info->hdr_info.z);
    cJSON_AddNumberToObject(dns_flags_object, "rcode", dns_info->hdr_info.rcode);

    fwrite(dns_header, used_len, 1, fp);

    cJSON_AddItemToObject(dns_info_object, "hdr", dns_hdr_object);
    cJSON_AddItemToObject(dns_info_object, "flags", dns_flags_object);
    cJSON_AddItemToObject(dns_info_object, "question", dns_question_array);

    used_len = snprintf(question, sizeof(question), "question: <qtype: %d, qclass: %d, qname: %s>\n",
                        dns_info->query_question.qtype,
                        dns_info->query_question.qclass,
                        dns_info->query_question.qname);

    cJSON *dns_question_object = cJSON_CreateObject();
    cJSON_AddItemToArray(dns_question_array, dns_question_object);

    cJSON_AddNumberToObject(dns_question_object, "qtype", dns_info->query_question.qtype);
    cJSON_AddNumberToObject(dns_question_object, "qclass", dns_info->query_question.qclass);
    cJSON_AddStringToObject(dns_question_object, "qname", (const char *)dns_info->query_question.qname);

    fwrite(question, used_len, 1, fp);

    used_len = snprintf(rr_buf, sizeof(rr_buf), "RRS count: %d\n", dns_info->rr_count);
    fwrite(rr_buf, used_len, 1, fp);

    cJSON_AddItemToObject(dns_info_object, "rr", dns_rr_array);

    for (i = 0; i < dns_info->rr_count; i++)
    {
        cJSON *dns_rr_object = cJSON_CreateObject();
        cJSON_AddItemToArray(dns_rr_array, dns_rr_object);

        used_len = 0;
        dns_rr = &(dns_info->rr[i]);
        memset(rr_buf, 0, sizeof(rr_buf));

        if (dns_rr->type == DNS_TYPE_OPT)
        {
            used_len += snprintf(rr_buf, sizeof(rr_buf), "RRS%d OPT <name: %s, type: %d, udp_payload: %d, extended RCODE: %d, version: %d, Z; 0X%x, rdlength: %d>\n",
                                 i,
                                 dns_rr->name,
                                 dns_rr->type,
                                 dns_rr->rr_class,
                                 dns_rr->ttl >> 24,
                                 (dns_rr->ttl >> 16) & 0xFF,
                                 dns_rr->ttl && 0xFFFF,
                                 dns_rr->rdlength);

            cJSON_AddStringToObject(dns_rr_object, "name", (const char *)(dns_rr->name));
            cJSON_AddNumberToObject(dns_rr_object, "type", dns_rr->type);
            cJSON_AddNumberToObject(dns_rr_object, "udp_payload", dns_rr->rr_class);
            cJSON_AddNumberToObject(dns_rr_object, "rcode", (int)(dns_rr->ttl >> 24));
            cJSON_AddNumberToObject(dns_rr_object, "version", (int)((dns_rr->ttl >> 16) & 0xFF));
            cJSON_AddNumberToObject(dns_rr_object, "Z", (int)(dns_rr->ttl && 0xFFFF));
            cJSON_AddNumberToObject(dns_rr_object, "rdlength", dns_rr->rdlength);
        }
        else
        {
            used_len += snprintf(rr_buf, sizeof(rr_buf), "RRS%d <name: %s, type: %d, rr_class: %d, ttl: %d, rdlength: %d>",
                                 i,
                                 dns_rr->name,
                                 dns_rr->type,
                                 dns_rr->rr_class,
                                 dns_rr->ttl,
                                 dns_rr->rdlength);

            cJSON_AddStringToObject(dns_rr_object, "name", (const char *)(dns_rr->name));
            cJSON_AddNumberToObject(dns_rr_object, "type", dns_rr->type);
            cJSON_AddNumberToObject(dns_rr_object, "class", dns_rr->rr_class);
            cJSON_AddNumberToObject(dns_rr_object, "ttl", dns_rr->ttl);
            cJSON_AddNumberToObject(dns_rr_object, "rdlength", dns_rr->rdlength);
        }

        switch (dns_rr->type)
        {
        case DNS_TYPE_A:
            inet_ntop(AF_INET, (void *)(dns_rr->rdata.a), ip_str, sizeof(ip_str));
            used_len += snprintf(buf + used_len, buflen - used_len, "[A: %s]\n", ip_str);
            cJSON_AddStringToObject(dns_rr_object, "a", ip_str);
            break;
        case DNS_TYPE_NS:
            used_len += snprintf(buf + used_len, buflen - used_len, "[NS: %s]\n", dns_rr->rdata.ns);
            cJSON_AddStringToObject(dns_rr_object, "ns", (const char *)(dns_rr->rdata.ns));
            break;
        case DNS_TYPE_MD:
            used_len += snprintf(buf + used_len, buflen - used_len, "[MD: %s]\n", dns_rr->rdata.md);
            cJSON_AddStringToObject(dns_rr_object, "md", (const char *)(dns_rr->rdata.md));
            break;
        case DNS_TYPE_MF:
            used_len += snprintf(buf + used_len, buflen - used_len, "[MF: %s]\n", dns_rr->rdata.mf);
            cJSON_AddStringToObject(dns_rr_object, "mf", (const char *)(dns_rr->rdata.mf));
            break;
        case DNS_TYPE_CNAME:
            used_len += snprintf(buf + used_len, buflen - used_len, "[CNAME: %s]\n", dns_rr->rdata.cname);
            cJSON_AddStringToObject(dns_rr_object, "cname", (const char *)(dns_rr->rdata.cname));
            break;
        case DNS_TYPE_SOA:
            used_len += snprintf(buf + used_len, buflen - used_len,
                                 "[SOA  mname: %s, rname: %s, serial: %u, refresh: %u, retry: %u, expire: %u, minimum: %u]\n",
                                 dns_rr->rdata.soa.mname,
                                 dns_rr->rdata.soa.rname,
                                 dns_rr->rdata.soa.serial,
                                 dns_rr->rdata.soa.refresh,
                                 dns_rr->rdata.soa.retry,
                                 dns_rr->rdata.soa.expire,
                                 dns_rr->rdata.soa.minimum);

            cJSON_AddStringToObject(dns_rr_object, "mname", (const char *)(dns_rr->rdata.soa.mname));
            cJSON_AddStringToObject(dns_rr_object, "rname", (const char *)(dns_rr->rdata.soa.rname));
            cJSON_AddNumberToObject(dns_rr_object, "serial", dns_rr->rdata.soa.serial);
            cJSON_AddNumberToObject(dns_rr_object, "refresh", dns_rr->rdata.soa.refresh);
            cJSON_AddNumberToObject(dns_rr_object, "retry", dns_rr->rdata.soa.retry);
            cJSON_AddNumberToObject(dns_rr_object, "cname", dns_rr->rdata.soa.expire);
            cJSON_AddNumberToObject(dns_rr_object, "minimum", dns_rr->rdata.soa.minimum);
            break;
        case DNS_TYPE_MB:
            used_len += snprintf(buf + used_len, buflen - used_len, "[MB: %s]\n", dns_rr->rdata.mb);
            cJSON_AddStringToObject(dns_rr_object, "mb", (const char *)(dns_rr->rdata.mb));
            break;
        case DNS_TYPE_MG:
            used_len += snprintf(buf + used_len, buflen - used_len, "[MG: %s]\n", dns_rr->rdata.mg);
            cJSON_AddStringToObject(dns_rr_object, "mg", (const char *)(dns_rr->rdata.mg));
            break;
        case DNS_TYPE_MR:
            used_len += snprintf(buf + used_len, buflen - used_len, "[MR: %s]\n", dns_rr->rdata.mr);
            cJSON_AddStringToObject(dns_rr_object, "mr", (const char *)(dns_rr->rdata.mr));
            break;
        case DNS_TYPE_NULL:
            used_len += snprintf(buf + used_len, buflen - used_len, "[null size: %u, null: %s]\n",
                                 dns_rr->rdata.null.size, dns_rr->rdata.null.null);
            cJSON_AddNumberToObject(dns_rr_object, "size", dns_rr->rdata.null.size);
            cJSON_AddStringToObject(dns_rr_object, "null", (const char *)(dns_rr->rdata.null.null));
            break;
        case DNS_TYPE_WKS:
            inet_ntop(AF_INET, &(dns_rr->rdata.wks.addr), ip_str, sizeof(ip_str));
            used_len += snprintf(buf + used_len, buflen - used_len, "[WKS addr: %s, protocol: %u, bitmap: %s, size: %u]\n",
                                 ip_str, dns_rr->rdata.wks.protocol, dns_rr->rdata.wks.bitmap, dns_rr->rdata.wks.size);

            cJSON_AddStringToObject(dns_rr_object, "addr", ip_str);
            cJSON_AddNumberToObject(dns_rr_object, "protocol", dns_rr->rdata.wks.protocol);
            cJSON_AddStringToObject(dns_rr_object, "bitmap", (const char *)(dns_rr->rdata.wks.bitmap));
            cJSON_AddNumberToObject(dns_rr_object, "size", dns_rr->rdata.wks.size);
            break;
        case DNS_TYPE_PTR:
            used_len += snprintf(buf + used_len, buflen - used_len, "[PTR: %s]\n", dns_rr->rdata.ptr);
            cJSON_AddStringToObject(dns_rr_object, "ptr", (const char *)(dns_rr->rdata.ptr));
            break;
        case DNS_TYPE_HINFO:
            used_len += snprintf(buf + used_len, buflen - used_len, "[HINFO cpu: %s, os: %s]\n",
                                 dns_rr->rdata.hinfo.cpu, dns_rr->rdata.hinfo.os);

            cJSON_AddStringToObject(dns_rr_object, "cpu", (const char *)(dns_rr->rdata.hinfo.cpu));
            cJSON_AddStringToObject(dns_rr_object, "os", (const char *)(dns_rr->rdata.hinfo.os));
            break;
        case DNS_TYPE_MINFO:
            used_len += snprintf(buf + used_len, buflen - used_len, "[MINFO rmailbx: %s, emailbx: %s]\n",
                                 dns_rr->rdata.minfo.rmailbx, dns_rr->rdata.minfo.emailbx);

            cJSON_AddStringToObject(dns_rr_object, "rmailbx", (const char *)(dns_rr->rdata.minfo.rmailbx));
            cJSON_AddStringToObject(dns_rr_object, "emailbx", (const char *)(dns_rr->rdata.minfo.emailbx));
            break;
        case DNS_TYPE_MX:
            used_len += snprintf(buf + used_len, buflen - used_len, "[MX preference: %u, exchange: %s]\n",
                                 dns_rr->rdata.mx.preference, dns_rr->rdata.mx.exchange);

            cJSON_AddStringToObject(dns_rr_object, "exchange", (const char *)(dns_rr->rdata.mx.exchange));
            cJSON_AddNumberToObject(dns_rr_object, "preference", dns_rr->rdata.mx.preference);
            break;
        case DNS_TYPE_TXT:
            used_len += snprintf(buf + used_len, buflen - used_len, "[TXT size: %u, txt: %s]\n",
                                 dns_rr->rdata.txt.size, dns_rr->rdata.txt.txt);

            cJSON_AddStringToObject(dns_rr_object, "txt", (const char *)(dns_rr->rdata.txt.txt));
            cJSON_AddNumberToObject(dns_rr_object, "size", dns_rr->rdata.txt.size);
            break;
        case DNS_TYPE_RP:
            used_len += snprintf(buf + used_len, buflen - used_len, "[mailbox: %s, txt_rr: %s]\n",
                                 dns_rr->rdata.rp.mailbox, dns_rr->rdata.rp.txt_rr);

            cJSON_AddStringToObject(dns_rr_object, "mailbox", (const char *)(dns_rr->rdata.rp.mailbox));
            cJSON_AddStringToObject(dns_rr_object, "txt_rr", (const char *)(dns_rr->rdata.rp.txt_rr));
            break;
        case DNS_TYPE_AAAA:
            inet_ntop(AF_INET6, dns_rr->rdata.aaaa, ip_str, sizeof(ip_str));
            used_len += snprintf(buf + used_len, buflen - used_len, "[AAAA: %s]\n", ip_str);
            cJSON_AddStringToObject(dns_rr_object, "aaaa", ip_str);
            break;
        case DNS_TYPE_OPT:
            break;
        case DNS_TYPE_DS:
            len = 0;
            assert(dns_rr->rdata.ds.digest_len * 2 < sizeof(tmp_buf));
            for (j = 0; j < (int)(dns_rr->rdata.ds.digest_len); j++)
            {
                len += snprintf(tmp_buf + len, sizeof(tmp_buf) - len, "%02x", dns_rr->rdata.ds.digest[j]);
            }
            used_len += snprintf(buf + used_len, buflen - used_len,
                                 "[DS key_tag: %u, algo: %u, digest_type: %u, digest: %s]\n",
                                 dns_rr->rdata.ds.key_tag, dns_rr->rdata.ds.algo,
                                 dns_rr->rdata.ds.digest_type, tmp_buf);

            cJSON_AddNumberToObject(dns_rr_object, "key_tag", dns_rr->rdata.ds.key_tag);
            cJSON_AddNumberToObject(dns_rr_object, "algo", dns_rr->rdata.ds.algo);
            cJSON_AddNumberToObject(dns_rr_object, "digest_type", dns_rr->rdata.ds.digest_type);
            cJSON_AddStringToObject(dns_rr_object, "digest", tmp_buf);
            break;
        case DNS_TYPE_RRSIG:
            len = 0;
            assert(dns_rr->rdata.rrsig.signature_len * 2 < sizeof(tmp_buf));
            for (j = 0; j < (int)(dns_rr->rdata.rrsig.signature_len); j++)
            {
                len += snprintf(tmp_buf + len, sizeof(tmp_buf) - len, "%02x", dns_rr->rdata.rrsig.signature[j]);
            }
            used_len += snprintf(buf + used_len, buflen - used_len,
                                 "[RRSIG type_covered: %u, algo: %u, labels: %u, original_ttl: %u, sig_expiration: %u, sig_inception: %u, key_tag: %u, signer_name: %s, signature: %s]\n",
                                 dns_rr->rdata.rrsig.type_covered, dns_rr->rdata.rrsig.algo,
                                 dns_rr->rdata.rrsig.labels, dns_rr->rdata.rrsig.original_ttl,
                                 dns_rr->rdata.rrsig.sig_expiration, dns_rr->rdata.rrsig.sig_inception,
                                 dns_rr->rdata.rrsig.key_tag, dns_rr->rdata.rrsig.signer_name, tmp_buf);

            cJSON_AddNumberToObject(dns_rr_object, "type_covered", dns_rr->rdata.rrsig.type_covered);
            cJSON_AddNumberToObject(dns_rr_object, "algo", dns_rr->rdata.rrsig.algo);
            cJSON_AddNumberToObject(dns_rr_object, "labels", dns_rr->rdata.rrsig.labels);
            cJSON_AddNumberToObject(dns_rr_object, "original_ttl", dns_rr->rdata.rrsig.original_ttl);
            cJSON_AddNumberToObject(dns_rr_object, "sig_expiration", dns_rr->rdata.rrsig.sig_expiration);
            cJSON_AddNumberToObject(dns_rr_object, "sig_inception", dns_rr->rdata.rrsig.sig_inception);
            cJSON_AddNumberToObject(dns_rr_object, "key_tag", dns_rr->rdata.rrsig.key_tag);
            cJSON_AddStringToObject(dns_rr_object, "signer_name", (const char *)(dns_rr->rdata.rrsig.signer_name));
            cJSON_AddStringToObject(dns_rr_object, "signature", tmp_buf);
            break;
        case DNS_TYPE_NSEC:
            len = 0;
            for (j = 0; j < (int)(dns_rr->rdata.nsec.maps_len); j++)
            {
                len += snprintf(maps + len, sizeof(maps) - len, "%02x", dns_rr->rdata.nsec.type_bit_maps[j]);
            }
            used_len += snprintf(buf + used_len, buflen - used_len, "[NSEC next_domain: %s, type_bit_maps: %s]\n", dns_rr->rdata.nsec.next_domain, maps);

            cJSON_AddStringToObject(dns_rr_object, "next_domain", (const char *)(dns_rr->rdata.nsec.next_domain));
            cJSON_AddStringToObject(dns_rr_object, "type_bit_maps", maps);
            break;
        case DNS_TYPE_DNSKEY:
            len = 0;
            assert(dns_rr->rdata.dnskey.public_key_len * 2 < sizeof(tmp_buf));
            for (j = 0; j < (int)(dns_rr->rdata.dnskey.public_key_len); j++)
            {
                len += snprintf(tmp_buf + len, sizeof(tmp_buf) - len, "%02x", dns_rr->rdata.dnskey.public_key[j]);
            }
            used_len += snprintf(buf + used_len, buflen - used_len, "[DNSKEY flags: %u, protocol: %u, algo: %u, public_key: %s]\n",
                                 dns_rr->rdata.dnskey.flags, dns_rr->rdata.dnskey.protocol, dns_rr->rdata.dnskey.algo, tmp_buf);

            cJSON_AddNumberToObject(dns_rr_object, "flags", dns_rr->rdata.dnskey.flags);
            cJSON_AddNumberToObject(dns_rr_object, "protocol", dns_rr->rdata.dnskey.protocol);
            cJSON_AddNumberToObject(dns_rr_object, "algo", dns_rr->rdata.dnskey.algo);
            cJSON_AddStringToObject(dns_rr_object, "public_key", tmp_buf);
            break;
        case DNS_TYPE_NSEC3:
            memset(tmp_buf, 0, sizeof(tmp_buf));
            memset(maps, 0, sizeof(maps));
            len = 0;
            assert(dns_rr->rdata.nsec3.hash_len * 2 < sizeof(tmp_buf));
            for (j = 0; j < (int)(dns_rr->rdata.nsec3.hash_len); j++)
            {
                len += snprintf(tmp_buf + len, sizeof(tmp_buf) - len, "%02x", dns_rr->rdata.nsec3.next_hash_owner[j]);
            }
            len = 0;
            for (j = 0; j < (int)(dns_rr->rdata.nsec3.maps_len); j++)
            {
                len += snprintf(maps + len, sizeof(maps) - len, "%02x", dns_rr->rdata.nsec3.type_bit_maps[j]);
            }
            len = 0;
            for (j = 0; j < (int)(dns_rr->rdata.nsec3.salt_len); j++)
            {
                len += snprintf(salt_value + len, sizeof(salt_value) - len, "%02x", dns_rr->rdata.nsec3.salt_value[j]);
            }

            used_len += snprintf(buf + used_len, buflen - used_len,
                                 "[NSEC3 hash_algo: %u, flags: %u, iteration: %u, salt_len: %u, hash_len: %u, salt_value: %s, next_hash_owner: %s, type_bit_maps: %s]\n",
                                 dns_rr->rdata.nsec3.hash_algo, dns_rr->rdata.nsec3.flags,
                                 dns_rr->rdata.nsec3.iteration, dns_rr->rdata.nsec3.salt_len,
                                 dns_rr->rdata.nsec3.hash_len, salt_value, tmp_buf, maps);

            cJSON_AddNumberToObject(dns_rr_object, "hash_algo", dns_rr->rdata.nsec3.hash_algo);
            cJSON_AddNumberToObject(dns_rr_object, "flags", dns_rr->rdata.nsec3.flags);
            cJSON_AddNumberToObject(dns_rr_object, "iteration", dns_rr->rdata.nsec3.iteration);
            cJSON_AddNumberToObject(dns_rr_object, "salt_len", dns_rr->rdata.nsec3.salt_len);
            cJSON_AddNumberToObject(dns_rr_object, "hash_len", dns_rr->rdata.nsec3.hash_len);
            cJSON_AddStringToObject(dns_rr_object, "salt_value", salt_value);
            cJSON_AddStringToObject(dns_rr_object, "next_hash_owner", tmp_buf);
            cJSON_AddStringToObject(dns_rr_object, "type_bit_maps", maps);
            break;
        case DNS_TYPE_NSEC3PARAM:
            len = 0;
            assert(dns_rr->rdata.nsec3param.salt_len * 2 < sizeof(tmp_buf));
            for (j = 0; j < (int)(dns_rr->rdata.nsec3param.salt_len); j++)
            {
                len += snprintf(tmp_buf + len, sizeof(tmp_buf) - len, "%02x", dns_rr->rdata.nsec3param.salt_value[j]);
            }

            used_len += snprintf(buf + used_len, buflen - used_len,
                                 "[NSEC3PARAM hash_algo: %u, flags: %u, iteration: %u, salt_len: %u, salt_value: %s]\n",
                                 dns_rr->rdata.nsec3param.hash_algo, dns_rr->rdata.nsec3param.flags,
                                 dns_rr->rdata.nsec3param.iteration, dns_rr->rdata.nsec3param.salt_len,
                                 tmp_buf);

            cJSON_AddNumberToObject(dns_rr_object, "hash_algo", dns_rr->rdata.nsec3param.hash_algo);
            cJSON_AddNumberToObject(dns_rr_object, "flags", dns_rr->rdata.nsec3param.flags);
            cJSON_AddNumberToObject(dns_rr_object, "iteration", dns_rr->rdata.nsec3param.iteration);
            cJSON_AddNumberToObject(dns_rr_object, "salt_len", dns_rr->rdata.nsec3param.salt_len);
            cJSON_AddStringToObject(dns_rr_object, "salt_value", tmp_buf);
            break;
        case DNS_TYPE_UNKNOWN:
            used_len += snprintf(buf + used_len, buflen - used_len, "[data: %s]\n", dns_rr->rdata.unknown_data);
            cJSON_AddStringToObject(dns_rr_object, "data", (const char *)(dns_rr->rdata.unknown_data));
            break;
        case DNS_QTYPE_AXFR:
            continue;
            break;
        case DNS_QTYPE_MAILB:
            continue;
            break;
        case DNS_QTYPE_MAILA:
            continue;
            break;
        case DNS_QTYPE_ANY:
            continue;
            break;
        default:
            continue;
            break;
        }
        fwrite(buf, used_len, 1, fp);
    }

    fclose(fp);
    fp = NULL;

    //char *question_array=cJSON_Print(dns_info_object);
    //char *rr_array=cJSON_Print(dns_info_object);

    //cJSON_AddArrayToObject(dns_info_object, question_array);
    //cJSON_AddArrayToObject(dns_info_object, rr_array);

    char *outout1 = cJSON_PrintUnformatted(dns_info_object);
    char *outout2 = cJSON_Print(dns_info_object);

    printf("%s\n", outout1);
    printf("%s\n", outout2);
    free(outout1);
    free(outout2);
    cJSON_Delete(dns_info_object);

    return 0;
}

static void util_dump_dns(const char *data, int len, int is_req)
{
    int en_len;
    int de_len;
    int out_len;
    char en_buff[102400] = {0};
    char de_buff[102400] = {0};
    char out_buff[102400 * 2] = {0};

    en_len = base64_encode(en_buff, sizeof(en_buff), data, len);
    if (en_len == MS_ERROR)
    {
        ERROR("base64_encode() failed\n");
    }

    de_len = base64_decode(de_buff, sizeof(de_buff), en_buff, en_len);
    if (de_len == MS_ERROR)
    {
        ERROR("base64_encode() failed\n");
    }

    if (de_len != len || strncmp(data, de_buff, de_len) != 0)
    {
        ERROR("base64_encode() != base64_decode()\n");
    }

    FILE *fp = fopen("dns_test_base64.log", "a+");
    if (fp == NULL)
    {
        ERROR("fopen() failed\n");
    }
    out_len = snprintf(out_buff, sizeof(out_buff), "dir:%s len:%04d, data:%s\n", is_req ? "req" : "rsp", en_len, en_buff);
    printf("%s", out_buff);
    fwrite(out_buff, out_len, 1, fp);
    fclose(fp);
}

static int util_proxy_dns(char *req_buff, int req_len, char *rsp_buff, int rsp_size)
{
    int ret;
    int clientfd;
    struct sockaddr_in saddr;
    socklen_t addrlen = sizeof(struct sockaddr_in);

    clientfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (clientfd == -1)
    {
        ERROR("socket() failed\n");
    }

    memset(&saddr, 0, sizeof(saddr));
    saddr.sin_family = PF_INET;
    saddr.sin_port = htons(53);
    saddr.sin_addr.s_addr = inet_addr("114.114.114.114");

    ret = sendto(clientfd, req_buff, req_len, 0, (struct sockaddr *)&saddr, addrlen);
    if (ret < 0)
    {
        ERROR("sendto() failed\n");
    }

    ret = recvfrom(clientfd, rsp_buff, rsp_size, 0, (struct sockaddr *)&saddr, &addrlen);
    if (ret < 0)
    {
        ERROR("recvfrom() failed\n");
    }

    close(clientfd);

    return ret;
}

static int util_parser_dns(const char *buff, int len, char *out_buff, int size)
{
    int ret;

    dns_info_t *parser = dns_new();
    dns_info_t *package = dns_new();
    assert(parser);
    assert(package);

    // buff to struct
    ret = dns_parser(parser, (char *)buff, len);
    if (ret == -1)
    {
        ERROR("dns_parser() failed\n");
    }

    // struct to buff
    ret = dns_package(parser, out_buff, size);
    if (ret == -1)
    {
        ERROR("dns_package() failed\n");
    }

    if (dns_parser(package, out_buff, ret) == -1)
    {
        ERROR("dns_parser() failed\n");
    }

    printf("dns first parser\n");
    dns_print(parser, "./dns_test_parser1.log");
    printf("dns second parser\n");
    dns_print(package, "./dns_test_parser2.log");

    if (len != ret || strncmp(buff, out_buff, len) != 0)
    {
        printf("==================================\n"
               "first parser != second parser\n"
               "==================================\n");
    }

    dns_free(parser);
    dns_free(package);

    return ret;
}

static void test_as_dns_proxy_not_cheat_respone(void)
{
    int len;
    int ret;
    int listenfd = -1;
    int reuseaddr = 1;
    char req_buff[10240];
    char rsp_buff[10240];
    char req_pacakge[10240];
    char rsp_package[10240];

    struct sockaddr_in saddr;
    struct sockaddr_in caddr;
    socklen_t addrlen = sizeof(struct sockaddr_in);

    listenfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (listenfd == -1)
    {
        ERROR("socket() failed\n");
    }

    if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)) == -1)
    {
        ERROR("setsockopt() failed\n");
    }

    memset(&saddr, 0, sizeof(saddr));
    saddr.sin_family = PF_INET;
    saddr.sin_port = htons(53);
    saddr.sin_addr.s_addr = INADDR_ANY;
    if (bind(listenfd, (struct sockaddr *)&saddr, sizeof(struct sockaddr)) == -1)
    {
        ERROR("bind() failed\n");
    }

    while (1)
    {
        memset(req_buff, 0, sizeof(req_buff));
        memset(rsp_buff, 0, sizeof(rsp_buff));
        ret = recvfrom(listenfd, req_buff, sizeof(req_buff), 0, (struct sockaddr *)&caddr, &addrlen);
        if (ret < 0)
        {
            ERROR("recvfrom() failed\n");
        }

        // DNS 请求包 dump
        util_dump_dns(req_buff, ret, 1);

        // DNS 请求包 解析/封装 测试
        printf("============ parser req ============\n");
        len = util_parser_dns(req_buff, ret, req_pacakge, sizeof(req_pacakge));

        ret = util_proxy_dns(req_buff, ret, rsp_buff, sizeof(rsp_buff));

        // DNS 响应包 dump
        util_dump_dns(rsp_buff, ret, 0);

        // DNS 响应包 解析/封装 测试
        printf("============ parser rsp ============\n");
        len = util_parser_dns(rsp_buff, ret, rsp_package, sizeof(rsp_package));

        ret = sendto(listenfd, rsp_package, len, 0, (struct sockaddr *)&caddr, addrlen);
        if (ret < 0)
        {
            ERROR("sendto() failed\n");
        }
    }
}

static void test_as_dns_proxy_but_cheat_respone(void)
{
    int ret;
    int listenfd = -1;
    int reuseaddr = 1;
    char req_buff[10240];
    char rsp_buff[10240];

    struct sockaddr_in saddr;
    struct sockaddr_in caddr;
    socklen_t addrlen = sizeof(struct sockaddr_in);

    listenfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (listenfd == -1)
    {
        ERROR("socket() failed\n");
    }

    if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)) == -1)
    {
        ERROR("setsockopt() failed\n");
    }

    memset(&saddr, 0, sizeof(saddr));
    saddr.sin_family = PF_INET;
    saddr.sin_port = htons(53);
    saddr.sin_addr.s_addr = INADDR_ANY;
    if (bind(listenfd, (struct sockaddr *)&saddr, sizeof(struct sockaddr)) == -1)
    {
        ERROR("bind() failed\n");
    }

    cheat_pkt_opt_t cheat_opt[32];
    memset(cheat_opt, 0, sizeof(cheat_opt));

    // DNS_TYPE_A
    cheat_opt[0].res_type = DNS_TYPE_A;
    cheat_opt[0].ttl = 30;
    cheat_opt[0].res_len = 4;
    inet_pton(AF_INET, "192.168.1.1", cheat_opt[0].res_info);

    // DNS_TYPE_AAAA
    cheat_opt[1].res_type = DNS_TYPE_AAAA;
    cheat_opt[1].ttl = 60;
    cheat_opt[1].res_len = 16;
    inet_pton(AF_INET6, "fe80::230:64ff:fe57:4a15", cheat_opt[1].res_info);

    // DNS_TYPE_CNAME
    cheat_opt[2].res_type = DNS_TYPE_CNAME;
    cheat_opt[2].ttl = 90;
    cheat_opt[2].res_len = sizeof("www.cname1.com");
    memcpy(cheat_opt[2].res_info, "www.cname1.com", cheat_opt[2].res_len);

    // DNS_TYPE_NS
    cheat_opt[3].res_type = DNS_TYPE_NS;
    cheat_opt[3].ttl = 90;
    cheat_opt[3].res_len = sizeof("www.ns.com");
    memcpy(cheat_opt[3].res_info, "www.ns.com", cheat_opt[3].res_len);

    while (1)
    {
        memset(req_buff, 0, sizeof(req_buff));
        memset(rsp_buff, 0, sizeof(rsp_buff));
        ret = recvfrom(listenfd, req_buff, sizeof(req_buff), 0, (struct sockaddr *)&caddr, &addrlen);
        if (ret < 0)
        {
            ERROR("recvfrom() failed\n");
        }

        // DNS 请求包 dump
        util_dump_dns(req_buff, ret, 1);

        // 解析 DNS 请求包
        dns_info_t *parser = dns_new();
        assert(parser);
        ret = dns_parser(parser, req_buff, ret);
        if (ret == -1)
        {
            ERROR("dns_parser() failed\n");
        }

        // 扫描策略

        // 若命中策略，根据策略信息伪造 DNS 响应

        // 伪造 DNS 响应包
        ret = dns_cheat_response(parser, cheat_opt, 4, rsp_buff, sizeof(rsp_buff));
        if (ret == -1)
        {
            ERROR("dns_cheat_response() failed\n");
        }
        dns_free(parser);

        // DNS 响应包 dump
        util_dump_dns(rsp_buff, ret, 0);

        ret = sendto(listenfd, rsp_buff, ret, 0, (struct sockaddr *)&caddr, addrlen);
        if (ret < 0)
        {
            ERROR("sendto() failed\n");
        }
    }
}

static void test_use_offline_package(void)
{
    int i;
    int req_len;
    int rsp_len;
    char req_buff[102400] = {0};
    char rsp_buff[102400] = {0};
    char out_buff[102400 * 2] = {0};

    for (i = 0; i < package_arry_size; i++)
    {
        printf("test[%d]: %s\n", i, package_arry[i].desc);
        req_len = base64_decode(req_buff, sizeof(req_buff), package_arry[i].req.data, package_arry[i].req.len);
        if (req_len == MS_ERROR)
        {
            ERROR("base64_encode() failed\n");
        }
        printf("============ parser req ============\n");
        util_parser_dns(req_buff, req_len, out_buff, sizeof(out_buff));

        rsp_len = base64_decode(rsp_buff, sizeof(rsp_buff), package_arry[i].rsp.data, package_arry[i].rsp.len);
        if (rsp_len == MS_ERROR)
        {
            ERROR("base64_encode() failed\n");
        }
        printf("============ parser rsp ============\n");
        util_parser_dns(rsp_buff, rsp_len, out_buff, sizeof(out_buff));
    }
}

static void test_use_stdin_package(char *package)
{
    int req_len;
    char req_buff[102400] = {0};
    char out_buff[102400 * 2] = {0};

    req_len = base64_decode(req_buff, sizeof(req_buff), package, strlen(package));
    if (req_len == MS_ERROR)
    {
        ERROR("base64_encode() failed\n");
    }
    util_parser_dns(req_buff, req_len, out_buff, sizeof(out_buff));
}

static void usage(char *name)
{
    printf("Usage: %s [option]\n"
           "    -p                : test as dns proxy not cheat respone (dig @127.0.0.1 www.baidu.com a) dump data in base64\n"
           "    -c                : test as dns proxy but cheat respone (dig @127.0.0.1 www.baidu.com a) dump data in base64\n"
           "\n"
           "    -o                : test use offline dns package\n"
           "    -b ${base64_pack} : test use stdin   dns package\n",
           name);
    exit(0);
}

int main(int argc, char **argv)
{
    if (argc != 2 && argc != 3)
    {
        usage(argv[0]);
    }

    if (argc == 2 && (strncmp(argv[1], "-o", 2) != 0 && strncmp(argv[1], "-p", 2) != 0 && strncmp(argv[1], "-c", 2) != 0))
    {
        usage(argv[0]);
    }
    if (argc == 3 && strncmp(argv[1], "-b", 2) != 0)
    {
        usage(argv[0]);
    }

    if (strncmp(argv[1], "-p", 2) == 0)
    {
        test_as_dns_proxy_not_cheat_respone();
    }
    else if (strncmp(argv[1], "-c", 2) == 0)
    {
        test_as_dns_proxy_but_cheat_respone();
    }
    else if (strncmp(argv[1], "-o", 2) == 0)
    {
        test_use_offline_package();
    }
    else if (strncmp(argv[1], "-b", 2) == 0)
    {
        test_use_stdin_package(argv[2]);
    }

    return 0;
}
