Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
Michal Zima
coincer
Commits
044e1174
Commit
044e1174
authored
Sep 16, 2018
by
xpetrak2
Browse files
Daemon messages processing
parent
10797b1c
Changes
7
Hide whitespace changes
Inline
Side-by-side
Makefile.am
View file @
044e1174
...
...
@@ -8,6 +8,7 @@ src_coincerd_SOURCES = \
src/crypto.c src/crypto.h
\
src/daemon_events.c src/daemon_events.h
\
src/daemon_messages.c src/daemon_messages.h
\
src/daemon_messages_processor.c src/daemon_messages_processor.h
\
src/global_state.c src/global_state.h
\
src/hosts.c src/hosts.h
\
src/json_parser.c src/json_parser.h
\
...
...
src/daemon_messages_processor.c
0 → 100644
View file @
044e1174
/*
* Coincer
* Copyright (C) 2017-2018 Coincer Developers
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <netinet/in.h>
#include <sodium.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "crypto.h"
#include "daemon_messages.h"
#include "daemon_messages_processor.h"
#include "global_state.h"
#include "hosts.h"
#include "json_parser.h"
#include "log.h"
#include "neighbours.h"
#include "routing.h"
/** The minimum time in seconds that has to pass between the announcements of
* presence of one identity. */
#define ADV_GAP_TIME 10
static
enum
process_message_result
process_message
(
const
message_t
*
message
,
neighbour_t
*
sender
,
global_state_t
*
global_state
);
static
int
process_p2p_bye
(
const
message_t
*
message
,
neighbour_t
*
sender
,
peer_t
*
sender_peer
,
global_state_t
*
global_state
);
static
int
process_p2p_hello
(
const
message_t
*
message
,
neighbour_t
*
sender
,
linkedlist_t
*
neighbours
,
unsigned
short
port
);
static
int
process_p2p_peers_adv
(
const
message_t
*
message
,
neighbour_t
*
sender
,
linkedlist_t
*
hosts
);
static
int
process_p2p_peers_sol
(
neighbour_t
*
sender
,
const
linkedlist_t
*
hosts
);
static
int
process_p2p_ping
(
neighbour_t
*
sender
);
static
int
process_p2p_pong
(
neighbour_t
*
sender
);
static
int
process_p2p_route_adv
(
const
message_t
*
message
,
neighbour_t
*
sender
,
peer_t
*
sender_peer
,
global_state_t
*
global_state
);
static
int
process_p2p_route_sol
(
const
message_t
*
message
,
neighbour_t
*
sender
,
global_state_t
*
global_state
);
/**
* Process a JSON message received from its forwarder/sender (our neighbour).
*
* @param json_message The received JSON message.
* @param sender The message sender/forwarder.
* @param global_state The global state.
*
* @return PMR_DONE The received message was successfully
* processed.
* @return PMR_ERR_INTEGRITY Someone tampered with the message.
* @return PMR_ERR_INTERNAL Internal processing error.
* @return PMR_ERR_PARSING Parsing failure.
* @return PMR_ERR_SEMANTIC Semantic error.
* @return PMR_ERR_VERSION The message is of different
* protocol version.
*/
enum
process_message_result
process_encoded_message
(
const
char
*
json_message
,
neighbour_t
*
sender
,
global_state_t
*
global_state
)
{
char
*
json_message_body
;
message_t
message
;
enum
process_message_result
ret
;
/* decode JSON message; if parsing JSON message into message_t failed */
if
(
decode_message
(
json_message
,
&
message
))
{
log_debug
(
"process_encoded_message - decoding a received "
"message has failed"
);
return
PMR_ERR_PARSING
;
}
if
(
message
.
version
!=
PROTOCOL_VERSION
)
{
message_delete
(
&
message
);
return
PMR_ERR_VERSION
;
}
/* integrity verification part; get message body in JSON format */
if
(
encode_message_body
(
&
message
.
body
,
&
json_message_body
))
{
log_error
(
"Encoding what we've just decoded has failed"
);
message_delete
(
&
message
);
return
PMR_ERR_INTERNAL
;
}
/* if the message integrity is violated */
if
(
verify_signature
(
json_message_body
,
message
.
from
,
message
.
sig
))
{
log_warn
(
"Someone tampered with a received message"
);
free
(
json_message_body
);
message_delete
(
&
message
);
return
PMR_ERR_INTEGRITY
;
}
/* integrity verification done */
free
(
json_message_body
);
/* process the message */
ret
=
process_message
(
&
message
,
sender
,
global_state
);
message_delete
(
&
message
);
return
ret
;
}
/**
* Process a message received from its forwarder/sender (our neighbour).
*
* @param message The received message.
* @param sender The message sender/forwarder.
* @param global_state The global state.
*
* @return PMR_DONE The received message was successfully
* processed.
* @return PMR_ERR_INTERNAL Internal processing error.
* @return PMR_ERR_SEMANTIC Semantic error.
*/
static
enum
process_message_result
process_message
(
const
message_t
*
message
,
neighbour_t
*
sender
,
global_state_t
*
global_state
)
{
int
cmp_val
;
linkedlist_t
*
hosts
;
linkedlist_t
*
identities
;
identity_t
*
identity
;
const
message_body_t
*
msg_body
;
const
linkedlist_t
*
msg_traces
;
enum
message_type
msg_type
;
linkedlist_t
*
neighbours
;
uint64_t
nonce_value
;
linkedlist_t
*
peers
;
int
res
;
linkedlist_t
*
routing_table
;
peer_t
*
sender_peer
;
msg_body
=
&
message
->
body
;
msg_type
=
msg_body
->
type
;
nonce_value
=
msg_body
->
nonce
;
hosts
=
&
global_state
->
hosts
;
identities
=
&
global_state
->
identities
;
msg_traces
=
&
global_state
->
message_traces
;
neighbours
=
&
global_state
->
neighbours
;
peers
=
&
global_state
->
peers
;
routing_table
=
&
global_state
->
routing_table
;
/* if we haven't yet received p2p.hello from the neighbour who's
* sent/forwarded the received message */
if
(
!
(
sender
->
flags
&
NEIGHBOUR_ACTIVE
))
{
/* and the message is not p2p.hello */
if
(
msg_type
!=
P2P_HELLO
)
{
log_debug
(
"process_message - received non-hello msg "
"before p2p.hello"
);
return
PMR_ERR_SEMANTIC
;
}
res
=
process_p2p_hello
(
message
,
sender
,
neighbours
,
global_state
->
port
);
if
(
!
res
)
{
nonce_store
(
&
sender
->
pseudonym
.
nonces
,
nonce_value
);
return
PMR_DONE
;
}
return
PMR_ERR_INTERNAL
;
}
/* if we've received a message that we've created */
if
(
identity_find
(
identities
,
message
->
from
))
{
/* and if it is a p2p message */
if
(
!
identifier_empty
(
msg_body
->
to
))
{
routing_loop_remove
(
routing_table
,
neighbours
,
identities
,
msg_body
->
to
);
}
return
PMR_DONE
;
}
/* let's get peer representations of the sender and appropriate ours */
cmp_val
=
0
;
sender_peer
=
NULL
;
/* all bytes of 'to' are set to 0x0 => we are one of the recipients */
if
(
identifier_empty
(
msg_body
->
to
))
{
/* 'from' different from the neighbour's pseudonym =>
* the message should be a broadcast (we will check later) */
if
(
memcmp
(
message
->
from
,
sender
->
pseudonym
.
identifier
,
crypto_box_PUBLICKEYBYTES
))
{
/* process the broadcast with the true identity */
identity
=
global_state
->
true_identity
;
/* otherwise it is a n2n message */
}
else
{
identity
=
&
sender
->
my_pseudonym
;
sender_peer
=
&
sender
->
pseudonym
;
/* for later parity check */
cmp_val
=
memcmp
(
message
->
from
,
identity
->
keypair
.
public_key
,
crypto_box_PUBLICKEYBYTES
);
}
/* the message is of type p2p */
}
else
{
/* identity will be NULL if the message is not meant for us */
identity
=
identity_find
(
identities
,
msg_body
->
to
);
/* for later parity check */
cmp_val
=
memcmp
(
message
->
from
,
msg_body
->
to
,
crypto_box_PUBLICKEYBYTES
);
}
/* nonce parity check; the message must have:
* sender ID > our ID => odd nonce; sender ID < our ID => even nonce */
if
((
cmp_val
>
0
&&
nonce_value
%
2
==
0
)
||
(
cmp_val
<
0
&&
nonce_value
%
2
==
1
))
{
log_debug
(
"process_mesage - wrong nonce parity"
);
return
PMR_ERR_SEMANTIC
;
}
/* if the message is not n2n */
if
(
!
sender_peer
)
{
/* from what peer is this message? */
sender_peer
=
peer_find
(
peers
,
message
->
from
);
}
/* if we don't know this peer yet */
if
(
!
sender_peer
)
{
sender_peer
=
peer_store
(
peers
,
message
->
from
);
/* storing the new peer has failed */
if
(
!
sender_peer
)
{
return
PMR_ERR_INTERNAL
;
}
}
else
{
if
(
msg_type
==
P2P_ROUTE_ADV
)
{
/* if it's an old announcement of presence, skip it */
if
(
nonce_value
<
sender_peer
->
presence_nonce
.
value
)
{
return
PMR_DONE
;
}
}
else
{
/* have we already processed this message? */
if
(
nonces_find
(
&
sender_peer
->
nonces
,
nonce_value
))
{
/* if broadcast or n2n msg */
if
(
identifier_empty
(
msg_body
->
to
)
||
identity
==
&
sender
->
my_pseudonym
)
{
return
PMR_DONE
;
}
/* check if there's a routing loop */
if
(
routing_loop_detect
(
msg_traces
,
sender
,
nonce_value
))
{
routing_loop_remove
(
routing_table
,
neighbours
,
identities
,
msg_body
->
to
);
}
return
PMR_DONE
;
}
/* if the message smells like replay attack */
if
(
!
linkedlist_empty
(
&
sender_peer
->
nonces
)
&&
nonce_value
<
(
nonces_get_first
(
&
sender_peer
->
nonces
))
->
value
)
{
log_warn
(
"Potential replay attack detected"
);
return
PMR_ERR_SEMANTIC
;
}
}
}
/* no corresponding identity to process the message => the message is
* not meant for us */
if
(
!
identity
)
{
/* if forwarding succeeded */
if
(
!
message_forward
(
message
,
sender
,
global_state
))
{
/* store message's nonce */
nonce_store
(
&
sender_peer
->
nonces
,
nonce_value
);
}
return
PMR_DONE
;
}
else
if
(
identity
->
flags
&
IDENTITY_TMP
)
{
/* no one is supposed to send a message to this identity */
return
PMR_ERR_SEMANTIC
;
}
/* the message is meant for us; process it */
switch
(
msg_type
)
{
case
P2P_BYE
:
res
=
process_p2p_bye
(
message
,
sender
,
sender_peer
,
global_state
);
break
;
case
P2P_HELLO
:
res
=
process_p2p_hello
(
message
,
sender
,
neighbours
,
global_state
->
port
);
break
;
case
P2P_PEERS_ADV
:
/* if the message was not sent by the same peer
* pseudonym of the sender as their p2p.hello */
if
(
&
sender
->
pseudonym
!=
sender_peer
)
{
log_debug
(
"process_message - incorrect sender"
);
/* since the message was sent by other ID than
* neighbour's pseudonym, we've treated it as a
* new peer; that's unwanted for n2n messages */
peer_delete
(
sender_peer
);
return
PMR_ERR_SEMANTIC
;
}
res
=
process_p2p_peers_adv
(
message
,
sender
,
hosts
);
break
;
case
P2P_PEERS_SOL
:
res
=
process_p2p_peers_sol
(
sender
,
hosts
);
break
;
case
P2P_PING
:
res
=
process_p2p_ping
(
sender
);
break
;
case
P2P_PONG
:
if
(
&
sender
->
pseudonym
!=
sender_peer
)
{
log_debug
(
"process_message - incorrect sender"
);
peer_delete
(
sender_peer
);
return
PMR_ERR_SEMANTIC
;
}
res
=
process_p2p_pong
(
sender
);
break
;
case
P2P_ROUTE_ADV
:
res
=
process_p2p_route_adv
(
message
,
sender
,
sender_peer
,
global_state
);
break
;
case
P2P_ROUTE_SOL
:
res
=
process_p2p_route_sol
(
message
,
sender
,
global_state
);
break
;
default:
log_debug
(
"process_message - unknown message type"
);
return
PMR_ERR_SEMANTIC
;
}
/* if the message processing has failed */
if
(
res
)
{
return
PMR_ERR_INTERNAL
;
}
/* sucessfully processed; store message's nonce */
if
(
msg_type
==
P2P_ROUTE_ADV
)
{
presence_nonce_store
(
sender_peer
,
nonce_value
);
}
else
if
(
msg_type
!=
P2P_BYE
)
{
nonce_store
(
&
sender_peer
->
nonces
,
nonce_value
);
}
sender
->
failed_pings
=
0
;
return
PMR_DONE
;
}
/**
* Process p2p.bye.
*
* @param message Process this message.
* @param sender We've received the message from this neighbour.
* @param sender_peer Sender's peer representation.
* @param global_state The global state.
*
* @return 0 Successfully processed.
*/
static
int
process_p2p_bye
(
const
message_t
*
message
,
neighbour_t
*
sender
,
peer_t
*
sender_peer
,
global_state_t
*
global_state
)
{
route_delete
(
&
global_state
->
routing_table
,
message
->
from
);
peer_delete
(
sender_peer
);
message_forward
(
message
,
sender
,
global_state
);
return
0
;
}
/**
* Process p2p.hello.
*
* @param message Process this message.
* @param sender We've received the message from this neighbour.
* @param neighbours Our neighbours.
* @param port Our listening port.
*
* @return 0 Successfully processed.
* @return 1 Failure.
*/
static
int
process_p2p_hello
(
const
message_t
*
message
,
neighbour_t
*
sender
,
linkedlist_t
*
neighbours
,
unsigned
short
port
)
{
p2p_hello_t
*
hello
;
neighbour_t
*
neighbour
;
/* don't take hello from already active neighbour */
if
(
sender
->
flags
&
NEIGHBOUR_ACTIVE
)
{
return
1
;
}
/* check self-neighbouring; if we have a neighbour with 'my_pseudonym'
* the same as the received message's sender ID, then we've
* detected self-neighbouring; delete the mentioned neighbour,
* and also delete the message sender (our neighbour representation of
* the sending counterparty)
*/
if
((
neighbour
=
find_neighbour
(
neighbours
,
message
->
from
,
compare_neighbour_my_pseudonyms
)))
{
linkedlist_delete_safely
(
neighbour
->
node
,
clear_neighbour
);
linkedlist_delete_safely
(
sender
->
node
,
clear_neighbour
);
return
1
;
}
hello
=
(
p2p_hello_t
*
)
message
->
body
.
data
;
sender
->
client
=
(
char
*
)
malloc
((
strlen
(
hello
->
client
)
+
1
)
*
sizeof
(
char
));
if
(
!
sender
->
client
)
{
log_error
(
"Processing p2p.hello"
);
return
1
;
}
strcpy
(
sender
->
client
,
hello
->
client
);
/* if the new neighbour is not one of the default hosts */
if
(
sender
->
host
)
{
sender
->
host
->
port
=
hello
->
port
;
}
memcpy
(
sender
->
pseudonym
.
identifier
,
message
->
from
,
crypto_box_PUBLICKEYBYTES
);
set_neighbour_flags
(
sender
,
NEIGHBOUR_ACTIVE
);
return
send_p2p_hello
(
sender
,
port
);
}
/**
* Process p2p.peers.adv.
*
* @param message Process this message.
* @param sender We've received the message from this neighbour.
* @param hosts Our known hosts.
*
* @return 0 Successfully processed.
* @return 1 Failure.
*/
static
int
process_p2p_peers_adv
(
const
message_t
*
message
,
neighbour_t
*
sender
,
linkedlist_t
*
hosts
)
{
struct
in6_addr
addr
;
char
addr_str
[
INET6_ADDRSTRLEN
];
size_t
n
;
p2p_peers_adv_t
*
peers_adv
;
unsigned
short
port
;
char
*
pos
;
char
tuple
[
55
];
/* string address = 45 chars,
* string port = 5 chars,
* ',' + 3x' ' + '\0' = 5 chars,
* total = 55 chars
*/
/* if we haven't asked this neighbour for addresses */
if
(
!
(
sender
->
flags
&
NEIGHBOUR_ADDRS_REQ
))
{
log_debug
(
"process_p2p_peers_adv - unwanted addrs arrived"
);
return
1
;
}
unset_neighbour_flags
(
sender
,
NEIGHBOUR_ADDRS_REQ
);
peers_adv
=
(
p2p_peers_adv_t
*
)
message
->
body
.
data
;
/* if the list doesn't have enough chars to be even empty ("[ ]",
* which is 4 chars) */
if
(
strlen
(
peers_adv
->
addresses
)
<
4
)
{
log_debug
(
"process_p2p_peers_adv - wrong addrs format"
);
return
1
;
}
/* set initial pointer position; skip the outer list '[' */
pos
=
peers_adv
->
addresses
+
1
;
/* get position of the first '[' */
pos
=
strchr
(
pos
,
'['
);
/* while there is a tuple to be processed */
while
(
pos
)
{
/* get the number of chars between the current '[' and the next
* ']', both boundary chars excluding */
n
=
strcspn
(
++
pos
,
"]"
);
/* the tuple can not have more than 54 chars (plus '\0') */
if
(
n
>
54
)
{
log_debug
(
"process_p2p_peers_adv - wrong addrs format"
);
return
1
;
}
/* copy the chars between '[' and ']', both boundary
* chars excluding */
strncpy
(
tuple
,
pos
,
n
);
tuple
[
n
]
=
'\0'
;
/* get string addr and numerical port from the tuple */
if
(
sscanf
(
tuple
,
" %[^,], %hu "
,
addr_str
,
&
port
)
!=
2
)
{
log_debug
(
"process_p2p_peers_adv - wrong addrs format"
);
return
1
;
}
/* if conversion of addr_str into addr succeeded */
if
(
inet_pton
(
AF_INET6
,
addr_str
,
&
addr
)
==
1
)
{
save_host
(
hosts
,
&
addr
,
port
,
HOST_AVAILABLE
);
}
/* go to next tuple */
pos
=
strchr
(
pos
,
'['
);
}
return
0
;
}
/**
* Process p2p.peers.sol.
*
* @param sender We've received the message from this neighbour.
* @param hosts Hosts known to us.
*
* @return 0 Successfully processed.
* @return 1 Failure.
*/
static
int
process_p2p_peers_sol
(
neighbour_t
*
sender
,
const
linkedlist_t
*
hosts
)
{
return
send_p2p_peers_adv
(
sender
,
hosts
);
}
/**
* Process p2p.ping.
*
* @param sender We've received the message from this neighbour.
*
* @return 0 Successfully processed.
* @return 1 Failure.
*/
static
int
process_p2p_ping
(
neighbour_t
*
sender
)
{
return
send_p2p_pong
(
sender
);
}
/**
* Process p2p.pong.
*
* @param sender We've received the message from this neighbour.
*
* @return 0 Successfully processed.
*/
static
int
process_p2p_pong
(
neighbour_t
*
sender
)
{
sender
->
failed_pings
=
0
;
return
0
;
}
/**
* Process p2p.route.adv.
*
* @param message Process this message.
* @param sender We've received the message from this neighbour.
* @param sender_peer Sender's peer representation.
* @param global_state The global state.
*
* @return 0 Successfully processed.
* @return 1 Failure.
*/
static
int
process_p2p_route_adv
(
const
message_t
*
message
,
neighbour_t
*
sender
,
peer_t
*
sender_peer
,
global_state_t
*
global_state
)
{
route_t
*
route
;
linkedlist_t
*
routing_table
;
routing_table
=
&
global_state
->
routing_table
;
route
=
route_find
(
routing_table
,
message
->
from
);
if
(
!
route
)
{
if
(
!
(
route
=
route_add
(
routing_table
,
sender_peer
,
sender
)))
{
log_error
(
"Adding a new route"
);
return
1
;
}
}