Beispiel #1
0
static int rdllib_init (struct rdllib *rl)
{
    char *error = NULL;

    /* dlopen liblua.so to pickup symbols that will be referenced by
     * "require" */
    dlopen ("liblua.so", RTLD_NOW | RTLD_GLOBAL);
    if ((error = dlerror()) != NULL)
        dlopen ("liblua5.1.so", RTLD_NOW | RTLD_GLOBAL);
    if ((error = dlerror()) != NULL)  {
        VERR (rl, "dlopen(liblua.so) failed: %s\n", error);
        return (-1);
    }

    /* XXX: Is there no equivalent from C? */
    lua_getglobal (rl->L, "require");
    lua_pushstring (rl->L, "RDL");
    if (lua_pcall (rl->L, 1, 1, 0)) {
        VERR (rl, "loading RDL: %s\n", lua_tostring (rl->L, -1));
        return (-1);
    }
    if (!lua_istable (rl->L, -1)) {
        VERR (rl, "Failed to load RDL: %s\n", lua_tostring (rl->L, -1));
    }
    /*
     *  Assign implementation to global RDL table.
     */
    lua_setglobal (rl->L, "RDL");

    lua_settop (rl->L, 0);
    return (0);
}
Beispiel #2
0
static int dns_init_sockets() {
    struct addrinfo hints;
    struct addrinfo *addr_ip;
    int r;

    local_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (0 != setnonblock(local_sock))
        return -1;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_DGRAM;
    if (0 != (r = getaddrinfo(listen_addr, listen_port, &hints, &addr_ip))) {
        VERR("%s:%s:%s\n", gai_strerror(r), listen_addr, listen_port);
        return -1;
    }
    if (0 != bind(local_sock, addr_ip->ai_addr, addr_ip->ai_addrlen)) {
        ERR("bind");
        VERR("Can't bind address %s:%s\n", listen_addr, listen_port);
        return -1;
    }
    freeaddrinfo(addr_ip);
    remote_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (0 != setnonblock(remote_sock))
        return -1;
    return 0;
}
Beispiel #3
0
static int parse_chnroute() {
    FILE *fp;
    char line_buf[32];
    char *line;
    size_t len = sizeof(line_buf);
    ssize_t read;
    char net[32];
    chnroute_list.entries = 0;
    int i = 0;

    if (chnroute_file == NULL) {
        VERR("CHNROUTE_FILE not specified, CHNRoute is disabled\n");
        return 0;
    }

    fp = fopen(chnroute_file, "rb");
    if (fp == NULL) {
        ERR("fopen");
        VERR("Can't open chnroute: %s\n", chnroute_file);
        return -1;
    }
    while ((line = fgets(line_buf, len, fp))) {
        chnroute_list.entries++;
    }

    chnroute_list.nets = calloc(chnroute_list.entries, sizeof(net_mask_t));
    if (0 != fseek(fp, 0, SEEK_SET)) {
        VERR("fseek");
        return -1;
    }
    while ((line = fgets(line_buf, len, fp))) {
        char *sp_pos;
        sp_pos = strchr(line, '\r');
        if (sp_pos) *sp_pos = 0;
        sp_pos = strchr(line, '\n');
        if (sp_pos) *sp_pos = 0;
        sp_pos = strchr(line, '/');
        if (sp_pos) {
            *sp_pos = 0;
            chnroute_list.nets[i].mask = (1 << (32 - atoi(sp_pos + 1))) - 1;
        } else {
            chnroute_list.nets[i].mask = UINT32_MAX;
        }
        if (0 == inet_aton(line, &chnroute_list.nets[i].net)) {
            VERR("invalid addr %s in %s:%d\n", line, chnroute_file, i + 1);
            return 1;
        }
        i++;
    }

    qsort(chnroute_list.nets, chnroute_list.entries, sizeof(net_mask_t),
          cmp_net_mask);

    fclose(fp);
    return 0;
}
Beispiel #4
0
static int parse_chnroute() {
  FILE * fp;
  char * line = NULL;
  size_t len = 0;
  ssize_t read;
  char net[32];
  chnroute_list.entries = 0;
  int i = 0;

  if (chnroute_file == NULL) {
    VERR("CHNROUTE_FILE not specified, CHNRoute is disabled\n");
    return 0;
  }

  fp = fopen(chnroute_file, "rb");
  if (fp == NULL) {
    ERR("fopen");
    VERR("Can't open chnroute: %s\n", chnroute_file);
    return -1;
  }
  while ((read = getline(&line, &len, fp)) != -1) {
    chnroute_list.entries++;
  }
  if (line)
    free(line);
  line = NULL;

  chnroute_list.nets = calloc(chnroute_list.entries, sizeof(net_mask_t));
  if (0 != fseek(fp, 0, SEEK_SET)) {
    VERR("fseek");
    return -1;
  }
  while ((read = getline(&line, &len, fp)) != -1) {
    char *sp_pos = strchr(line, '/');
    *sp_pos = 0;
    chnroute_list.nets[i].mask = (1 << (32 - atoi(sp_pos + 1))) - 1;
    inet_aton(line, &chnroute_list.nets[i].net);
    i++;
  }
  if (line)
    free(line);

  qsort(chnroute_list.nets, chnroute_list.entries, sizeof(net_mask_t),
        cmp_net_mask);

  fclose(fp);
  return 0;
}
Beispiel #5
0
void rdl_resource_delete_tag (struct resource *r, const char *tag)
{
    if (rdl_resource_method_call1_string (r, "delete_tag", tag) < 0) {
        VERR (r->rdl->rl, "delete_tag (%s): %s\n", tag,
              lua_tostring (r->rdl->L, -1));
    }
}
Beispiel #6
0
static int rdl_dostringf (struct rdl *rdl, const char *fmt, ...)
{
    char *s;
    int rc;
    int top;

    va_list ap;
    va_start (ap, fmt);
    rc = vasprintf (&s, fmt, ap);
    va_end (ap);

    if (rc < 0)
        return (-1);

    top = lua_gettop (rdl->L);

    if (  luaL_loadstring (rdl->L, s)
       || lua_pcall (rdl->L, 0, LUA_MULTRET, 0)) {
        VERR (rdl->rl, "dostring (%s): %s\n", s, lua_tostring (rdl->L, -1));
        lua_settop (rdl->L, 0);
        free (s);
        return (-1);
    }
    free (s);

    return (lua_gettop (rdl->L) - top);
}
Beispiel #7
0
bool rdl_accumulator_is_empty (struct rdl_accumulator *a)
{
  bool ret_val = true;
  lua_State *L = a->rdl->L;

  lua_rdl_accumulator_method_push (a, "is_empty");

  if (lua_pcall (L, 1, LUA_MULTRET, 0)) {
    VERR (a->rdl->rl, "accumulator_is_empty had an error in pcall.  Additional info: %s\n", lua_tostring (L, -1));
  } else if (lua_isnoneornil (L, -1)) {
    VERR (a->rdl->rl, "lua method 'is_empty' returned none or nil. Additional info: %s\n", lua_tostring (L, -1));
  } else {
    ret_val = lua_toboolean(L, -1);
  }

  lua_settop (L, 0);
  return ret_val;
}
Beispiel #8
0
int rdl_accumulator_add (struct rdl_accumulator *a, struct resource *r)
{
    size_t n = rdl_resource_available (r);
    if (n <= 0) {
        VERR (a->rdl->rl, "accumulator_add: Insufficient capacity in resource");
        return (-1);
    }
    return rdl_accumulator_add_n (a, r, n);
}
Beispiel #9
0
struct rdl * rdl_find (struct rdl *rdl, json_object *args)
{
    lua_rdl_method_push (rdl, "find");

