netsukuku/src/andna.c
Alexander von Gluck IV e264723db2 all: Automatic style cleanup, KnR. No functional change.
* Style varied wildly between files and within files.
* find . -name "*.c" -exec indent {} -ut -ts4 -kr -psl \;
* find . -name "*.h" -exec indent {} -ut -ts4 -kr -psl \;
2014-09-17 07:41:24 -05:00

2587 lines
68 KiB
C

/* This file is part of Netsukuku
* (c) Copyright 2005 Andrea Lo Pumo aka AlpT <alpt@freaknet.org>
*
* This source code is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* This source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* Please refer to the GNU Public License for more details.
*
* You should have received a copy of the GNU Public License along with
* this source code; if not, write to:
* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* --
* andna.c:
* Here there are all the functions that send, receive and exec ANDNA packets.
* All the threads of the ANDNA daemon and the main andna functions are here
* too.
*/
#include "includes.h"
#include "inet.h"
#include "endianness.h"
#include "map.h"
#include "gmap.h"
#include "bmap.h"
#include "route.h"
#include "request.h"
#include "tracer.h"
#include "qspn.h"
#include "radar.h"
#include "netsukuku.h"
#include "daemon.h"
#include "crypto.h"
#include "snsd_cache.h"
#include "andna_cache.h"
#include "andna.h"
#include "andns.h"
#include "dns_wrapper.h"
#include "hash.h"
#include "common.h"
/*
*
* * * * (de)-Initialization functions * * *
*
*/
/*
* andna_load_caches
*
* loads all the ANDNA caches
*/
int
andna_load_caches(void)
{
int ret = 0;
if (file_exist(server_opt.lclkey_file) &&
(!load_lcl_keyring(&lcl_keyring, server_opt.lclkey_file)))
debug(DBG_NORMAL, "Andna LCL Keyring loaded");
if (file_exist(server_opt.lcl_file) &&
(andna_lcl = load_lcl_cache(server_opt.lcl_file, &lcl_counter)))
debug(DBG_NORMAL, "Andna Local Cache loaded");
if (file_exist(server_opt.andna_cache_file) &&
(andna_c = load_andna_cache(server_opt.andna_cache_file,
&andna_c_counter)))
debug(DBG_NORMAL, "Andna cache loaded");
if (file_exist(server_opt.counter_c_file) &&
(andna_counter_c = load_counter_c(server_opt.counter_c_file,
&cc_counter)))
debug(DBG_NORMAL, "Counter cache loaded");
if (file_exist(server_opt.rhc_file) &&
(andna_rhc = load_rh_cache(server_opt.rhc_file, &rhc_counter)))
debug(DBG_NORMAL, "Resolved hostnames cache loaded");
if (file_exist(server_opt.andna_hnames_file) &&
!(load_hostnames(server_opt.andna_hnames_file,
&andna_lcl, &lcl_counter)))
debug(DBG_NORMAL, "Hostnames file loaded");
if (file_exist(server_opt.snsd_nodes_file) &&
!(ret = load_snsd(server_opt.snsd_nodes_file, andna_lcl)))
debug(DBG_NORMAL, "SNSD nodes loaded");
else if (ret == -2)
fatal("Malformed %s file", server_opt.snsd_nodes_file);
return 0;
}
int
andna_save_caches(void)
{
debug(DBG_NORMAL, "Saving the andna local cache");
save_lcl_cache(andna_lcl, server_opt.lcl_file);
debug(DBG_NORMAL, "Saving the andna cache");
save_andna_cache(andna_c, server_opt.andna_cache_file);
debug(DBG_NORMAL, "Saving the andna counter cache");
save_counter_c(andna_counter_c, server_opt.counter_c_file);
debug(DBG_NORMAL, "Saving the resolved hnames cache");
save_rh_cache(andna_rhc, server_opt.rhc_file);
return 0;
}
/*
* andna_resolvconf_modify: modifies /etc/resolv.conf. See add_resolv_conf().
*/
void
andna_resolvconf_modify(void)
{
int ret;
char *my_nameserv;
if (!server_opt.disable_resolvconf) {
loginfo("Modifying /etc/resolv.conf");
my_nameserv =
my_family == AF_INET ? MY_NAMESERV : MY_NAMESERV_IPV6;
ret = add_resolv_conf(my_nameserv, ETC_RESOLV_CONF);
if (ret < 0)
error("It wasn't possible to modify %s, you have to add "
"\"%s\" by yourself", ETC_RESOLV_CONF, my_nameserv);
} else
loginfo
("Modification of /etc/resolv.conf is disabled: do it by yourself.");
}
void
andna_resolvconf_restore(void)
{
char *my_nameserv;
/*
* If there are valid nameserver in /etc/resolv.conf (excluding
* "127.0.0.1", do not restore the backup. Probably /etc/resolv.conf
* has been edited manually. Damn you, user! Why can't we code you ;)
*/
reset_andns_ns();
if (collect_resolv_conf(ETC_RESOLV_CONF) != -1)
return;
reset_andns_ns();
my_nameserv = my_family == AF_INET ? MY_NAMESERV : MY_NAMESERV_IPV6;
del_resolv_conf(my_nameserv, ETC_RESOLV_CONF);
}
void
andna_init(void)
{
/* register the andna's ops in the pkt_op_table */
add_pkt_op(ANDNA_REGISTER_HNAME, SKT_TCP, andna_tcp_port,
andna_recv_reg_rq);
add_pkt_op(ANDNA_CHECK_COUNTER, SKT_TCP, andna_tcp_port,
andna_recv_check_counter);
add_pkt_op(ANDNA_RESOLVE_HNAME, SKT_UDP, andna_udp_port,
andna_recv_resolve_rq);
add_pkt_op(ANDNA_RESOLVE_REPLY, SKT_UDP, andna_udp_port, 0);
add_pkt_op(ANDNA_RESOLVE_IP, SKT_TCP, andna_tcp_port,
andna_recv_rev_resolve_rq);
add_pkt_op(ANDNA_REV_RESOLVE_REPLY, SKT_TCP, andna_tcp_port, 0);
add_pkt_op(ANDNA_GET_ANDNA_CACHE, SKT_TCP, andna_tcp_port,
put_andna_cache);
add_pkt_op(ANDNA_PUT_ANDNA_CACHE, SKT_TCP, andna_tcp_port, 0);
add_pkt_op(ANDNA_GET_COUNT_CACHE, SKT_TCP, andna_tcp_port,
put_counter_cache);
add_pkt_op(ANDNA_PUT_COUNT_CACHE, SKT_TCP, andna_tcp_port, 0);
add_pkt_op(ANDNA_GET_SINGLE_ACACHE, SKT_UDP, andna_udp_port,
put_single_acache);
add_pkt_op(ANDNA_SPREAD_SACACHE, SKT_UDP, andna_udp_port,
recv_spread_single_acache);
if (!server_opt.disable_resolvconf)
/* Restore resolv.conf if our backup is still there */
andna_resolvconf_restore();
pkt_queue_init();
andna_caches_init(my_family);
snsd_cache_init(my_family);
/* Load the good old caches */
andna_load_caches();
if (!lcl_keyring.priv_rsa) {
/*
* No keyring was loaded, generate a new one save it
*/
lcl_new_keyring(&lcl_keyring);
debug(DBG_NORMAL, "Saving the new andna local keyring");
save_lcl_keyring(&lcl_keyring, server_opt.lclkey_file);
/**/}
/* Init ANDNS */
if (andns_init(restricted_mode, ETC_RESOLV_CONF, my_family) < 0)
if (andns_init(restricted_mode, ETC_RESOLV_CONF_BAK, my_family) <
0) {
error("In %s there isn't a single Internet nameserver.",
ETC_RESOLV_CONF);
loginfo("Internet hostname resolution is disabled");
}
setzero(last_reg_pkt_id, sizeof(int) * ANDNA_MAX_FLOODS);
setzero(last_counter_pkt_id, sizeof(int) * ANDNA_MAX_FLOODS);
setzero(last_spread_acache_pkt_id, sizeof(int) * ANDNA_MAX_FLOODS);
/* Modify /etc/resolv.conf if requested */
andna_resolvconf_modify();
}
void
andna_close(void)
{
andna_save_caches();
if (!server_opt.disable_resolvconf)
andna_resolvconf_restore();
andns_close();
lcl_destroy_keyring(&lcl_keyring);
lcl_cache_destroy(andna_lcl, &lcl_counter);
andna_cache_destroy();
counter_c_destroy();
rh_cache_flush();
pkt_queue_close();
}
/*
*
* * * * Hash_node search functions * * *
*
*/
/*
* andna_hash_by_family
*
* If `family' is equal to AF_INET, in `hash' it stores the
* 32bit hash of the `msg', otherwise it just copies `msg' to `hash_ip'.
* Note that this function is used to hash other hashes, so it operates on the
* ANDNA_HASH_SZ fixed length.
*/
void
andna_hash_by_family(int family, void *msg, u_int hash[MAX_IP_INT])
{
setzero(hash, ANDNA_HASH_SZ);
if (family == AF_INET)
hash[0] = fnv_32_buf((u_char *) msg, ANDNA_HASH_SZ, FNV1_32_INIT);
else
memcpy(hash, msg, ANDNA_HASH_SZ);
}
/*
* andna_hash
*
* This function makes a digest of `msg' which is `len' bytes big and
* stores it in `hash'.
* If `family'is equal to AF_INET, in `ip_hash' it stores the 32bit hash
* of the `hash', otherwise it just copies `hash' to `ip_hash'.
*
* Note: `hash' is a single string of `MAX_IP_INT'*4 bytes, it is the MD5 hash
* of `msg', therefore do not attempt to convert it to network order.
*/
void
andna_hash(int family, void *msg, int len, u_int hash[MAX_IP_INT],
u_int ip_hash[MAX_IP_INT])
{
hash_md5(msg, len, (u_char *) hash);
andna_hash_by_family(family, (u_char *) hash, ip_hash);
}
/*
* is_hgnode_excluded: it converts the `qg'->gid to an ip, then it searches a
* member of `excluded_hgnode', which has all its gids, from the level `lvl` to
* FAMILY_LVLS, equal to the ip. If it is found 1 is returned, otherwise 0
* will be the return value.
* This function is utilised by find_hash_gnode() to exclude from the search
* the hash_gnodes not wanted.
* All the `excluded_hgnode[x]' which are a null pointer are skipped.
*/
int
is_hgnode_excluded(quadro_group * qg, u_int ** excluded_hgnode,
int tot_excluded_hgnodes, int lvl)
{
int i, e, x, total_levels = FAMILY_LVLS;
inet_prefix ip;
for (e = 0; e < tot_excluded_hgnodes; e++) {
x = 0;
if (!excluded_hgnode[e])
continue;
inet_setip_raw(&ip, excluded_hgnode[e], my_family);
for (i = lvl; i < total_levels; i++) {
#ifdef DEBUG
debug(DBG_INSANE,
"is_hgnode_excluded: l %d, qg->gid %d, ipgid %d", i,
qg->gid[i], iptogid(&ip, i));
#endif
if (qg->gid[i] != iptogid(&ip, i)) {
x = 1;
break;
}
}
if (!x)
return 1;
}
return 0;
}
/*
* is_hgnodeip_excluded: is a wrapper of is_hgnode_excluded() which takes as
* first argv an inet_prefix instead of a quadro_group
*/
int
is_hgnodeip_excluded(inet_prefix * hgnodeip, u_int ** excluded_hgnode,
int tot_excluded_hgnodes)
{
quadro_group qg;
iptoquadg(*hgnodeip, 0, &qg, QUADG_GID);
return is_hgnode_excluded(&qg, excluded_hgnode, tot_excluded_hgnodes,
0);
}
/*
* random_gid_level_0: chooses a random gid of level 0, which is up, and
* stores it in `qg'->gid[0], then convert it to an ip and stores it in `to'.
* If `exclude_me' isn't zero, it won't choose ourself as the gid of level 0.
* If the gid is found 0 is returned otherwise -1 is the return value.
* If the gid found is also a MAP_ME node, 2 is returned.
*/
int
random_gid_level_0(quadro_group * qg, inet_prefix * to, int exclude_me)
{
int x, e, i;
/*
* Set `e' and `i' to a rand value from 0 to MAXGROUPNODE-1.
* In the first for go from `e' to MAXGROUPNODE-1, if nothing is found
* continue in the second for and go from `i' to 0. If nothing is
* found return -1.
*/
for (x = 0, e = i = rand_range(0, MAXGROUPNODE - 1); e < MAXGROUPNODE;
e++) {
if (!(me.int_map[e].flags & MAP_VOID)) {
if (exclude_me && (me.int_map[e].flags & MAP_ME))
continue;
qg->gid[0] = e;
x = 1;
break;
}
}
if (!x)
for (x = 0; i >= 0; i--) {
if (!(me.int_map[i].flags & MAP_VOID)) {
if (exclude_me && (me.int_map[i].flags & MAP_ME))
continue;
qg->gid[0] = i;
x = 1;
break;
}
}
if (x) {
gidtoipstart(qg->gid, me.cur_quadg.levels, me.cur_quadg.levels,
my_family, to);
debug(DBG_NOISE, "find_hashgnode: Internal found: gid0 %d, to "
"%s!", qg->gid[0], inet_to_str(*to));
return me.int_map[qg->gid[0]].flags & MAP_ME ? 2 : 0;
}
return -1;
}
/*
* find_hash_gnode_recurse: it recurse itself to create multiple sub tree. The
* tree is started by find_hash_gnode()
*/
int
find_hash_gnode_recurse(quadro_group qg, int level, inet_prefix * to,
u_int ** excluded_hgnode, int tot_excluded_hgnodes,
int exclude_me)
{
int gid, i, e, steps, err, ret;
map_gnode *gnode;
if (!level)
return random_gid_level_0(&qg, to, exclude_me);
/*
* This is how the ip nearer to `hash' is found:
* - find_hash_gnode() calls, for the first time,
* find_hash_gnode_recurse(). The hash's ip is converted as a
* quadro_group and stored in the argv `qg' and the parameter `level'
* is set to the number of total levels available minus one.
* - if `level' is equal to 0 choose a random gid of level 0 an
* convert `qg' in the inet_prefix format, store it in `to' and
* return.
* loop1:
* - If the gnode `gq'.gid[level] is down increment or decrement
* (alternatively) `gq'.gid[level] and continue the loop1.
* If (it is up) {
* if (`gq'.gid[level] is a gnode where we belong) {
* call recursively find_hash_gnode_recurse,
* giving the new modified `qg' and `level'-1 as
* new parametres.
* If (the return value is not an error) {
* return now with that value because in
* a sub tree, the hash_gnode was already
* found.
* }
* } otherwise {
* return and stor into `to' the ip of the
* border_node that will be used to forward the
* pkt to the hash_gnode.
* }
* }
* - return -1
*/
#if 0 /* TOO NOISY */
debug(DBG_INSANE, "find_hashgnode: lvl %d, start gid %d", level,
qg.gid[level]);
#endif
/* the maximum steps required to complete the for. */
steps =
qg.gid[level] >=
MAXGROUPNODE / 2 ? qg.gid[level] : MAXGROUPNODE - qg.gid[level];
for (i = 0, e = 1, gid = qg.gid[level]; i < steps;
e & 1 ? i++ : i, e = (~(e & 1)) & 1) {
/* `i' is incremented only when `e' is odd, while `e'
* is always alternated between 0 and 1. */
if (!(e & 1) && (qg.gid[level] + i < MAXGROUPNODE))
gid = qg.gid[level] + i;
else if (qg.gid[level] - i >= 0)
gid = qg.gid[level] - i;
else
continue;
gnode = gnode_from_pos(gid, me.ext_map[_EL(level)]);
if (!(gnode->g.flags & MAP_VOID) && !(gnode->flags & GMAP_VOID)) {
qg.gid[level] = gid;
#if 0 /* TOO NOISY */
debug(DBG_NOISE, "find_hashgnode: lvl %d gid %d", level,
qg.gid[level]);
#endif
if (!quadg_gids_cmp(qg, me.cur_quadg, level)) {
/* Is this hash_gnode not wanted ? */
if (excluded_hgnode && level == 1 &&
is_hgnode_excluded(&qg, excluded_hgnode,
tot_excluded_hgnodes, 1))
continue; /* yea, exclude it */
ret =
find_hash_gnode_recurse(qg, level - 1, to,
excluded_hgnode,
tot_excluded_hgnodes,
exclude_me);
if (ret != -1)
/* We have found it in some sub trees. */
return ret;
} else {
/* Check if it is excluded */
if (excluded_hgnode &&
is_hgnode_excluded(&qg, excluded_hgnode,
tot_excluded_hgnodes, level))
continue; /* Yes, it is */
err = get_gw_ips(me.int_map, me.ext_map, me.bnode_map,
me.bmap_nodes, &me.cur_quadg,
gnode, level, 0, to, 0, 1);
debug(DBG_NOISE,
"find_hashgnode: ext_found, err %d, to %s!", err,
inet_to_str(*to));
if (err >= 0)
/*
* Forward the pkt to the found
* border_node, which will forward it
* to the hash_gnode
*/
return 1;
}
}
} /* for(...) */
#if 0 /* TOO NOISY */
debug(DBG_INSANE, "find_hashgnode: Exausted: lvl %d gid %d", level,
qg.gid[level]);
#endif
/* We didn't find anything in the this level, so
* returns from this tree */
return -1;
}
/*
* find_hash_gnode: It stores in `to' the ip of the node nearer to the
* `hash'_gnode and returns 0. If we aren't part of the hash_gnode, it sets in
* `to' the ip of the bnode_gnode that will route the pkt and returns 1.
* If the found hash_gnode is the MAP_ME node itself then 2 is returned.
* If either a hash_gnode and a bnode_gnode aren't found, -1 is returned.
* All the hash_gnodes included in `excluded_hgnode' will be excluded by the
* algorithm.
* If `exclude_me' is set to 1, it will not return ourself as the hash_gnode.
*/
int
find_hash_gnode(u_int hash[MAX_IP_INT], inet_prefix * to,
u_int ** excluded_hgnode, int tot_excluded_hgnodes,
int exclude_me)
{
int total_levels;
quadro_group qg;
total_levels = FAMILY_LVLS;
/* Hash to ip and quadro_group conversion */
inet_setip(to, hash, my_family);
inet_htonl(to->data, to->family);
iptoquadg(*to, me.ext_map, &qg, QUADG_GID | QUADG_GNODE);
return find_hash_gnode_recurse(qg, total_levels - 1, to,
excluded_hgnode, tot_excluded_hgnodes,
exclude_me);
}
/*
*
* * * * Packets * * *
*
*/
/*
* andna_flood_pkt: Sends the `rpkt' pkt to all the rnodes of our same gnode
* and exclude the the rpkt->from node if `exclude_rfrom' is non zero.
* The return value of flood_pkt_send() is returned.
*/
int
andna_flood_pkt(PACKET * rpkt, int exclude_rfrom)
{
PACKET flood_pkt;
int real_from_rpos = -1, ret;
int (*exclude_function) (TRACER_PKT_EXCLUDE_VARS);
pkt_copy(&flood_pkt, rpkt);
flood_pkt.sk = 0;
/* be sure that the other nodes don't reply to rfrom again */
flood_pkt.hdr.flags &= ~ASYNC_REPLY;
flood_pkt.hdr.flags |= BCAST_PKT;
/* If the pkt was sent from an our rnode, ignore it while flooding */
if (exclude_rfrom)
real_from_rpos = ip_to_rfrom(flood_pkt.from, 0, 0, 0);
if (real_from_rpos < 0 || !exclude_rfrom) {
exclude_function = exclude_glevel;
real_from_rpos = -1;
} else
exclude_function = exclude_from_and_glevel;
ret =
flood_pkt_send(exclude_function, 1, -1, real_from_rpos, flood_pkt);
return ret;
}
/*
* andna_find_flood_pkt_id: Search in the `ids_array' a member which is equal
* to `pkt_id', if it is found its array position is returned otherwise -1
* will be the return value
*/
int
andna_find_flood_pkt_id(int *ids_array, int pkt_id)
{
int i;
for (i = 0; i < ANDNA_MAX_FLOODS; i++)
if (ids_array[i] == pkt_id)
return i;
return -1;
}
/*
* andna_add_flood_pkt_id: If the `pkt_id' is already present in the
* `ids_array', 1 is returned, otherwise `pkt_id' is added at the 0 position
* of the array. All the array elements, except the last one, will be
* preserved and shifted of one position like a FIFO.
*/
int
andna_add_flood_pkt_id(int *ids_array, int pkt_id)
{
int i;
if ((i = andna_find_flood_pkt_id(ids_array, pkt_id)) < 0) {
int tmp_array[ANDNA_MAX_FLOODS - 1];
/* Shift the array of one position to free the position 0
* where the new id will be added */
memcpy(tmp_array, ids_array, sizeof(int) * (ANDNA_MAX_FLOODS - 1));
memcpy(&ids_array[1], tmp_array,
sizeof(int) * (ANDNA_MAX_FLOODS - 1));
ids_array[0] = pkt_id;
return 0;
}
return 1;
}
/*
*
* * * * Hostname registration * * *
*
*/
/*
* andna_register_hname
*
* Registers or updates the `alcl->hostname' hostname. It also registers the
* snsd records present in `alcl->service'.
*
* If `snsd_delete' is not null, the registration request becomes a deletion
* request: it will ask to andna to delete all the snsd records equal to
* `snsd_delete'.
*/
int
andna_register_hname(lcl_cache * alcl, snsd_service * snsd_delete)
{
PACKET pkt, rpkt;
struct andna_reg_pkt req;
u_int hash_gnode[MAX_IP_INT];
inet_prefix to;
snsd_service *snsd;
char *sign = 0, *buf;
const char *ntop;
int ret = 0;
ssize_t err, pkt_sz = 0;
time_t cur_t;
setzero(&req, sizeof(req));
setzero(&pkt, sizeof(pkt));
setzero(&rpkt, sizeof(rpkt));
cur_t = time(0);
if (alcl->flags & ANDNA_UPDATING)
/* we are already updating this node! */
return 0;
alcl->flags |= ANDNA_UPDATING;
if (alcl->timestamp) {
if (cur_t > alcl->timestamp &&
(cur_t - alcl->timestamp) < ANDNA_MIN_UPDATE_TIME)
/* We have too wait a little more before sending an
* update */
return -1;
req.flags |= ANDNA_PKT_UPDATE;
req.hname_updates = ++alcl->hname_updates;
}
/* Don't register the hname while we are (re)-hooking,
* our IP might change */
while (me.cur_node->flags & MAP_HNODE)
sleep(1);
/*
* Filling the request structure
*/
inet_copy_ipdata(req.rip, &me.cur_ip);
andna_hash(my_family, alcl->hostname, strlen(alcl->hostname),
req.hash, hash_gnode);
memcpy(req.pubkey, lcl_keyring.pubkey, ANDNA_PKEY_LEN);
/* Convert the pkt from host to network order */
ints_host_to_network((void *) &req, andna_reg_pkt_iinfo);
/* Sign the packet */
sign = (char *) rsa_sign((u_char *) & req, ANDNA_REG_SIGNED_BLOCK_SZ,
lcl_keyring.priv_rsa, 0);
memcpy(req.sign, sign, ANDNA_SIGNATURE_LEN);
/* Find the hash_gnode that corresponds to the hash `hash_gnode' */
if ((err = find_hash_gnode(hash_gnode, &to, 0, 0, 1)) < 0) {
debug(DBG_SOFT, "andna_register_hname: hash_gnode not found ;(");
ERROR_FINISH(ret, -1, finish);
} else if (err == 1)
req.flags |= ANDNA_PKT_FORWARD;
if (snsd_delete) {
snsd = snsd_delete;
req.flags |= ANDNA_PKT_SNSD_DEL;
} else
snsd = alcl->service;
ntop = inet_to_str(to);
debug(DBG_INSANE, "Quest %s to %s", rq_to_str(ANDNA_REGISTER_HNAME),
ntop);
/* Fill the packet and send the request */
pkt_sz = ANDNA_REG_PKT_SZ + SNSD_SERVICE_LLIST_PACK_SZ(alcl->service);
pkt_addto(&pkt, &to);
pkt_addcompress(&pkt);
pkt_fill_hdr(&pkt.hdr, ASYNC_REPLY, 0, ANDNA_REGISTER_HNAME, pkt_sz);
pkt.msg = buf = xmalloc(pkt_sz);
memcpy(pkt.msg, &req, ANDNA_REG_PKT_SZ);
buf += ANDNA_REG_PKT_SZ;
if (snsd_pack_all_services(buf, pkt_sz - ANDNA_REG_PKT_SZ, snsd) < 0) {
debug(DBG_SOFT, "andna_register_hname: couldn't pack "
"alcl->service");
ERROR_FINISH(ret, -1, finish);
}
err =
send_rq(&pkt, 0, ANDNA_REGISTER_HNAME, 0, ACK_AFFERMATIVE, 1,
&rpkt);
if (err < 0) {
error("andna_register_hname(): Registration of \"%s\" to %s "
"failed.", alcl->hostname, ntop);
ERROR_FINISH(ret, -1, finish);
}
/* Ehi, we've registered it! Update the hname timestamp */
alcl->timestamp = cur_t;
finish:
alcl->flags &= ~ANDNA_UPDATING;
if (sign)
xfree(sign);
pkt_free(&pkt, 1);
pkt_free(&rpkt, 0);
return ret;
}
/*
* andna_recv_reg_rq
*
* It takes care of a registration request. If we aren't
* the rightful hash_gnode, we forward the request (again), otherwise the
* request is examined. If it is valid the hostname is registered or updated
* in the andna_cache.
*/
int
andna_recv_reg_rq(PACKET rpkt)
{
PACKET pkt, rpkt_local_copy;
struct andna_reg_pkt *req;
u_int hash_gnode[MAX_IP_INT], *excluded_hgnode[1];
inet_prefix rfrom, to;
RSA *pubkey = 0;
andna_cache_queue *acq = 0;
andna_cache *ac = 0;
snsd_service *snsd_unpacked = 0;
snsd_node *snd = 0;
time_t cur_t;
u_short snsd_counter;
int ret = 0, err;
size_t unpacked_sz, packed_sz;
char *ntop = 0, *rfrom_ntop = 0, *snsd_pack;
u_char forwarded_pkt = 0;
const u_char *pk;
pkt_copy(&rpkt_local_copy, &rpkt);
req = (struct andna_reg_pkt *) rpkt_local_copy.msg;
if (rpkt.hdr.sz < ANDNA_REG_PKT_SZ ||
rpkt.hdr.sz > SNSD_SERVICE_MAX_LLIST_PACK_SZ)
ERROR_FINISH(ret, -1, finish);
packed_sz = rpkt.hdr.sz - ANDNA_REG_PKT_SZ;
snsd_pack = rpkt_local_copy.msg + ANDNA_REG_PKT_SZ;
if (rpkt.hdr.flags & BCAST_PKT)
/* The pkt we received has been only forwarded to us */
forwarded_pkt = 1;
/* Check if we already received this pkt during the flood */
if (forwarded_pkt &&
andna_add_flood_pkt_id(last_reg_pkt_id, rpkt.hdr.id)) {
debug(DBG_INSANE, "Dropped 0x%0x andna pkt, we already "
"received it", rpkt.hdr.id);
ERROR_FINISH(ret, 0, finish);
}
/* Save the real sender of the request */
inet_setip(&rfrom, req->rip, my_family);
ntop = xstrdup(inet_to_str(rpkt.from));
rfrom_ntop = xstrdup(inet_to_str(rfrom));
debug(DBG_SOFT, "Received %s%sfrom %s, rfrom %s",
rq_to_str(rpkt.hdr.op), forwarded_pkt ? " (forwarded) " : " ",
ntop, rfrom_ntop);
memcpy(&pkt, &rpkt, sizeof(PACKET));
inet_copy(&pkt.from, &rfrom);
/* Send the replies in UDP, they are so tiny */
pkt_addsk(&pkt, my_family, 0, SKT_UDP);
/* Verify the signature */
pk = (const u_char *) req->pubkey;
pubkey = get_rsa_pub(&pk, ANDNA_PKEY_LEN);
if (!pubkey || !verify_sign((u_char *) req, ANDNA_REG_SIGNED_BLOCK_SZ,
(u_char *) req->sign, ANDNA_SIGNATURE_LEN,
pubkey)) {
/* Bad, bad signature */
debug(DBG_SOFT, "Invalid signature of the 0x%x reg request",
rpkt.hdr.id);
if (!forwarded_pkt)
pkt_err(pkt, E_INVALID_SIGNATURE, 0);
ERROR_FINISH(ret, -1, finish);
}
/* Revert the packet from network to host order */
ints_network_to_host((void *) req, andna_reg_pkt_iinfo);
/* If we don't belong to the gnode of `rfrom', then we have to exclude
* it from the find_hash_gnode search, since we have received the
* pkt from that gnode and it is likely that `rfrom' is the only node
* of that gnode.
*/
excluded_hgnode[0] = ip_gids_cmp(rfrom, me.cur_ip, 1) ? rfrom.data : 0;
/*
* Are we the hash_gnode for req->hash? Check also if we have to
* continue to forward the pkt to make it reach the real hash_gnode
*/
andna_hash_by_family(my_family, (u_char *) req->hash, hash_gnode);
if ((err =
find_hash_gnode(hash_gnode, &to, excluded_hgnode, 1, 0)) < 0) {
debug(DBG_SOFT,
"We are not the right (rounded)hash_gnode. "
"Rejecting the 0x%x reg request", rpkt.hdr.id);
if (!forwarded_pkt)
pkt_err(pkt, E_ANDNA_WRONG_HASH_GNODE, 0);
ERROR_FINISH(ret, -1, finish);
} else if (err == 1) {
if (!(req->flags & ANDNA_PKT_FORWARD)) {
if (!forwarded_pkt)
pkt_err(pkt, E_ANDNA_WRONG_HASH_GNODE, 0);
ERROR_FINISH(ret, -1, finish);
}
/* Continue to forward the received pkt */
debug(DBG_SOFT, "The reg request pkt will be forwarded to: %s",
inet_to_str(to));
ret = forward_pkt(rpkt, to);
goto finish;
}
/* Are we a new hash_gnode ? */
if (time(0) - me.uptime < (ANDNA_EXPIRATION_TIME / 3) &&
!(ac = andna_cache_findhash((int *) req->hash))) {
/*
* We are a new hash_gnode and we haven't this hostname in our
* andna_cache, so we have to check if there is an
* old hash_gnode which has already registered this hostname.
*/
if ((ac = get_single_andna_c(req->hash, hash_gnode))) {
/*
* The hostname was already registered, so we save it
* in our andna_cache.
*/
clist_add(&andna_c, &andna_c_counter, ac);
/* Spread it in our gnode */
spread_single_acache(req->hash);
}
}
/* Ask the counter_node if it is ok to register/update the hname */
if (!(req->flags & ANDNA_PKT_SNSD_DEL)) {
if (andna_check_counter(rpkt) == -1) {
debug(DBG_SOFT, "Registration rq 0x%x rejected: %s",
rpkt.hdr.id, rq_strerror(E_ANDNA_CHECK_COUNTER));
if (!forwarded_pkt)
pkt_err(pkt, E_ANDNA_CHECK_COUNTER, 0);
ERROR_FINISH(ret, -1, finish);
}
}
/***
* Finally, let's register/update the hname
*/
cur_t = time(0);
ac = andna_cache_addhash((int *) req->hash);
acq = ac_queue_add(ac, req->pubkey);
if (!acq) {
debug(DBG_SOFT, "Registration rq 0x%x rejected: %s",
rpkt.hdr.id, rq_strerror(E_ANDNA_QUEUE_FULL));
if (!forwarded_pkt)
pkt_err(pkt, E_ANDNA_QUEUE_FULL, 0);
ERROR_FINISH(ret, -1, finish);
}
/***/
/**
* Check if the counter of number of updates matches with the pkt */
if (acq->hname_updates > req->hname_updates) {
debug(DBG_SOFT, "Registration rq 0x%x rejected: hname_updates"
" mismatch %d > %d", rpkt.hdr.id,
acq->hname_updates, req->hname_updates);
if (!forwarded_pkt)
pkt_err(pkt, E_ANDNA_HUPDATE_MISMATCH, 0);
ERROR_FINISH(ret, -1, finish);
}
/**/
/**
* Has the registration request been sent too early ? */
if (cur_t > acq->timestamp &&
(cur_t - acq->timestamp) < ANDNA_MIN_UPDATE_TIME) {
debug(DBG_SOFT, "Registration rq 0x%x rejected: %s",
rpkt.hdr.id, rq_strerror(E_ANDNA_UPDATE_TOO_EARLY));
if (!forwarded_pkt)
pkt_err(pkt, E_ANDNA_UPDATE_TOO_EARLY, 0);
ERROR_FINISH(ret, -1, finish);
}
/**/
/*
* SNSD services registration
*/
if (acq != ac->acq) {
/*
* This request has been queued in the acq. We store the main
* ip and discard the rest of the snsd records
*/
snsd_add_mainip(&snsd_unpacked, &snsd_counter,
SNSD_MAX_QUEUE_RECORDS, rfrom.data);
} else {
/*
* Register the snsd records
*/
/* unpack the snsd services */
snsd_unpacked = snsd_unpack_all_service(snsd_pack, packed_sz,
&unpacked_sz,
&snsd_counter);
if ((!snsd_unpacked && req->flags & ANDNA_PKT_SNSD_DEL) ||
(snsd_counter > SNSD_MAX_RECORDS - 1)) {
debug(DBG_SOFT,
"Registration rq 0x%x rejected: couldn't unpack"
" the snsd llist", rpkt.hdr.id);
if (!forwarded_pkt)
pkt_err(pkt, E_INVALID_REQUEST, 0);
ERROR_FINISH(ret, -1, finish);
}
if (req->flags & ANDNA_PKT_SNSD_DEL) {
/*
* Delete, from the andna_cache, all the snsd records
* which are equal to those included in the
* registration pkt.
*/
snsd_record_del_selected(&acq->service, &acq->snsd_counter,
snsd_unpacked);
} else {
/*
* Register the snsd services in our andna_cache
*/
/* Add the mainip and update its IP */
snd = snsd_add_mainip(&snsd_unpacked, &snsd_counter,
SNSD_MAX_RECORDS, rfrom.data);
inet_copy_ipdata_raw(snd->record, &rfrom);
/**
* Set the mainip flag only to the real mainip record,
* in this way we're sure that there is only one. */
snsd_unset_all_flags(snsd_unpacked, SNSD_NODE_MAIN_IP);
snd->flags |= SNSD_NODE_MAIN_IP;
/**/}
}
/*
* substitute the old snsd records with the new ones
*/
snsd_service_llist_del(&acq->service);
acq->service = snsd_unpacked;
acq->snsd_counter = snsd_counter;
/*
* Final registration step: touch the hname timestamp
*/
acq->hname_updates = req->hname_updates + 1;
acq->timestamp = cur_t;
/* Reply to the requester: <<Yes, don't worry, it worked.>> */
if (!forwarded_pkt) {
debug(DBG_SOFT, "Registration rq 0x%x accepted.", rpkt.hdr.id);
pkt.msg = 0;
pkt_fill_hdr(&pkt.hdr, ASYNC_REPLIED, rpkt.hdr.id, ACK_AFFERMATIVE,
0);
ret = forward_pkt(pkt, rfrom);
}
/**
* Broadcast the request to the entire gnode of level 1 to let the
* other nodes register the hname.
*/
if (!forwarded_pkt)
andna_add_flood_pkt_id(last_reg_pkt_id, rpkt.hdr.id);
andna_flood_pkt(&rpkt, 1);
/**/ finish:
if (ntop)
xfree(ntop);
if (rfrom_ntop)
xfree(rfrom_ntop);
if (pubkey)
RSA_free(pubkey);
pkt_free(&rpkt_local_copy, 0);
return ret;
}
/*
* andna_check_counter
*
* asks the counter_node if it is ok to accept the registration request in
* `pkt' and register the requested hname.
*
* If -1 is returned the answer is no.
*/
int
andna_check_counter(PACKET pkt)
{
PACKET rpkt;
int ret = 0, err;
u_int rip_hash[MAX_IP_INT], hash_gnode[MAX_IP_INT];
struct andna_reg_pkt *req;
const char *ntop;
u_char forwarded_pkt = 0;
setzero(&rpkt, sizeof(PACKET));
req = (struct andna_reg_pkt *) pkt.msg;
pkt.msg = 0;
if (pkt.hdr.flags & BCAST_PKT)
forwarded_pkt = 1;
/* Calculate the hash of the IP of the sender node. This hash will
* be used to reach its counter node. */
andna_hash(my_family, req->rip, MAX_IP_SZ, rip_hash, hash_gnode);
/* Find a hash_gnode for the rip_hash */
req->flags &= ~ANDNA_PKT_FORWARD;
if ((err = find_hash_gnode(hash_gnode, &pkt.to, 0, 0, 1)) < 0) {
debug(DBG_INSANE, "andna_check_counter: Couldn't find a decent"
" counter_gnode");
ERROR_FINISH(ret, -1, finish);
} else if (err == 1)
req->flags |= ANDNA_PKT_FORWARD;
ntop = inet_to_str(pkt.to);
debug(DBG_INSANE, "Quest %s to %s", rq_to_str(ANDNA_CHECK_COUNTER),
ntop);
pkt.hdr.flags |= ASYNC_REPLY;
/* Append our ip in the pkt */
pkt.hdr.sz = ANDNA_REG_PKT_SZ + MAX_IP_SZ;
pkt.msg = xmalloc(pkt.hdr.sz);
memcpy(pkt.msg, req, ANDNA_REG_PKT_SZ);
inet_copy_ipdata((u_int *) (pkt.msg + ANDNA_REG_PKT_SZ), &me.cur_ip);
/* If we are checking a registration pkt which has been only
* forwarded to us (and already registered), we tell the counter_node
* to just check the request. We don't want it to update its
* hname_updates counter */
if (forwarded_pkt) {
req = (struct andna_reg_pkt *) pkt.msg;
req->flags |= ANDNA_PKT_JUST_CHECK;
/* Adjust the flags */
pkt.hdr.flags &= ~BCAST_PKT;
}
/* Throw it */
pkt.sk = 0; /* Create a new connection */
ret =
send_rq(&pkt, 0, ANDNA_CHECK_COUNTER, 0, ACK_AFFERMATIVE, 1,
&rpkt);
ret = (ret < 0) ? -1 : ret;
finish:
pkt_free(&pkt, 1);
pkt_free(&rpkt, 0);
return ret;
}
int
andna_recv_check_counter(PACKET rpkt)
{
PACKET pkt, rpkt_local_copy;
struct andna_reg_pkt *req;
inet_prefix rfrom, to;
RSA *pubkey = 0;
counter_c *cc;
counter_c_hashes *cch;
u_int rip_hash[MAX_IP_INT], hash_gnode[MAX_IP_INT],
*excluded_hgnode[1];
int ret = 0, err, old_updates;
char *ntop = 0, *rfrom_ntop = 0, *buf;
u_char forwarded_pkt = 0, just_check = 0;
const u_char *pk;
pkt_copy(&rpkt_local_copy, &rpkt);
ntop = xstrdup(inet_to_str(rpkt.from));
req = (struct andna_reg_pkt *) rpkt_local_copy.msg;
if (rpkt.hdr.sz != ANDNA_REG_PKT_SZ + MAX_IP_SZ) {
debug(DBG_SOFT,
ERROR_MSG "Malformed check_counter pkt from %s: %d != %d",
ERROR_FUNC, ntop, rpkt.hdr.sz, ANDNA_REG_PKT_SZ + MAX_IP_SZ);
ERROR_FINISH(ret, -1, finish);
}
if (rpkt.hdr.flags & BCAST_PKT)
/* The pkt we received has been only forwarded to us */
forwarded_pkt = 1;
if (req->flags & ANDNA_PKT_JUST_CHECK)
just_check = 1;
/* Check if we already received this pkt during the flood */
if (forwarded_pkt &&
andna_add_flood_pkt_id(last_counter_pkt_id, rpkt.hdr.id)) {
debug(DBG_INSANE, "Dropped 0x%0x andna pkt, we already "
"received it", rpkt.hdr.id);
ERROR_FINISH(ret, 0, finish);
}
/* Save the real sender of the request */
buf = rpkt.msg + ANDNA_REG_PKT_SZ;
inet_setip(&rfrom, (u_int *) buf, my_family);
rfrom_ntop = xstrdup(inet_to_str(rfrom));
debug(DBG_SOFT, "Received %s%sfrom %s, rfrom %s",
rq_to_str(rpkt.hdr.op), forwarded_pkt ? " (forwarded) " : " ",
ntop, rfrom_ntop);
memcpy(&pkt, &rpkt, sizeof(PACKET));
inet_copy(&pkt.from, &rfrom);
/* Reply to rfrom using a UDP sk, since the replies are very small */
pkt_addsk(&pkt, my_family, 0, SKT_UDP);
/* Verify the signature */
pk = (const u_char *) req->pubkey;
pubkey = get_rsa_pub(&pk, ANDNA_PKEY_LEN);
if (!pubkey || !verify_sign((u_char *) req, ANDNA_REG_SIGNED_BLOCK_SZ,
(u_char *) req->sign, ANDNA_SIGNATURE_LEN,
pubkey)) {
/* Bad signature */
debug(DBG_SOFT, "Invalid signature of the 0x%x check "
"counter request", rpkt.hdr.id);
if (!forwarded_pkt)
pkt_err(pkt, E_INVALID_SIGNATURE, 0);
ERROR_FINISH(ret, -1, finish);
}
/* Revert the packet from network to host order */
ints_network_to_host((void *) req, andna_reg_pkt_iinfo);
/* If don't belong to the gnode of `rfrom', then we have to exclude
* it from the find_hash_gnode search, since we have received the
* pkt from that gnode and it is likely that `rfrom' is the only node
* of that gnode.
*/
excluded_hgnode[0] = ip_gids_cmp(rfrom, me.cur_ip, 1) ? rfrom.data : 0;
debug(DBG_INSANE, "excluded_hgnode: %x ", excluded_hgnode[0]);
/*
* Check if we are the real counter node or if we have to continue to
* forward the pkt
*/
andna_hash(my_family, req->rip, MAX_IP_SZ, rip_hash, hash_gnode);
if ((err =
find_hash_gnode(hash_gnode, &to, excluded_hgnode, 1, 0)) < 0) {
debug(DBG_SOFT,
"We are not the real (rounded)hash_gnode. "
"Rejecting the 0x%x check_counter request", rpkt.hdr.id);
if (!forwarded_pkt)
pkt_err(pkt, E_ANDNA_WRONG_HASH_GNODE, 0);
ERROR_FINISH(ret, -1, finish);
} else if (err == 1) {
if (!(req->flags & ANDNA_PKT_FORWARD)) {
if (!forwarded_pkt)
pkt_err(pkt, E_ANDNA_WRONG_HASH_GNODE, 0);
ERROR_FINISH(ret, -1, finish);
}
/* Continue to forward the received pkt */
debug(DBG_SOFT, "The check_counter rq pkt will be forwarded "
"to: %s", inet_to_str(to));
ret = forward_pkt(rpkt, to);
goto finish;
}
/* Finally, let's register/update the hname */
cc = counter_c_add(&rfrom, req->pubkey);
if (!just_check)
cch = cc_hashes_add(cc, (int *) req->hash);
else
cch = cc_findhash(cc, (int *) req->hash);
if (!cch) {
debug(DBG_SOFT, "Request %s (0x%x) rejected: %s",
rq_to_str(rpkt.hdr.op), rpkt.hdr.id,
rq_strerror(E_ANDNA_TOO_MANY_HNAME));
if (!forwarded_pkt)
pkt_err(pkt, E_ANDNA_TOO_MANY_HNAME, 0);
ERROR_FINISH(ret, -1, finish);
}
old_updates = cch->hname_updates;
old_updates -= ! !just_check;
if (old_updates > req->hname_updates) {
debug(DBG_SOFT, "Request %s (0x%x) rejected: hname_updates"
" mismatch %d > %d", rq_to_str(rpkt.hdr.op), rpkt.hdr.id,
old_updates, req->hname_updates);
if (!forwarded_pkt)
pkt_err(pkt, E_ANDNA_HUPDATE_MISMATCH, 0);
ERROR_FINISH(ret, -1, finish);
} else if (!just_check) {
/* Touch the hname */
cch->hname_updates = req->hname_updates + 1;
cch->timestamp = time(0);
}
/* Report the successful result to rfrom */
if (!forwarded_pkt || just_check) {
debug(DBG_SOFT, "Check_counter rq 0x%x accepted.", rpkt.hdr.id);
pkt_fill_hdr(&pkt.hdr, ASYNC_REPLIED, rpkt.hdr.id,
ACK_AFFERMATIVE, 0);
pkt.msg = 0;
ret = forward_pkt(pkt, rfrom);
}
/*
* Broadcast the request to the entire gnode of level 1 to let the
* other counter_nodes register the hname.
*/
if (!just_check) {
if (!forwarded_pkt)
andna_add_flood_pkt_id(last_counter_pkt_id, rpkt.hdr.id);
andna_flood_pkt(&rpkt, forwarded_pkt);
}
finish:
if (ntop)
xfree(ntop);
if (rfrom_ntop)
xfree(rfrom_ntop);
if (pubkey)
RSA_free(pubkey);
pkt_free(&rpkt_local_copy, 0);
return ret;
}
/*
*
* * * * Hostname/IP resolution * * *
*
*/
/*
* andna_resolve_hash_locally
*
* It tries to resolve the given md5 hname-hash by searching in the
* local andna caches.
* It uses the same arguments of `andna_resolve_hash' see below).
*/
snsd_service *
andna_resolve_hash_locally(u_int hname_hash[MAX_IP_INT], int service,
u_char proto, int *records)
{
struct andna_resolve_rq_pkt req;
lcl_cache *lcl;
rh_cache *rhc;
andna_cache *ac;
snsd_service *ret;
u_int hash;
setzero(&req, sizeof(req));
hash = fnv_32_buf(hname_hash, ANDNA_HASH_SZ, FNV1_32_INIT);
#ifndef ANDNA_DEBUG
/*
* Search the hostname in the local cache first. Maybe we are so
* dumb that we are trying to resolve the same ip we registered.
*/
if ((lcl = lcl_cache_find_hash(andna_lcl, hash))) {
u_short fake_counter = 0;
*records = lcl->snsd_counter;
ret = snsd_service_llist_copy(lcl->service, service, proto);
/* Add our current main ip */
if (service == SNSD_ALL_SERVICE ||
service == SNSD_DEFAULT_SERVICE || !ret)
snsd_add_mainip(&ret, &fake_counter,
SNSD_MAX_RECORDS, me.cur_ip.data);
return ret;
}
/*
* Last try before asking to ANDNA: let's see if we have it in
* the resolved_hnames cache
*/
if ((rhc = rh_cache_find_hash(hash))) {
*records = rhc->snsd_counter;
ret = snsd_service_llist_copy(rhc->service, service, proto);
if (!ret && (service != SNSD_ALL_SERVICE) &&
(service != SNSD_DEFAULT_SERVICE))
/* The specific service hasn't been found, fallback to
* SNSD_DEFAULT_SERVICE */
ret = snsd_service_llist_copy(rhc->service,
SNSD_DEFAULT_SERVICE, 0);
if (ret)
return ret;
}
#endif
/*
* If we manage an andna_cache, it's better to peek at it.
*/
if ((ac = andna_cache_gethash((int *) hname_hash))) {
*records = ac->acq->snsd_counter;
ret = snsd_service_llist_copy(ac->acq->service, service, proto);
if (!ret && (service != SNSD_ALL_SERVICE) &&
(service != SNSD_DEFAULT_SERVICE))
/* The specific service hasn't been found, fallback to
* SNSD_DEFAULT_SERVICE */
ret = snsd_service_llist_copy(ac->acq->service,
SNSD_DEFAULT_SERVICE, 0);
if (ret)
return ret;
}
return 0;
}
/*
* andna_resolve_hash
*
* It returns a snsd_service llist (see snsd.h) which contains the snsd
* records of the resolved hostname. Among them there's at least the mainip
* record which can be found using snsd_find_mainip().
*
* `hname_hash' is the full MD5 hash of the hostname we want to resolve.
*
* `service' specifies the service number of the resolution. If it is equal to
* -1 the resolution will return all the registered snds records.
*
* `proto' is the protocol of the `service', it must be specified in the
* proto_to_8bit() format.
*
* In `*records' the number of records stored in the returned snsd_service
* llist is written.
*
* It returns 0 on error
*/
snsd_service *
andna_resolve_hash(u_int hname_hash[MAX_IP_INT], int service,
u_char proto, int *records)
{
PACKET pkt, rpkt;
struct andna_resolve_rq_pkt req;
struct andna_resolve_reply_pkt *reply;
rh_cache *rhc;
snsd_service *sns_dup;
u_int hash_gnode[MAX_IP_INT], hash32;
inet_prefix to;
snsd_service *sns, *snsd_unpacked, *ret = 0;
const char *ntop;
char *snsd_packed;
u_short snsd_counter = 0;
size_t packed_sz, unpacked_sz;
ssize_t err;
*records = 0;
setzero(&req, sizeof(req));
setzero(&pkt, sizeof(pkt));
setzero(&rpkt, sizeof(pkt));
hash32 =
fnv_32_buf((u_char *) hname_hash, ANDNA_HASH_SZ, FNV1_32_INIT);
/* Try to resolve the hostname locally */
sns = andna_resolve_hash_locally(hname_hash, service, proto, records);
if (sns)
return sns;
/*
* Fill the request structure.
*/
req.service = service;
req.proto = proto;
inet_copy_ipdata(req.rip, &me.cur_ip);
memcpy(req.hash, hname_hash, ANDNA_HASH_SZ);
andna_hash_by_family(my_family, hname_hash, hash_gnode);
/*
* Ok, we have to ask to someone for the resolution.
* Let's see to whom we have to send the pkt
*/
if ((err = find_hash_gnode(hash_gnode, &to, 0, 0, 1)) < 0)
ERROR_FINISH(ret, 0, finish);
else if (err == 1)
req.flags |= ANDNA_PKT_FORWARD;
ntop = inet_to_str(to);
debug(DBG_INSANE, "Quest %s to %s", rq_to_str(ANDNA_RESOLVE_HNAME),
ntop);
/*
* Fill the packet and send the request
*/
/* host -> network order */
ints_host_to_network(&req, andna_resolve_rq_pkt_iinfo);
pkt_addto(&pkt, &to);
pkt.pkt_flags |= PKT_SET_LOWDELAY;
pkt.hdr.flags |= ASYNC_REPLY;
pkt.hdr.sz = ANDNA_RESOLVE_RQ_PKT_SZ;
pkt.msg = xmalloc(pkt.hdr.sz);
memcpy(pkt.msg, &req, pkt.hdr.sz);
setzero(&rpkt, sizeof(PACKET));
err =
send_rq(&pkt, 0, ANDNA_RESOLVE_HNAME, 0, ANDNA_RESOLVE_REPLY, 1,
&rpkt);
if (err < 0) {
debug(DBG_NORMAL, ERROR_MSG "Resolution of 0x%x failed.",
ERROR_FUNC, pkt.hdr.id);
ERROR_FINISH(ret, 0, finish);
}
if (rpkt.hdr.sz <= ANDNA_RESOLVE_REPLY_PKT_SZ ||
rpkt.hdr.sz > (SNSD_SERVICE_MAX_PACK_SZ +
ANDNA_RESOLVE_REPLY_PKT_SZ))
ERROR_FINISH(ret, 0, finish);
/*
* Take the ip we need from the replied pkt
*/
reply = (struct andna_resolve_reply_pkt *) rpkt.msg;
/* network -> host order */
ints_network_to_host(reply, andna_resolve_reply_pkt_iinfo);
/* Unpack the received snsd records */
snsd_packed = rpkt.msg + sizeof(struct andna_resolve_reply_pkt);
packed_sz = rpkt.hdr.sz - sizeof(struct andna_resolve_reply_pkt);
if (service == -1)
snsd_unpacked = snsd_unpack_all_service(snsd_packed, packed_sz,
&unpacked_sz,
&snsd_counter);
else
snsd_unpacked = snsd_unpack_service(snsd_packed, packed_sz,
&unpacked_sz, &snsd_counter);
if (!snsd_unpacked ||
((service == SNSD_ALL_SERVICE ||
service == SNSD_DEFAULT_SERVICE) &&
!snsd_find_mainip(snsd_unpacked))) {
debug(DBG_SOFT, ERROR_MSG "Malformed resolution reply (0x%x)",
ERROR_FUNC, rpkt.hdr.id);
ERROR_FINISH(ret, 0, finish);
}
*records = snsd_counter;
ret = snsd_unpacked;
/*
* Add the hostname in the resolved_hnames cache since it was
* successful resolved ;)
*/
reply->timestamp = time(0) - reply->timestamp;
rhc = rh_cache_add_hash(hash32, reply->timestamp);
sns_dup = snsd_service_llist_copy(snsd_unpacked, SNSD_ALL_SERVICE, 0);
snsd_service_llist_merge(&rhc->service, &rhc->snsd_counter, sns_dup);
finish:
pkt_free(&pkt, 1);
pkt_free(&rpkt, 0);
return ret;
}
/*
* andna_resolve_hname
*
* It is a wrapper to andna_resolve_hash() (see above): it hashes the given
* `hname' string and calls andna_resolve_hash.
*
* It returns 0 on error
*/
snsd_service *
andna_resolve_hname(char *hname, int service, u_char proto, int *records)
{
u_int hname_hash[MAX_IP_INT];
hash_md5((u_char *) hname, strlen(hname), (u_char *) hname_hash);
return andna_resolve_hash(hname_hash, service, proto, records);
}
/*
* andna_recv_resolve_rq
*
* replies to a hostname resolve request by giving the snsd records associated
* to the hostname.
*/
int
andna_recv_resolve_rq(PACKET rpkt)
{
PACKET pkt, rpkt_local_copy;
struct andna_resolve_rq_pkt *req;
struct andna_resolve_reply_pkt reply;
andna_cache *ac;
snsd_service *sns;
u_int hash_gnode[MAX_IP_INT];
inet_prefix rfrom, to;
u_short service;
size_t pack_sz;
int ret = 0, err;
char *ntop = 0, *rfrom_ntop = 0, *buf;
u_char spread_the_acache = 0;
if (rpkt.hdr.sz != ANDNA_RESOLVE_RQ_PKT_SZ)
ERROR_FINISH(ret, -1, finish);
pkt_copy(&rpkt_local_copy, &rpkt);
req = (struct andna_resolve_rq_pkt *) rpkt_local_copy.msg;
/* Save the real sender of the request */
inet_setip(&rfrom, req->rip, my_family);
ntop = xstrdup(inet_to_str(rpkt.from));
rfrom_ntop = xstrdup(inet_to_str(rfrom));
debug(DBG_NOISE, "Andna Resolve request 0x%x from: %s, real from: %s",
rpkt.hdr.id, ntop, rfrom_ntop);
memcpy(&pkt, &rpkt, sizeof(PACKET));
inet_copy(&pkt.from, &rfrom);
pkt_addsk(&pkt, my_family, 0, SKT_UDP);
pkt_addcompress(&pkt);
pkt_addlowdelay(&pkt);
pkt_addlowdelay(&rpkt);
/* network -> host order conversion of the rpkt_local_copy.smg */
ints_network_to_host((void *) req, andna_resolve_rq_pkt_iinfo);
/*
* Are we the right hash_gnode or have we to still forward the pkt ?
*/
andna_hash_by_family(my_family, (u_char *) req->hash, hash_gnode);
if ((err = find_hash_gnode(hash_gnode, &to, 0, 0, 0)) < 0) {
debug(DBG_SOFT, "We are not the real (rounded)hash_gnode. "
"Rejecting the 0x%x resolve_hname request", rpkt.hdr.id);
pkt_err(pkt, E_ANDNA_WRONG_HASH_GNODE, 0);
ERROR_FINISH(ret, -1, finish);
} else if (err == 1) {
if (!(req->flags & ANDNA_PKT_FORWARD)) {
pkt_err(pkt, E_ANDNA_WRONG_HASH_GNODE, 0);
ERROR_FINISH(ret, -1, finish);
}
/* Continue to forward the received pkt */
debug(DBG_SOFT, "The resolve_hame rq pkt will be forwarded "
"to: %s", inet_to_str(to));
ret = forward_pkt(rpkt, to);
goto finish;
}
/*
* Search the hostname to resolve in the andna_cache
*/
if (!(ac = andna_cache_gethash((int *) req->hash))) {
/* We don't have that hname in our andna_cache */
if (time(0) - me.uptime < (ANDNA_EXPIRATION_TIME / 2)) {
/*
* We are a new hash_gnode, let's see if there is
* an old hash_gnode which has this hostname.
*/
if ((ac = get_single_andna_c(req->hash, hash_gnode))) {
/*
* We got the andna_cache from the old
* hash_gnode. Save it in our andna_cache, then
* reply to `rfrom' and diffuse it in our gnode
*/
clist_add(&andna_c, &andna_c_counter, ac);
spread_the_acache = 1;
goto reply_resolve_rq;
}
}
/* Nothing to do, there isn't it, bye. */
debug(DBG_SOFT, "Request %s (0x%x) rejected: %s",
rq_to_str(rpkt.hdr.op), rpkt.hdr.id,
rq_strerror(E_ANDNA_NO_HNAME));
pkt_err(pkt, E_ANDNA_NO_HNAME, 0);
ERROR_FINISH(ret, -1, finish);
}
reply_resolve_rq:
/*
* Send back the ip associated to the hname
*/
debug(DBG_SOFT, "Resolve request 0x%x accepted", rpkt.hdr.id);
/* Write the reply */
setzero(&reply, sizeof(reply));
reply.timestamp = time(0) - ac->acq->timestamp;
/* host -> network order */
ints_host_to_network((void *) &reply, andna_resolve_reply_pkt_iinfo);
pack_sz = sizeof(reply);
pack_sz += req->service == SNSD_ALL_SERVICE ?
SNSD_SERVICE_LLIST_PACK_SZ(ac->acq->service) :
SNSD_SERVICE_SINGLE_PACK_SZ(ac->acq->service);
pkt_fill_hdr(&pkt.hdr, ASYNC_REPLIED, rpkt.hdr.id, ANDNA_RESOLVE_REPLY,
pack_sz);
pkt.msg = buf = xmalloc(pkt.hdr.sz);
memcpy(pkt.msg, &reply, sizeof(reply));
buf += sizeof(reply);
pack_sz -= sizeof(reply);
if (req->service == SNSD_ALL_SERVICE)
/* Pack all the registered snsd records */
ret = snsd_pack_all_services(buf, pack_sz, ac->acq->service);
else {
/* Pack the snsd records of the specified service number */
service = (u_short) req->service;
sns = snsd_find_service(ac->acq->service, service, req->proto);
if (!sns && service != SNSD_DEFAULT_SERVICE) {
/*
* The specified service and proto record hasn't been
* found, fallback to SNSD_DEFAULT_SERVICE
*/
sns = snsd_find_service(ac->acq->service,
SNSD_DEFAULT_SERVICE, 0);
}
/* pack what we found */
ret = snsd_pack_service(buf, pack_sz, sns);
}
if (ret < 0) {
debug(DBG_NORMAL, "Cannot pack the services for the 0x%x "
"resolve request", rpkt.hdr.id);
goto finish;
}
/* Forward it */
ret = forward_pkt(pkt, rfrom);
pkt_free(&pkt, 0);
if (spread_the_acache) {
/* Spread the received andna_cache in our gnode */
spread_single_acache(req->hash);
}
finish:
if (ntop)
xfree(ntop);
if (rfrom_ntop)
xfree(rfrom_ntop);
pkt_free(&rpkt_local_copy, 0);
return ret;
}
/*
* andna_reverse_resolve
*
* It returns the copy of the local cache of a remote node.
* The ip of the node is specified in `ip'.
*
* On error 0 is returned.
*/
lcl_cache *
andna_reverse_resolve(inet_prefix ip)
{
PACKET pkt, rpkt;
lcl_cache *unpacked_lcl = 0, *ret = 0;
inet_prefix to;
const char *ntop;
int tmp_counter;
ssize_t err;
setzero(&pkt, sizeof(PACKET));
setzero(&rpkt, sizeof(PACKET));
inet_copy(&to, &ip);
ntop = inet_to_str(to);
debug(DBG_INSANE, "Quest %s to %s", rq_to_str(ANDNA_RESOLVE_IP), ntop);
/* We have been asked to reverse resolve our same IP */
if (!memcmp(to.data, me.cur_ip.data, MAX_IP_SZ) ||
LOOPBACK(htonl(to.data[0])))
return lcl_get_registered_hnames(andna_lcl);
/*
* Fill the packet and send the request
*/
pkt_addto(&pkt, &to);
pkt_addfrom(&rpkt, &to);
pkt_addtimeout(&pkt, ANDNA_REV_RESOLVE_RQ_TIMEOUT, 1, 1);
err =
send_rq(&pkt, 0, ANDNA_RESOLVE_IP, 0, ANDNA_REV_RESOLVE_REPLY, 1,
&rpkt);
if (err < 0) {
error(ERROR_MSG "Reverse resolution of the %s "
"ip failed.", ERROR_FUNC, inet_to_str(to));
ERROR_FINISH(ret, 0, finish);
}
unpacked_lcl = unpack_lcl_cache(rpkt.msg, rpkt.hdr.sz, &tmp_counter);
if (tmp_counter > ANDNA_MAX_HOSTNAMES || tmp_counter < 0)
ERROR_FINISH(ret, 0, finish);
ret = unpacked_lcl;
finish:
pkt_free(&pkt, 1);
pkt_free(&rpkt, 1);
return ret;
}
/*
* andna_recv_rev_resolve_rq
*
* It replies to a reverse hostname resolve request which asks all the
* hostnames associated with a given ip.
*/
int
andna_recv_rev_resolve_rq(PACKET rpkt)
{
PACKET pkt;
const char *ntop;
int ret = 0, err;
lcl_cache *alcl = andna_lcl;
setzero(&pkt, sizeof(PACKET));
ntop = inet_to_str(rpkt.from);
debug(DBG_INSANE,
"Andna reverse resolve request received 0x%x from %s",
rpkt.hdr.id, ntop);
/*
* Build the reply pkt
*/
pkt_fill_hdr(&pkt.hdr, 0, rpkt.hdr.id, ANDNA_REV_RESOLVE_REPLY, 0);
/* Build the list of registered hnames */
if (!(pkt.msg = pack_lcl_cache(alcl, &pkt.hdr.sz))) {
pkt_err(rpkt, E_ANDNA_NO_HNAME, 0);
ERROR_FINISH(ret, -1, finish);
}
debug(DBG_INSANE, "Reverse resolve request 0x%x accepted",
rpkt.hdr.id);
/*
* Send it.
*/
pkt_addto(&pkt, &rpkt.from);
pkt_addsk(&pkt, my_family, rpkt.sk, rpkt.sk_type);
pkt_addcompress(&pkt);
err = send_rq(&pkt, 0, ANDNA_REV_RESOLVE_REPLY, rpkt.hdr.id, 0, 0, 0);
if (err < 0)
ERROR_FINISH(ret, -1, finish);
finish:
pkt_free(&pkt, 0);
return ret;
}
/*
*
* * * * Andna caches transfer * * *
*
*/
/*
* get_single_andna_c
*
* It sends the ANDNA_GET_SINGLE_ACACHE request to the old `hash_gnode' of `hash'
* to retrieve the andna_cache that contains the information about `hash'.
*/
andna_cache *
get_single_andna_c(u_int hash[MAX_IP_INT], u_int hash_gnode[MAX_IP_INT])
{
PACKET pkt, rpkt;
struct single_acache_hdr req_hdr;
andna_cache *ret = 0;
u_int *new_hgnodes[1];
size_t pack_sz;
int err, counter;
const char *ntop;
char *pack;
setzero(&pkt, sizeof(PACKET));
setzero(&rpkt, sizeof(PACKET));
setzero(&req_hdr, sizeof(struct single_acache_hdr));
/*
* Find the old hash_gnode that corresponds to the hash `hash_gnode',
* but exclude from the search ourself 'cause we are a new hash_gnode
* and we are trying to reach the new one.
*/
new_hgnodes[0] = me.cur_ip.data;
if ((err =
find_hash_gnode(hash_gnode, &pkt.to, new_hgnodes, 1, 1)) < 0) {
debug(DBG_SOFT, "get_single_andna_c: old hash_gnode not found");
ERROR_FINISH(ret, 0, finish);
} else if (err == 1)
req_hdr.flags |= ANDNA_PKT_FORWARD;
req_hdr.hgnodes = 1;
inet_copy_ipdata(req_hdr.rip, &me.cur_ip);
memcpy(req_hdr.hash, hash, MAX_IP_SZ);
/* host -> network order */
ints_host_to_network((void *) &req_hdr, single_acache_hdr_iinfo);
/* Pack the request */
pkt.hdr.flags |= ASYNC_REPLY;
pkt.hdr.sz = SINGLE_ACACHE_PKT_SZ(1);
pkt.msg = xmalloc(pkt.hdr.sz);
memcpy(pkt.msg, &req_hdr, pkt.hdr.sz);
/* Append our ip at the end of the pkt */
inet_copy_ipdata((u_int *) (pkt.msg +
sizeof(struct single_acache_hdr)),
&me.cur_ip);
ntop = inet_to_str(pkt.to);
debug(DBG_INSANE, "Quest %s to %s", rq_to_str(ANDNA_GET_SINGLE_ACACHE),
ntop);
err =
send_rq(&pkt, 0, ANDNA_GET_SINGLE_ACACHE, 0, ACK_AFFERMATIVE, 1,
&rpkt);
if (err < 0)
ERROR_FINISH(ret, 0, finish);
/* Unpack the waited reply */
pack_sz = rpkt.hdr.sz;
pack = rpkt.msg;
ret = unpack_andna_cache(pack, pack_sz, &counter, ACACHE_PACK_PKT);
if (!ret && counter < 0) {
error("get_single_acache(): Malformed andna_cache.");
ERROR_FINISH(ret, 0, finish);
}
finish:
pkt_free(&pkt, 1);
pkt_free(&rpkt, 0);
return ret;
}
/*
* put_single_acache
*
* It replies to a get_single_acache request as described
* in andna.h (near the single_acache_hdr struct).
*/
int
put_single_acache(PACKET rpkt)
{
PACKET pkt, rpkt_local_copy;
struct single_acache_hdr *req_hdr;
u_int hash_gnode[MAX_IP_INT], **new_hgnodes = 0;
inet_prefix rfrom, to;
andna_cache *ac, *ac_tmp = 0;
char *buf;
char *ntop = 0, *rfrom_ntop = 0;
int ret = 0, i;
ssize_t err = 0;
size_t pkt_sz = 0;
pkt_copy(&rpkt_local_copy, &rpkt);
req_hdr = (struct single_acache_hdr *) rpkt_local_copy.msg;
/* Save the real sender of the request */
inet_setip(&rfrom, req_hdr->rip, my_family);
ntop = xstrdup(inet_to_str(rpkt.from));
rfrom_ntop = xstrdup(inet_to_str(rfrom));
debug(DBG_NOISE, "Andna get single cache request 0x%x from: %s, real "
"from: %s", rpkt.hdr.id, ntop, rfrom_ntop);
memcpy(&pkt, &rpkt, sizeof(PACKET));
inet_copy(&pkt.from, &rfrom);
pkt_addsk(&pkt, my_family, 0, SKT_UDP);
pkt_addcompress(&pkt);
/* network -> host order */
ints_network_to_host(req_hdr, single_acache_hdr_iinfo);
/* Verify the validity of the header */
if (rpkt.hdr.sz != SINGLE_ACACHE_PKT_SZ(req_hdr->hgnodes) ||
req_hdr->hgnodes > ANDNA_MAX_NEW_GNODES) {
debug(DBG_NOISE, "Malformed GET_SINGLE_ACACHE pkt: %d, %d, %d",
rpkt.hdr.sz, SINGLE_ACACHE_PKT_SZ(req_hdr->hgnodes),
req_hdr->hgnodes);
pkt_err(pkt, E_INVALID_REQUEST, 0);
ERROR_FINISH(ret, -1, finish);
}
/* Unpack the hash_gnodes to exclude */
new_hgnodes = xmalloc(sizeof(u_int *) * (req_hdr->hgnodes + 1));
buf = rpkt.msg + sizeof(struct single_acache_hdr);
for (i = 0; i < req_hdr->hgnodes; i++) {
new_hgnodes[i] = (u_int *) buf;
inet_ntohl(new_hgnodes[i], me.cur_ip.family);
buf += MAX_IP_SZ;
}
/*
* Check if we are the destined old hash_gnode or if we have to still
* forward the pkt
*/
andna_hash_by_family(my_family, (u_char *) req_hdr->hash, hash_gnode);
if ((err = find_hash_gnode(hash_gnode, &to, new_hgnodes,
req_hdr->hgnodes, 0)) < 0) {
debug(DBG_SOFT, "We are not the real (rounded)hash_gnode. "
"Rejecting the 0x%x get_single_acache request", rpkt.hdr.id);
pkt_err(pkt, E_ANDNA_WRONG_HASH_GNODE, 0);
ERROR_FINISH(ret, -1, finish);
} else if (err == 1) {
/* Continue to forward the received pkt */
debug(DBG_SOFT, "The 0x%x rq pkt will be forwarded "
"to: %s", rpkt.hdr.id, inet_to_str(to));
ret = forward_pkt(rpkt, to);
goto finish;
}
/*
* Search in our andna_cache if we have what `rfrom' wants.
*/
if (!(ac = andna_cache_gethash((int *) req_hdr->hash))) {
/*
* Nothing found! Maybe it's because we have an uptime less than
* (ANDNA_EXPIRATION_TIME/2) and so we are a new hash_gnode,
* therefore we have to forward the pkt to an older hash_gnode.
*/
if (time(0) - me.uptime < (ANDNA_EXPIRATION_TIME / 2)) {
new_hgnodes[req_hdr->hgnodes] = me.cur_ip.data;
if ((err = find_hash_gnode(hash_gnode, &to, new_hgnodes,
req_hdr->hgnodes + 1, 1)) < 0) {
/* An older hash_gnode doesn't exist */
debug(DBG_SOFT,
"put_single_andna_c: old hash_gnode not found");
ERROR_FINISH(ret, -1, finish);
}
/*
* Append our ip at the end of the pkt and forward it
* to the older hash_gnode.
*/
memcpy(&pkt, &rpkt, sizeof(PACKET));
pkt.sk = 0;
pkt.hdr.sz = rpkt.hdr.sz + MAX_IP_SZ;
pkt.msg = xmalloc(pkt.hdr.sz);
memcpy(pkt.msg, rpkt.msg, rpkt.hdr.sz);
inet_copy_ipdata((u_int *) (pkt.msg + rpkt.hdr.sz),
&me.cur_ip);
debug(DBG_SOFT, "The 0x%x rq pkt will be forwarded "
"to an older hgnode: %s", rpkt.hdr.id, inet_to_str(to));
ret = forward_pkt(pkt, to);
pkt_free(&pkt, 1);
goto finish;
}
/*
* We are neither a new hash_gnode so the
* get_andna_single_cache propagation ends here and we reply to
* `rfrom': <<That andna_cache doesn't exist, bye bye!>>
*/
debug(DBG_SOFT, "Request %s (0x%x) rejected: %s",
rq_to_str(rpkt.hdr.op), rpkt.hdr.id,
rq_strerror(E_ANDNA_NO_HNAME));
pkt_err(pkt, E_ANDNA_NO_HNAME, 0);
ERROR_FINISH(ret, -1, finish);
}
/*
* We found the andna_cache! Let's reply.
* Pack the `ac' andna_cache and send it to `rfrom'
*/
setzero(&pkt, sizeof(PACKET));
pkt_fill_hdr(&pkt.hdr, ASYNC_REPLIED, rpkt.hdr.id, ACK_AFFERMATIVE, 0);
pkt_addto(&pkt, &rfrom);
pkt_addsk(&pkt, my_family, 0, SKT_TCP);
pkt_addport(&pkt, rpkt.port);
pkt_addcompress(&pkt);
/* Exctract the `ac' cache from the llist, so we can pack it alone */
ac_tmp = list_dup(ac);
pkt.msg = pack_andna_cache(ac_tmp, &pkt_sz, ACACHE_PACK_PKT);
pkt.hdr.sz = pkt_sz;
debug(DBG_INSANE, "Reply put_single_acache to %s", ntop);
err = send_rq(&pkt, 0, ACK_AFFERMATIVE, rpkt.hdr.id, 0, 0, 0);
pkt_free(&pkt, 0);
if (err < 0) {
error("put_andna_cache(): Cannot send the put_single_acache "
"reply to %s.", ntop);
ERROR_FINISH(ret, -1, finish);
}
finish:
if (ac_tmp)
xfree(ac_tmp);
if (new_hgnodes)
xfree(new_hgnodes);
if (ntop)
xfree(ntop);
if (rfrom_ntop)
xfree(rfrom_ntop);
pkt_free(&rpkt_local_copy, 0);
return ret;
}
/*
* spread_single_acache
*
* It tells to all the rnodes to get the single andna_cache of `hash'
* using get_single_andna_c()
*/
int
spread_single_acache(u_int hash[MAX_IP_INT])
{
PACKET pkt;
struct spread_acache_pkt req;
setzero(&pkt, sizeof(PACKET));
memcpy(req.hash, hash, MAX_IP_SZ);
ints_host_to_network(&req, spread_acache_pkt_info);
pkt_fill_hdr(&pkt.hdr, 0, 0, ANDNA_SPREAD_SACACHE,
SPREAD_ACACHE_PKT_SZ);
pkt.msg = xmalloc(pkt.hdr.sz);
memcpy(pkt.msg, &req, pkt.hdr.sz);
debug(DBG_NOISE, "Spreading the single andna_cache 0x%x", pkt.hdr.id);
andna_add_flood_pkt_id(last_spread_acache_pkt_id, pkt.hdr.id);
return andna_flood_pkt(&pkt, 0);
}
/*
* recv_spread_single_acache
*
* It receives and execute the ANDNA_SPREAD_SACACHE request.
*/
int
recv_spread_single_acache(PACKET rpkt)
{
PACKET rpkt_local_copy;
struct spread_acache_pkt *req;
andna_cache *ac;
u_int hash_gnode[MAX_IP_INT];
int ret = 0;
pkt_copy(&rpkt_local_copy, &rpkt);
req = (struct spread_acache_pkt *) rpkt_local_copy.msg;
if (rpkt.hdr.sz != SPREAD_ACACHE_PKT_SZ)
ERROR_FINISH(ret, -1, finish);
/* network -> host order */
ints_network_to_host(req, spread_acache_pkt_info);
/* Check if we already received this pkt during the flood */
if (andna_add_flood_pkt_id(last_spread_acache_pkt_id, rpkt.hdr.id)) {
debug(DBG_INSANE, "Dropped 0x%0x andna pkt, we already "
"received it", rpkt.hdr.id);
ERROR_FINISH(ret, 0, finish);
}
if (time(0) - me.uptime > (ANDNA_EXPIRATION_TIME / 2) ||
(ac = andna_cache_findhash((int *) req->hash))) {
/* We don't need to get the andna_cache from an old
* hash_gnode, since we currently are one of them! */
debug(DBG_NOISE, "recv_spread_single_acache: We are an old "
"hash_gnode, dropping 0x%x", rpkt.hdr.id);
ERROR_FINISH(ret, -1, finish);
}
debug(DBG_NOISE, "Received the spreaded andna_cache 0x%x",
rpkt.hdr.id);
/*
* Do as the rpkt tells us to do: retrieve the andna_hash for
* req->hash from the old hash_gnode and store it.
*/
andna_hash_by_family(my_family, (u_char *) req->hash, hash_gnode);
if ((ac = get_single_andna_c(req->hash, hash_gnode))) {
/* Save it in our andna_cache. */
clist_add(&andna_c, &andna_c_counter, ac);
} else {
debug(DBG_NOISE, "recv_spread_single_acache: (0x%x) "
"get_single_andna_c request failed", rpkt.hdr.id);
ERROR_FINISH(ret, -1, finish);
}
/* Continue to forward the pkt in the gnode */
andna_flood_pkt(&rpkt, 1);
finish:
pkt_free(&rpkt_local_copy, 0);
return ret;
}
/*
* get_andna_cache
*
* sends the ANDNA_GET_ANDNA_CACHE request to rnode `dst_rnode' to retrieve its
* andna_cache.
*/
andna_cache *
get_andna_cache(map_node * dst_rnode, int *counter)
{
PACKET pkt, rpkt;
andna_cache *ret = 0;
size_t pack_sz;
int err;
char *pack;
setzero(&pkt, sizeof(PACKET));
setzero(&rpkt, sizeof(PACKET));
if (rnl_fill_rq(dst_rnode, &pkt) < 0)
ERROR_FINISH(ret, 0, finish);
if (server_opt.dbg_lvl) {
const char *ntop;
ntop = inet_to_str(pkt.to);
debug(DBG_INSANE, "Quest %s to %s",
rq_to_str(ANDNA_GET_ANDNA_CACHE), ntop);
}
pkt_addtimeout(&pkt, ANDNA_HOOK_TIMEOUT, 1, 1);
err = send_rq(&pkt, 0, ANDNA_GET_ANDNA_CACHE, 0,
ANDNA_PUT_ANDNA_CACHE, 1, &rpkt);
if (err < 0) {
ret = 0;
goto finish;
}
pack_sz = rpkt.hdr.sz;
pack = rpkt.msg;
ret = unpack_andna_cache(pack, pack_sz, counter, ACACHE_PACK_PKT);
if (!ret && counter < 0)
error("get_andna_cache(): Malformed or empty andna_cache. "
"Cannot load it");
finish:
pkt_free(&pkt, 0);
pkt_free(&rpkt, 0);
return ret;
}
/*
* put_andna_cache
*
* replies to a ANDNA_GET_ANDNA_CACHE request, sending back the complete
* andna cache
*/
int
put_andna_cache(PACKET rq_pkt)
{
PACKET pkt;
const char *ntop;
int ret = 0;
ssize_t err = 0;
size_t pkt_sz = 0;
ntop = inet_to_str(rq_pkt.from);
setzero(&pkt, sizeof(PACKET));
pkt_addto(&pkt, &rq_pkt.from);
pkt_addsk(&pkt, my_family, rq_pkt.sk, rq_pkt.sk_type);
pkt_addcompress(&pkt);
pkt.msg = pack_andna_cache(andna_c, &pkt_sz, ACACHE_PACK_PKT);
pkt.hdr.sz = pkt_sz;
debug(DBG_INSANE, "Reply %s to %s", re_to_str(ANDNA_PUT_ANDNA_CACHE),
ntop);
err = send_rq(&pkt, 0, ANDNA_PUT_ANDNA_CACHE, rq_pkt.hdr.id, 0, 0, 0);
if (err < 0) {
error
("put_andna_cache(): Cannot send the ANDNA_PUT_ANDNA_CACHE reply to %s.",
ntop);
ERROR_FINISH(ret, -1, finish);
}
finish:
pkt_free(&pkt, 0);
return ret;
}
/*
* get_counter_cache
*
* sends the ANDNA_GET_COUNT_CACHE request to rnode `dst_rnode' to retrieve its
* counter_cache.
*/
counter_c *
get_counter_cache(map_node * dst_rnode, int *counter)
{
PACKET pkt, rpkt;
counter_c *ccache, *ret = 0;
size_t pack_sz;
int err;
char *pack;
setzero(&pkt, sizeof(PACKET));
setzero(&rpkt, sizeof(PACKET));
if (rnl_fill_rq(dst_rnode, &pkt) < 0)
ERROR_FINISH(ret, 0, finish);
if (server_opt.dbg_lvl) {
const char *ntop;
ntop = inet_to_str(pkt.to);
debug(DBG_INSANE, "Quest %s to %s",
rq_to_str(ANDNA_GET_COUNT_CACHE), ntop);
}
pkt_addtimeout(&pkt, ANDNA_HOOK_TIMEOUT, 1, 1);
err = send_rq(&pkt, 0, ANDNA_GET_COUNT_CACHE, 0,
ANDNA_PUT_COUNT_CACHE, 1, &rpkt);
if (err < 0) {
ret = 0;
goto finish;
}
pack_sz = rpkt.hdr.sz;
pack = rpkt.msg;
ret = ccache = unpack_counter_cache(pack, pack_sz, counter);
if (!ccache && counter < 0)
error(ERROR_MSG "Malformed counter_cache. Cannot load it",
ERROR_FUNC);
finish:
pkt_free(&pkt, 0);
pkt_free(&rpkt, 0);
return ret;
}
/*
* put_counter_cache
*
* replies to a ANDNA_GET_COUNT_CACHE request, sending back the
* complete andna cache.
*/
int
put_counter_cache(PACKET rq_pkt)
{
PACKET pkt;
const char *ntop;
int ret = 0;
ssize_t err = 0;
size_t pkt_sz = 0;
ntop = inet_to_str(rq_pkt.from);
setzero(&pkt, sizeof(PACKET));
pkt_addto(&pkt, &rq_pkt.from);
pkt_addsk(&pkt, my_family, rq_pkt.sk, rq_pkt.sk_type);
pkt_addcompress(&pkt);
pkt.msg = pack_counter_cache(andna_counter_c, &pkt_sz);
pkt.hdr.sz = pkt_sz;
debug(DBG_INSANE, "Reply %s to %s", re_to_str(ANDNA_PUT_COUNT_CACHE),
ntop);
err = send_rq(&pkt, 0, ANDNA_PUT_COUNT_CACHE, rq_pkt.hdr.id, 0, 0, 0);
if (err < 0) {
error
("put_counter_cache(): Cannot send the ANDNA_PUT_COUNT_CACHE reply to %s.",
ntop);
ERROR_FINISH(ret, -1, finish);
}
finish:
pkt_free(&pkt, 0);
return ret;
}
/*
*
* * * * ANDNA threads * * *
*
*/
/*
* andna_hook
*
* The andna_hook gets the andna_cache and the counter_node cache from
* the nearest rnodes.
*/
void *
andna_hook(void *null)
{
inet_prefix to;
map_node *node;
int e = 0, i;
setzero(&to, sizeof(inet_prefix));
/* Block these requests */
op_filter_set(ANDNA_SPREAD_SACACHE);
loginfo("Starting the ANDNA hook.");
if (!me.cur_node->links) {
/* nothing to do */
debug(DBG_NORMAL, "There are no nodes, skipping the ANDNA hook.");
goto finish;
}
/*
* Send the GET_ANDNA_CACHE request to the nearest rnode we have, if it
* fails send it to the second rnode and so on...
*/
for (i = 0, e = 0; i < me.cur_node->links; i++) {
node = (map_node *) me.cur_node->r_node[i].r_node;
if (!node || node->flags & MAP_ERNODE)
continue;
andna_c = get_andna_cache(node, &andna_c_counter);
if (andna_c) {
e = 1;
break;
}
}
if (!e)
loginfo
("None of the rnodes in this area gave me the andna_cache.");
/*
* Send the GET_COUNT_CACHE request to the nearest rnode we have, if it
* fails send it to the second rnode and so on...
*/
for (i = 0, e = 0; i < me.cur_node->links; i++) {
node = (map_node *) me.cur_node->r_node[i].r_node;
if (!node || node->flags & MAP_ERNODE)
continue;
andna_counter_c = get_counter_cache(node, &cc_counter);
if (andna_counter_c) {
e = 1;
break;
}
}
if (!e)
loginfo
("None of the rnodes in this area gave me the counter_cache.");
finish:
/* Un-block these requests */
op_filter_clr(ANDNA_SPREAD_SACACHE);
loginfo("ANDNA hook completed");
return 0;
}
/*
* andna_min_update_retry
*
* waits ANDNA_MIN_UPDATE_TIME seconds and then updates `void_alcl'.
*/
void *
andna_min_update_retry(void *void_alcl)
{
lcl_cache *alcl = (lcl_cache *) void_alcl;
int ret;
sleep(ANDNA_MIN_UPDATE_TIME);
ret = andna_register_hname(alcl, 0);
if (!ret)
loginfo("Hostname \"%s\" registered/updated "
"successfully", alcl->hostname);
return 0;
}
/*
* andna_update_hnames
*
* updates/registers all the hostnames present in the local cache.
* If `only_new_hname' is not zero, it registers only the new hostnames
* added in the local cache.
*/
void
andna_update_hnames(int only_new_hname)
{
pthread_t thread;
lcl_cache *alcl = andna_lcl;
int ret, updates = 0;
list_for(alcl) {
if (only_new_hname && alcl->timestamp)
/* don't register old hnames */
continue;
ret = andna_register_hname(alcl, 0);
if (!ret) {
loginfo("Hostname \"%s\" registered/updated "
"successfully", alcl->hostname);
updates++;
} else {
/* Wait ANDNA_MIN_UPDATE_TIME seconds then retry to
* update it */
pthread_create(&thread, 0, andna_min_update_retry,
(void *) alcl);
pthread_detach(thread);
}
}
if (updates)
save_lcl_cache(andna_lcl, server_opt.lcl_file);
}
/*
* andna_maintain_hnames_active
*
* periodically registers and keep up to date the hostnames of the local cache.
*/
void *
andna_maintain_hnames_active(void *null)
{
lcl_cache *alcl;
int ret, updates;
/* Wait a bit before trying to register the hname. The first QSPN must
* be already sent */
while (time(0) - me.uptime < QSPN_WAIT_ROUND / 2)
sleep(1);
for (;;) {
updates = 0;
alcl = andna_lcl;
/** If we don't have rnodes, it's useless to try
* anything */
while (!me.cur_node->links)
sleep(2);
/**/ list_for(alcl) {
ret = andna_register_hname(alcl, 0);
if (!ret) {
loginfo("Hostname \"%s\" registered/updated "
"successfully", alcl->hostname);
updates++;
}
}
if (updates)
save_lcl_cache(andna_lcl, server_opt.lcl_file);
sleep((ANDNA_EXPIRATION_TIME / 2) + rand_range(1, 10));
}
return 0;
}
void *
andna_main(void *null)
{
struct udp_daemon_argv ud_argv;
u_short *port;
pthread_t thread;
pthread_attr_t t_attr;
pthread_attr_init(&t_attr);
pthread_attr_setdetachstate(&t_attr, PTHREAD_CREATE_DETACHED);
setzero(&ud_argv, sizeof(struct udp_daemon_argv));
port = xmalloc(sizeof(u_short));
pthread_mutex_init(&udp_daemon_lock, 0);
pthread_mutex_init(&tcp_daemon_lock, 0);
debug(DBG_SOFT, "Evoking the andna udp daemon.");
ud_argv.port = andna_udp_port;
ud_argv.flags |= UDP_THREAD_FOR_EACH_PKT;
pthread_mutex_lock(&udp_daemon_lock);
pthread_create(&thread, &t_attr, udp_daemon, (void *) &ud_argv);
pthread_mutex_lock(&udp_daemon_lock);
pthread_mutex_unlock(&udp_daemon_lock);
debug(DBG_SOFT, "Evoking the andna tcp daemon.");
*port = andna_tcp_port;
pthread_mutex_lock(&tcp_daemon_lock);
pthread_create(&thread, &t_attr, tcp_daemon, (void *) port);
pthread_mutex_lock(&tcp_daemon_lock);
pthread_mutex_unlock(&tcp_daemon_lock);
/*
* Start the ANDNA hook
*/
pthread_create(&thread, &t_attr, andna_hook, 0);
/*
* DNS wrapper
*/
debug(DBG_SOFT, "Evoking the DNS wrapper daemon.");
pthread_create(&thread, &t_attr, dns_wrapper_thread, 0);
/*
* Start the hostnames updater and register
*/
andna_maintain_hnames_active(0);
xfree(port);
return 0;
}