    if (json_object_to_lua (rdl->L, args) < 0) {
        VERR (rdl->rl, "Failed to convert JSON to Lua\n");
        return (NULL);
    }
    /*
     *  stack: [ Method, object, args-table ]
     */
    if (lua_pcall (rdl->L, 2, LUA_MULTRET, 0) || lua_isnoneornil (rdl->L, 1)) {
        VERR (rdl->rl, "find(%s): %s\n",
                json_object_to_json_string (args),
                lua_tostring (rdl->L, -1));
        return (NULL);
    }

    return (lua_pop_new_rdl (rdl));
}
Beispiel #10
0
static int parse_ip_list() {
  FILE *fp;
  char line_buf[32];
  char *line = NULL;
  size_t len = sizeof(line_buf);
  ssize_t read;
  ip_list.entries = 0;
  int i = 0;

  if (ip_list_file == NULL)
    return 0;

  fp = fopen(ip_list_file, "rb");
  if (fp == NULL) {
    ERR("fopen");
    VERR("Can't open ip list: %s\n", ip_list_file);
    return -1;
  }
  while ((line = fgets(line_buf, len, fp))) {
    ip_list.entries++;
  }

  ip_list.ips = calloc(ip_list.entries, sizeof(struct in_addr));
  if (0 != fseek(fp, 0, SEEK_SET)) {
    VERR("fseek");
    return -1;
  }
  while ((line = fgets(line_buf, len, fp))) {
    char *sp_pos;
    sp_pos = strchr(line, '\r');
    if (sp_pos) *sp_pos = 0;
    sp_pos = strchr(line, '\n');
    if (sp_pos) *sp_pos = 0;
    inet_aton(line, &ip_list.ips[i]);
    i++;
  }

  qsort(ip_list.ips, ip_list.entries, sizeof(struct in_addr), cmp_in_addr);
  fclose(fp);
  return 0;
}
Beispiel #11
0
char * rdl_accumulator_serialize (struct rdl_accumulator *a)
{
    char *s;
    lua_State *L = a->rdl->L;
    lua_rdl_accumulator_method_push (a, "serialize");
    if (lua_pcall (L, 1, LUA_MULTRET, 0)) {
        VERR (a->rdl->rl, "accumulator:serialize: %s\n", lua_tostring (L, -1));
        return (NULL);
    }
    asprintf (&s, "-- RDL v1.0\n%s", lua_tostring (L, -1));
    lua_settop (L, 0);
    return (s);
}
Beispiel #12
0
/*
 *  Call [method] on resource [r] and return resulting Lua table
 *   as a json-c json_object.
 */
static json_object *
rdl_resource_method_to_json (struct resource *r, const char *method)
{
    json_object *o = NULL;
    lua_State *L = r->rdl->L;

    if (lua_rdl_resource_method_call (r,  method)) {
        VERR (r->rdl->rl, "json: %s\n", lua_tostring (L, -1));
        return (NULL);
    }
    if (lua_type (L, -1) != LUA_TTABLE) {
        VERR (r->rdl->rl, "json: Failed to get table. Got %s\n",
                             luaL_typename (L, -1));
        lua_pop (L, 1);
        return (NULL);
    }
    if (lua_value_to_json (L, -1, &o) < 0)
        o = NULL;

    /* Keep Lua stack clean */
    lua_settop (L, 0);
    return (o);
}
Beispiel #13
0
struct resource * rdl_resource_get (struct rdl *rdl, const char *uri)
{
    struct resource *r;
    if (uri == NULL)
        uri = "default";
    rdl_dostringf (rdl, "return rdl:resource ('%s')", uri);
    if (lua_type (rdl->L, -1) != LUA_TTABLE) {
        VERR (rdl->rl, "resource (%s): %s\n", uri, lua_tostring (rdl->L, -1));
        return (NULL);
    }
    r = create_resource_ref (rdl, -1);
    lua_settop (rdl->L, 0);
    return (r);
}
Beispiel #14
0
static int parse_ip_list() {
  FILE * fp;
  char * line = NULL;
  size_t len = 0;
  ssize_t read;
  ip_list.entries = 0;
  int i = 0;

  fp = fopen(ip_list_file, "rb");
  if (fp == NULL) {
    ERR("fopen");
    VERR("Can't open ip list: %s\n", ip_list_file);
    return -1;
  }
  while ((read = getline(&line, &len, fp)) != -1) {
    ip_list.entries++;
  }
  if (line)
    free(line);
  line = NULL;

  ip_list.ips = calloc(ip_list.entries, sizeof(struct in_addr));
  if (0 != fseek(fp, 0, SEEK_SET)) {
    VERR("fseek");
    return -1;
  }
  while ((read = getline(&line, &len, fp)) != -1) {
    inet_aton(line, &ip_list.ips[i]);
    i++;
  }
  if (line)
    free(line);

  qsort(ip_list.ips, ip_list.entries, sizeof(struct in_addr), cmp_in_addr);
  fclose(fp);
  return 0;
}
Beispiel #15
0
static struct rdl * loadfn (struct rdllib *rl, const char *fn, const char *s)
{
    int rc;
    struct rdl * rdl = rdl_new (rl);
    if (rdl == NULL)
        return NULL;

    /*
     *  First, get function to evaluate rdl:
     */
    rc = rdl_dostringf (rdl, "return require 'RDL'.%s", fn);
    if (rc <= 0) {
        VERR (rl, "rdl_load: Failed to get function RDL.%s\n", fn);
        rdl_destroy (rdl);
        return (NULL);
    }

    /*
     *  Now push function arg `s' onto stack, and evaluate the function:
     */
    if (s)
        lua_pushstring (rdl->L, s);
    if (lua_pcall (rdl->L, s?1:0, LUA_MULTRET, 0)) {
        VERR (rl, "rdl_load: RDL.%s: %s\n", fn, lua_tostring (rdl->L, -1));
        rdl_destroy (rdl);
        return (NULL);
    }

    if (lua_type (rdl->L, -1) != LUA_TTABLE) {
        VERR (rl, "rdl_load: %s\n", lua_tostring (rdl->L, -1));
        rdl_destroy (rdl);
        return (NULL);
    }
    lua_setglobal (rdl->L, "rdl");
    lua_settop (rdl->L, 0);
    return (rdl);
}
Beispiel #16
0
struct rdl_accumulator * rdl_accumulator_create (struct rdl *rdl)
{
    struct rdl_accumulator *a;

    rdl_dostringf (rdl, "return rdl:resource_accumulator()");
    if (lua_type (rdl->L, -1) != LUA_TTABLE) {
        VERR (rdl->rl, "accumlator_create: %s\n", lua_tostring (rdl->L, -1));
        return (NULL);
    }
    a = malloc (sizeof (*a));
    a->lua_ref = luaL_ref (rdl->L, LUA_GLOBALSINDEX);
    a->rdl = rdl;
    lua_settop (rdl->L, 0);
    return (a);
}
Beispiel #17
0
struct resource * rdl_resource_next_child (struct resource *r)
{
    struct resource *c;
    if (lua_rdl_resource_method_call (r, "next_child")) {
        VERR (r->rdl->rl, "next child: %s\n", lua_tostring (r->rdl->L, -1));
        return NULL;
    }
    if (lua_isnil (r->rdl->L, -1)) {
        /* End of child list is indicated by nil return */
        return (NULL);
    }
    c = create_resource_ref (r->rdl, -1);
    lua_settop (r->rdl->L, 0);
    return (c);
}
Beispiel #18
0
int rdl_resource_set_int (struct resource *r, const char *tag, int64_t val)
{
    int rc = 0;
    lua_State *L = r->rdl->L;

    if (lua_rdl_resource_method_push (r, "tag") < 0)
        return (-1);
    lua_pushstring (L, tag);
    lua_pushnumber (L, val);
    if (lua_pcall (L, 3, LUA_MULTRET, 0) || lua_isnoneornil (L, 1)) {
        VERR (r->rdl->rl, "%s(%s): %s\n", "tag", tag, lua_tostring (L, -1));
        rc = -1;
    }
    lua_settop (L, 0);
    return (rc);
}
Beispiel #19
0
struct rdl * rdl_accumulator_copy (struct rdl_accumulator *a)
{
    struct rdl *rdl;
    char *s;

    if (a == NULL)
        return (NULL);

    if ((s = rdl_accumulator_serialize (a)) == NULL) {
        VERR (a->rdl->rl, "serialization failure\n");
        return (NULL);
    }
    rdl = rdl_load (a->rdl->rl, s);
    free (s);
    return (rdl);
}
Beispiel #20
0
const char *rdl_next_hierarchy (struct rdl *rdl, const char *last)
{
    lua_rdl_method_push (rdl, "hierarchy_next");

    if (last)
        lua_pushstring (rdl->L, last);
    else
        lua_pushnil (rdl->L);

    /* stack: [ Method, object, last ] */
    if (lua_pcall (rdl->L, 2, LUA_MULTRET, 0)) {
        VERR (rdl->rl, "next_hierarchy: %s\n", lua_tostring (rdl->L, -1));
        return (NULL);
    }
    if (lua_isnil (rdl->L, -1)) {
        /* End of child list is indicated by nil return */
        return (NULL);
    }
    return (lua_tostring (rdl->L, -1));
}
Beispiel #21
0
int rdl_accumulator_add_n (struct rdl_accumulator *a, struct resource *r,
                           size_t n)
{
    int rc = 0;
    lua_State *L = a->rdl->L;

    lua_rdl_accumulator_method_push (a, "add");
    if (lua_rdl_resource_getfield (r, "uuid") < 0)
        return (-1);

    lua_pushnumber (L, n);

    /* Stack: [ Method, Object, arg1, arg2 ] */
    if (lua_pcall (L, 3, LUA_MULTRET, 0) || lua_isnoneornil (L, 1)) {
        VERR (a->rdl->rl, "accumulator_add: %s\n", lua_tostring (L, -1));
        rc = -1;
    }
    lua_settop (L, 0);
    return rc;
}
Beispiel #22
0
static int resolve_dns_servers() {
    struct addrinfo hints;
    struct addrinfo *addr_ip;
    char* token;
    int r;
    int i = 0;
    char *pch = strchr(dns_servers, ',');
    dns_servers_len = 1;
    while (pch != NULL) {
        dns_servers_len++;
        pch = strchr(pch + 1, ',');
    }
    dns_server_addrs = calloc(dns_servers_len, sizeof(id_addr_t));

    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
    token = strtok(dns_servers, ",");
    while (token) {
        char *port;
        memset(global_buf, 0, BUF_SIZE);
        strncpy(global_buf, token, BUF_SIZE - 1);
        port = (strrchr(global_buf, ':'));
        if (port) {
            *port = '\0';
            port++;
        } else {
            port = "53";
        }
        if (0 != (r = getaddrinfo(global_buf, port, &hints, &addr_ip))) {
            VERR("%s:%s\n", gai_strerror(r), token);
            return -1;
        }
        dns_server_addrs[i].addr = addr_ip->ai_addr;
        dns_server_addrs[i].addrlen = addr_ip->ai_addrlen;
        i++;
        token = strtok(0, ",");
    }
    return 0;
}
Beispiel #23
0
static int rdl_resource_method_call1_keepstack (struct resource *r,
    const char *method, enum method_arg_type mtype, void *argptr)
{
    int rc = 0;
    lua_State *L = r->rdl->L;
    if (lua_rdl_resource_method_push (r, method) < 0)
        return (-1);

    if (mtype == M_ARG_TYPE_STRING)
        lua_pushstring (L, *(char **)argptr);
    else if (mtype == M_ARG_TYPE_INTEGER)
        lua_pushinteger (L, (lua_Integer) *(size_t *)argptr);

    /*
     *  stack: [ Method, object, arg ]
     */
    if (lua_pcall (L, 2, LUA_MULTRET, 0) || lua_isnoneornil (L, 1)) {
        VERR (r->rdl->rl, "%s(): %s\n", method, lua_tostring (L, -1));
        lua_settop (L, 0);
        rc = -1;
    }
    return (rc);
}
Beispiel #24
0
int main(int argc, char **argv) {
    fd_set readset, errorset;
    int max_fd;

    memset(&id_addr_queue, 0, sizeof(id_addr_queue));
    memset(&delay_queue, 0, sizeof(delay_queue));
    if (0 != parse_args(argc, argv))
        return EXIT_FAILURE;
    if (0 != parse_ip_list())
        return EXIT_FAILURE;
    if (0 != parse_chnroute())
        return EXIT_FAILURE;
    if (0 != resolve_dns_servers())
        return EXIT_FAILURE;
    if (0 != dns_init_sockets())
        return EXIT_FAILURE;

    printf("%s\n", version);
    max_fd = MAX(local_sock, remote_sock) + 1;
    while (1) {
        FD_ZERO(&readset);
        FD_ZERO(&errorset);
        FD_SET(local_sock, &readset);
        FD_SET(local_sock, &errorset);
        FD_SET(remote_sock, &readset);
        FD_SET(remote_sock, &errorset);
        struct timeval timeout = {
            .tv_sec = 1,
            .tv_usec = 0,
        };
        if (-1 == select(max_fd, &readset, NULL, &errorset, &timeout)) {
            ERR("select");
            return EXIT_FAILURE;
        }
        check_and_send_delay();
        if (FD_ISSET(local_sock, &errorset)) {
            // TODO getsockopt(..., SO_ERROR, ...);
            VERR("local_sock error\n");
            return EXIT_FAILURE;
        }
        if (FD_ISSET(remote_sock, &errorset)) {
            // TODO getsockopt(..., SO_ERROR, ...);
            VERR("remote_sock error\n");
            return EXIT_FAILURE;
        }
        if (FD_ISSET(local_sock, &readset))
            dns_handle_local();
        if (FD_ISSET(remote_sock, &readset))
            dns_handle_remote();
    }
    return EXIT_SUCCESS;
}

static int setnonblock(int sock) {
    int flags;
    flags = fcntl(sock, F_GETFL, 0);
    if (flags == -1) {
        ERR("fcntl");
        return -1;
    }
    if (-1 == fcntl(sock, F_SETFL, flags | O_NONBLOCK)) {
        ERR("fcntl");
        return -1;
    }
    return 0;
}
Beispiel #25
0
void rdl_resource_iterator_reset (struct resource *r)
{
    if (lua_rdl_resource_method_call (r, "reset"))
        VERR (r->rdl->rl, "iterator reset: %s\n", lua_tostring (r->rdl->L, -1));
}
Beispiel #26
0
static int resolve_dns_servers() {
  struct addrinfo hints;
  struct addrinfo *addr_ip;
  char* token;
  int r;
  int i = 0;
  char *pch = strchr(dns_servers, ',');
  has_chn_dns = 0;
  int has_foreign_dns = 0;
  dns_servers_len = 1;
  if (compression) {
    if (!chnroute_file) {
      VERR("Chnroutes are necessary when using DNS compression pointer mutation\n");
      return -1;
    }
  }
  while (pch != NULL) {
    dns_servers_len++;
    pch = strchr(pch + 1, ',');
  }
  dns_server_addrs = calloc(dns_servers_len, sizeof(id_addr_t));

  memset(&hints, 0, sizeof(hints));
  hints.ai_family = AF_INET;
  hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
  token = strtok(dns_servers, ",");
  while (token) {
    char *port;
    memset(global_buf, 0, BUF_SIZE);
    strncpy(global_buf, token, BUF_SIZE - 1);
    port = (strrchr(global_buf, ':'));
    if (port) {
      *port = '\0';
      port++;
    } else {
      port = "53";
    }
    if (0 != (r = getaddrinfo(global_buf, port, &hints, &addr_ip))) {
      VERR("%s:%s\n", gai_strerror(r), token);
      return -1;
    }
    if (compression) {
      if (test_ip_in_list(((struct sockaddr_in *)addr_ip->ai_addr)->sin_addr,
                          &chnroute_list)) {
        dns_server_addrs[has_chn_dns].addr = addr_ip->ai_addr;
        dns_server_addrs[has_chn_dns].addrlen = addr_ip->ai_addrlen;
        has_chn_dns++;
      } else {
        has_foreign_dns++;
        dns_server_addrs[dns_servers_len - has_foreign_dns].addr = addr_ip->ai_addr;
        dns_server_addrs[dns_servers_len - has_foreign_dns].addrlen = addr_ip->ai_addrlen;
      }
      token = strtok(0, ",");
    } else {
      dns_server_addrs[i].addr = addr_ip->ai_addr;
      dns_server_addrs[i].addrlen = addr_ip->ai_addrlen;
      i++;
      token = strtok(0, ",");
      if (chnroute_file) {
        if (test_ip_in_list(((struct sockaddr_in *)addr_ip->ai_addr)->sin_addr,
                            &chnroute_list)) {
          has_chn_dns = 1;
        } else {
          has_foreign_dns = 1;
        }
      }
    }
  }
  if (chnroute_file) {
    if (!(has_chn_dns && has_foreign_dns)) {
      if (compression) {
        VERR("You should have at least one Chinese DNS and one foreign DNS when "
             "using DNS compression pointer mutation\n");
        return -1;
      } else {
        VERR("You should have at least one Chinese DNS and one foreign DNS when "
             "chnroutes is enabled\n");
        return 0;
      }
    }
  }
  return 0;
}