mirror of
https://github.com/nmap/nmap.git
synced 2025-12-06 04:31:29 +00:00
1162 lines
48 KiB
C++
1162 lines
48 KiB
C++
|
|
/***************************************************************************
|
|
* EchoClient.cc -- *
|
|
* *
|
|
***********************IMPORTANT NMAP LICENSE TERMS************************
|
|
* *
|
|
* The Nmap Security Scanner is (C) 1996-2016 Insecure.Com LLC. Nmap is *
|
|
* also a registered trademark of Insecure.Com LLC. This program is free *
|
|
* software; you may redistribute and/or modify it under the terms of the *
|
|
* GNU General Public License as published by the Free Software *
|
|
* Foundation; Version 2 ("GPL"), BUT ONLY WITH ALL OF THE CLARIFICATIONS *
|
|
* AND EXCEPTIONS DESCRIBED HEREIN. This guarantees your right to use, *
|
|
* modify, and redistribute this software under certain conditions. If *
|
|
* you wish to embed Nmap technology into proprietary software, we sell *
|
|
* alternative licenses (contact sales@nmap.com). Dozens of software *
|
|
* vendors already license Nmap technology such as host discovery, port *
|
|
* scanning, OS detection, version detection, and the Nmap Scripting *
|
|
* Engine. *
|
|
* *
|
|
* Note that the GPL places important restrictions on "derivative works", *
|
|
* yet it does not provide a detailed definition of that term. To avoid *
|
|
* misunderstandings, we interpret that term as broadly as copyright law *
|
|
* allows. For example, we consider an application to constitute a *
|
|
* derivative work for the purpose of this license if it does any of the *
|
|
* following with any software or content covered by this license *
|
|
* ("Covered Software"): *
|
|
* *
|
|
* o Integrates source code from Covered Software. *
|
|
* *
|
|
* o Reads or includes copyrighted data files, such as Nmap's nmap-os-db *
|
|
* or nmap-service-probes. *
|
|
* *
|
|
* o Is designed specifically to execute Covered Software and parse the *
|
|
* results (as opposed to typical shell or execution-menu apps, which will *
|
|
* execute anything you tell them to). *
|
|
* *
|
|
* o Includes Covered Software in a proprietary executable installer. The *
|
|
* installers produced by InstallShield are an example of this. Including *
|
|
* Nmap with other software in compressed or archival form does not *
|
|
* trigger this provision, provided appropriate open source decompression *
|
|
* or de-archiving software is widely available for no charge. For the *
|
|
* purposes of this license, an installer is considered to include Covered *
|
|
* Software even if it actually retrieves a copy of Covered Software from *
|
|
* another source during runtime (such as by downloading it from the *
|
|
* Internet). *
|
|
* *
|
|
* o Links (statically or dynamically) to a library which does any of the *
|
|
* above. *
|
|
* *
|
|
* o Executes a helper program, module, or script to do any of the above. *
|
|
* *
|
|
* This list is not exclusive, but is meant to clarify our interpretation *
|
|
* of derived works with some common examples. Other people may interpret *
|
|
* the plain GPL differently, so we consider this a special exception to *
|
|
* the GPL that we apply to Covered Software. Works which meet any of *
|
|
* these conditions must conform to all of the terms of this license, *
|
|
* particularly including the GPL Section 3 requirements of providing *
|
|
* source code and allowing free redistribution of the work as a whole. *
|
|
* *
|
|
* As another special exception to the GPL terms, Insecure.Com LLC grants *
|
|
* permission to link the code of this program with any version of the *
|
|
* OpenSSL library which is distributed under a license identical to that *
|
|
* listed in the included docs/licenses/OpenSSL.txt file, and distribute *
|
|
* linked combinations including the two. *
|
|
* *
|
|
* Any redistribution of Covered Software, including any derived works, *
|
|
* must obey and carry forward all of the terms of this license, including *
|
|
* obeying all GPL rules and restrictions. For example, source code of *
|
|
* the whole work must be provided and free redistribution must be *
|
|
* allowed. All GPL references to "this License", are to be treated as *
|
|
* including the terms and conditions of this license text as well. *
|
|
* *
|
|
* Because this license imposes special exceptions to the GPL, Covered *
|
|
* Work may not be combined (even as part of a larger work) with plain GPL *
|
|
* software. The terms, conditions, and exceptions of this license must *
|
|
* be included as well. This license is incompatible with some other open *
|
|
* source licenses as well. In some cases we can relicense portions of *
|
|
* Nmap or grant special permissions to use it in other open source *
|
|
* software. Please contact fyodor@nmap.org with any such requests. *
|
|
* Similarly, we don't incorporate incompatible open source software into *
|
|
* Covered Software without special permission from the copyright holders. *
|
|
* *
|
|
* If you have any questions about the licensing restrictions on using *
|
|
* Nmap in other works, are happy to help. As mentioned above, we also *
|
|
* offer alternative license to integrate Nmap into proprietary *
|
|
* applications and appliances. These contracts have been sold to dozens *
|
|
* of software vendors, and generally include a perpetual license as well *
|
|
* as providing for priority support and updates. They also fund the *
|
|
* continued development of Nmap. Please email sales@nmap.com for further *
|
|
* information. *
|
|
* *
|
|
* If you have received a written license agreement or contract for *
|
|
* Covered Software stating terms other than these, you may choose to use *
|
|
* and redistribute Covered Software under those terms instead of these. *
|
|
* *
|
|
* Source is provided to this software because we believe users have a *
|
|
* right to know exactly what a program is going to do before they run it. *
|
|
* This also allows you to audit the software for security holes. *
|
|
* *
|
|
* Source code also allows you to port Nmap to new platforms, fix bugs, *
|
|
* and add new features. You are highly encouraged to send your changes *
|
|
* to the dev@nmap.org mailing list for possible incorporation into the *
|
|
* main distribution. By sending these changes to Fyodor or one of the *
|
|
* Insecure.Org development mailing lists, or checking them into the Nmap *
|
|
* source code repository, it is understood (unless you specify otherwise) *
|
|
* that you are offering the Nmap Project (Insecure.Com LLC) the *
|
|
* unlimited, non-exclusive right to reuse, modify, and relicense the *
|
|
* code. Nmap will always be available Open Source, but this is important *
|
|
* because the inability to relicense code has caused devastating problems *
|
|
* for other Free Software projects (such as KDE and NASM). We also *
|
|
* occasionally relicense the code to third parties as discussed above. *
|
|
* If you wish to specify special license conditions of your *
|
|
* contributions, just say so when you send them. *
|
|
* *
|
|
* 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 Nmap *
|
|
* license file for more details (it's in a COPYING file included with *
|
|
* Nmap, and also available from https://svn.nmap.org/nmap/COPYING) *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include "nping.h"
|
|
#include "EchoClient.h"
|
|
#include "EchoHeader.h"
|
|
#include "output.h"
|
|
#include "NEPContext.h"
|
|
#include "NpingOps.h"
|
|
#include "nsock.h"
|
|
#include "Crypto.h"
|
|
|
|
extern NpingOps o;
|
|
extern EchoClient ec;
|
|
|
|
|
|
EchoClient::EchoClient() {
|
|
this->reset();
|
|
} /* End of EchoClient constructor */
|
|
|
|
|
|
EchoClient::~EchoClient() {
|
|
this->reset();
|
|
} /* End of EchoClient destructor */
|
|
|
|
|
|
/** Sets every attribute to its default value- */
|
|
void EchoClient::reset() {
|
|
memset(&this->srvaddr4, 0, sizeof(struct sockaddr_in));
|
|
memset(&this->srvaddr6, 0, sizeof(struct sockaddr_in6));
|
|
memset(this->lasthdr, 0, MAX_NEP_PACKET_LENGTH);
|
|
this->readbytes=0;
|
|
this->af=AF_INET;
|
|
} /* End of reset() */
|
|
|
|
|
|
/** Closes current connection and destroys Nsock handlers */
|
|
int EchoClient::cleanup(){
|
|
this->probe.cleanup();
|
|
return OP_SUCCESS;
|
|
} /* End of cleanup() */
|
|
|
|
|
|
/** This is the main method, the boss of it all. It sets up nsock, establishes
|
|
* a TCP connection with the server, performs the NEP authentication handshake,
|
|
* sends the appropriate packet specs and handles raw packet transmission and
|
|
* NEP_ECHO reception and display. */
|
|
int EchoClient::start(NpingTarget *target, u16 port){
|
|
nping_print(DBG_4, "%s(%p, %u)", __func__, target, port);
|
|
|
|
/* Init Nsock in the probe engine */
|
|
if( this->probe.init_nsock() != OP_SUCCESS ){
|
|
nping_warning(QT_2, "Couldn't initialize Nsock.");
|
|
return OP_FAILURE;
|
|
}else{
|
|
/* Extract the nsock pool handler and store it here */
|
|
this->nsp=this->probe.getNsockPool();
|
|
this->nsi=nsock_iod_new(this->nsp, NULL);
|
|
}
|
|
|
|
/* Schedule a TCP connection attempt */
|
|
if( this->nep_connect(target, port) != OP_SUCCESS ){
|
|
nping_warning(QT_2, "Connection failed.");
|
|
return OP_FAILURE;
|
|
}
|
|
|
|
/* Perform NEP authentication handshake */
|
|
if( this->nep_handshake() != OP_SUCCESS ){
|
|
nping_warning(QT_2, "Handshake failed.");
|
|
return OP_FAILURE;
|
|
}
|
|
|
|
/* Send packet specification */
|
|
if( this->nep_send_packet_spec() != OP_SUCCESS ){
|
|
nping_warning(QT_2, "Couldn't send packet specification.");
|
|
return OP_FAILURE;
|
|
}
|
|
|
|
/* Wait for confirmation */
|
|
if( this->nep_recv_ready() != OP_SUCCESS ){
|
|
nping_warning(QT_2, "Didn't receive server's OK.");
|
|
return OP_FAILURE;
|
|
}
|
|
|
|
/* Schedule read of the first 16 bytes to determine the full packet length */
|
|
nsock_readbytes(this->nsp, this->nsi, recv_std_header_handler, NSOCK_INFINITE, NULL, STD_NEP_HEADER_LEN);
|
|
|
|
/* Start the probe mode engine */
|
|
probe.start();
|
|
|
|
return OP_SUCCESS;
|
|
} /* End of start() */
|
|
|
|
|
|
/** Attempts to establish a TCP connection to "target:port". On success it
|
|
* returns OP_SUCCESS. OP_FAILURE is returned when it was impossible to
|
|
* connect to the remote host (this can be because the server rejected the
|
|
* connection or because the connect() timed out). */
|
|
int EchoClient::nep_connect(NpingTarget *target, u16 port){
|
|
nping_print(DBG_4, "%s(%p, %u)", __func__, target, port);
|
|
struct sockaddr_storage ss;
|
|
struct sockaddr_storage src;
|
|
size_t ss_len;
|
|
struct sockaddr_in *s4=(struct sockaddr_in *)&ss;
|
|
struct sockaddr_in6 *s6=(struct sockaddr_in6 *)&ss;
|
|
enum nsock_loopstatus loopstatus;
|
|
|
|
if(target==NULL)
|
|
nping_fatal(QT_3, "nep_connect(): NULL parameter supplied.");
|
|
else
|
|
target->getTargetSockAddr(&ss, &ss_len);
|
|
|
|
/* AF_INET6 */
|
|
if( s6->sin6_family==AF_INET6 ){
|
|
this->af=AF_INET6;
|
|
this->srvaddr6.sin6_family = AF_INET6;
|
|
this->srvaddr6.sin6_port = htons(port);
|
|
this->srvaddr6.sin6_addr = s6->sin6_addr;
|
|
this->srvaddr6.sin6_flowinfo = 0;
|
|
#ifdef HAVE_SOCKADDR_IN6_SIN6_LEN
|
|
this->srvaddr6.sin6_len = sizeof(struct sockaddr_in6);
|
|
#endif
|
|
|
|
/* Try to bind the IOD to the IP address supplied by the user */
|
|
nsock_iod_set_localaddr(this->nsi, o.getSourceSockAddr(&src), sizeof(sockaddr_in6));
|
|
|
|
/* Schedule a connect event */
|
|
nsock_connect_tcp(this->nsp, this->nsi, connect_done_handler, ECHO_CONNECT_TIMEOUT,
|
|
NULL, (struct sockaddr *) &this->srvaddr6, sizeof(this->srvaddr6), port);
|
|
|
|
/* AF_INET */
|
|
}else{
|
|
this->af=AF_INET;
|
|
this->srvaddr4.sin_family = AF_INET;
|
|
this->srvaddr4.sin_port = htons(port);
|
|
this->srvaddr4.sin_addr = s4->sin_addr;
|
|
#ifdef HAVE_SOCKADDR_IN_SIN_LEN
|
|
this->srvaddr4.sin_len = sizeof(struct sockaddr_in);
|
|
#endif
|
|
|
|
/* Try to bind the IOD to the IP address supplied by the user */
|
|
nsock_iod_set_localaddr(this->nsi, o.getSourceSockAddr(&src), sizeof(sockaddr_in));
|
|
|
|
/* Schedule a connect event */
|
|
nsock_connect_tcp(this->nsp, this->nsi, connect_done_handler, ECHO_CONNECT_TIMEOUT,
|
|
NULL, (struct sockaddr *) &this->srvaddr4, sizeof(this->srvaddr4), port);
|
|
|
|
}
|
|
/* Try to connect or timeout */
|
|
loopstatus=nsock_loop(this->nsp, ECHO_CONNECT_TIMEOUT-1);
|
|
/* If nsock tells us that the handler asked to quit the loop, then the connect was successful */
|
|
return (loopstatus==NSOCK_LOOP_QUIT) ? OP_SUCCESS : OP_FAILURE;
|
|
} /* End of nep_connect() */
|
|
|
|
|
|
/** Attempts to perform the NEP authentication handshake with the server.
|
|
* Returns OP_SUCCESS if the authentication went well and OP_FAILURE otherwise */
|
|
int EchoClient::nep_handshake(){
|
|
nping_print(DBG_4, "%s()", __func__);
|
|
enum nsock_loopstatus loopstatus;
|
|
EchoHeader h;
|
|
|
|
/* Receive NEP_HANDSHAKE_SERVER message */
|
|
nsock_readbytes(this->nsp, this->nsi, recv_hs_server_handler, ECHO_READ_TIMEOUT, NULL, NEP_HANDSHAKE_SERVER_LEN);
|
|
loopstatus=nsock_loop(this->nsp, ECHO_READ_TIMEOUT-1);
|
|
if(loopstatus!=NSOCK_LOOP_QUIT)
|
|
return OP_FAILURE;
|
|
|
|
/* Generate client nonces and the session cryptographic keys*/
|
|
this->ctx.generateInitialClientSequence();
|
|
this->ctx.generateClientNonce();
|
|
this->ctx.generateCipherKeyC2S();
|
|
this->ctx.generateCipherKeyS2C();
|
|
this->ctx.generateMacKeyC2S();
|
|
this->ctx.generateMacKeyS2C();
|
|
|
|
nping_print(DBG_4,"Session Key MAC_C2S:"); print_hexdump(DBG_4,ctx.getMacKeyC2S(), MAC_KEY_LEN);
|
|
nping_print(DBG_4,"Session Key MAC_S2C:"); print_hexdump(DBG_4,ctx.getMacKeyS2C(), MAC_KEY_LEN);
|
|
nping_print(DBG_4,"Session Key CIPHER_C2S:"); print_hexdump(DBG_4,ctx.getCipherKeyC2S(), MAC_KEY_LEN);
|
|
nping_print(DBG_4,"Session Key CIPHER_S2C:"); print_hexdump(DBG_4,ctx.getCipherKeyS2C(), MAC_KEY_LEN);
|
|
|
|
|
|
/* Send NEP_HANDSHAKE_CLIENT message */
|
|
if( this->generate_hs_client(&h)!=OP_SUCCESS )
|
|
return OP_FAILURE;
|
|
nsock_write(this->nsp, this->nsi, write_done_handler, ECHO_WRITE_TIMEOUT, NULL, (char *)h.getBinaryBuffer(), h.getLen());
|
|
loopstatus=nsock_loop(this->nsp, ECHO_WRITE_TIMEOUT-1);
|
|
if(loopstatus!=NSOCK_LOOP_QUIT)
|
|
return OP_FAILURE;
|
|
|
|
/* Receive NEP_HANDSHAKE_FINAL message */
|
|
nsock_readbytes(this->nsp, this->nsi, recv_hs_final_handler, ECHO_READ_TIMEOUT, NULL, NEP_HANDSHAKE_FINAL_LEN);
|
|
loopstatus=nsock_loop(this->nsp, ECHO_READ_TIMEOUT-1);
|
|
if(loopstatus!=NSOCK_LOOP_QUIT)
|
|
return OP_FAILURE;
|
|
|
|
nping_print(DBG_1, "===NEP Handshake completed successfully===");
|
|
return OP_SUCCESS;
|
|
} /* End of nep_handshake() */
|
|
|
|
|
|
/** Sends the appropriate NEP_PACKET_SPEC message to the server. Returns
|
|
* OP_SUCCESS on success and OP_FAILURE in case of error. */
|
|
int EchoClient::nep_send_packet_spec(){
|
|
nping_print(DBG_4, "%s()", __func__);
|
|
enum nsock_loopstatus loopstatus;
|
|
EchoHeader h;
|
|
|
|
if (this->generate_packet_spec(&h)!=OP_SUCCESS)
|
|
return OP_FAILURE;
|
|
|
|
/* Send NEP_PACKET_SPEC message */
|
|
nsock_write(this->nsp, this->nsi, write_done_handler, ECHO_WRITE_TIMEOUT, NULL, (const char*)h.getBinaryBuffer(), h.getLen());
|
|
loopstatus=nsock_loop(this->nsp, ECHO_WRITE_TIMEOUT-1);
|
|
if(loopstatus!=NSOCK_LOOP_QUIT)
|
|
return OP_FAILURE;
|
|
else
|
|
return OP_SUCCESS;
|
|
} /* End of nep_send_packetspec() */
|
|
|
|
|
|
/** Receives and parses a NEP_READY message from the server. Returns OP_SUCCESS
|
|
* on success and OP_FAILURE in case of error. */
|
|
int EchoClient::nep_recv_ready(){
|
|
nping_print(DBG_4, "%s()", __func__);
|
|
enum nsock_loopstatus loopstatus;
|
|
/* Receive NEP_READY message */
|
|
nsock_readbytes(this->nsp, this->nsi, recv_ready_handler, ECHO_READ_TIMEOUT, NULL, NEP_READY_LEN);
|
|
loopstatus=nsock_loop(this->nsp, ECHO_READ_TIMEOUT-1);
|
|
if(loopstatus!=NSOCK_LOOP_QUIT)
|
|
return OP_FAILURE;
|
|
else
|
|
return OP_SUCCESS;
|
|
} /* End of nep_recv_ready(){ */
|
|
|
|
|
|
/** Reads and parses a NEP_ECHO message from the server. Returns OP_SUCCESS
|
|
* on success and OP_FAILURE in case of error. */
|
|
int EchoClient::nep_recv_echo(u8 *packet, size_t packetlen){
|
|
nping_print(DBG_4, "%s(%p, %lu)", __func__, packet, (unsigned long)packetlen);
|
|
EchoHeader pkt_in;
|
|
char *delayedstr=NULL;
|
|
nsock_event_id ev_id;
|
|
u8 *pkt=NULL;
|
|
u16 pktlen=0;
|
|
u8 pktinfobuffer[512+1];
|
|
struct timeval *t = (struct timeval *)nsock_gettimeofday();
|
|
memset(pktinfobuffer, 0, sizeof(pktinfobuffer));
|
|
|
|
/* Verify the received packet (this covers authentication etc) */
|
|
if(this->parse_echo(packet, packetlen)!=OP_SUCCESS){
|
|
return OP_FAILURE;
|
|
}
|
|
|
|
/* Once we have authenticated the received message, extract the echoed packet */
|
|
if(pkt_in.storeRecvData(packet, packetlen)==OP_FAILURE){
|
|
nping_print(VB_0, "Unexpected error dealing with the NEP_ECHO message,");
|
|
return OP_FAILURE;
|
|
}
|
|
if((pkt=pkt_in.getEchoedPacket(&pktlen))==NULL){
|
|
nping_print(VB_0, "Error displaying received NEP_ECHO message)");
|
|
return OP_FAILURE;
|
|
}
|
|
o.stats.addEchoedPacket(pktlen);
|
|
|
|
/* Guess the time the packet was captured. We do this computing the RTT
|
|
* between the last sent packet and the received echo packet. We assume
|
|
* the packet was captured RTT/2 seconds ago. */
|
|
struct timeval tmp=o.getLastPacketSentTime();
|
|
float sent_time = o.stats.elapsedRuntime(&tmp);
|
|
float now_time = o.stats.elapsedRuntime(t);
|
|
float rtt = now_time - sent_time;
|
|
float final_time = sent_time + rtt/2;
|
|
|
|
/* @todo: compute the link layer offset from the DLT type and discard
|
|
* link layer headers */
|
|
getPacketStrInfo("IP", pkt, pktlen, pktinfobuffer, 512);
|
|
nping_print(VB_0,"CAPT (%.4fs) %s", final_time, pktinfobuffer );
|
|
if( o.getVerbosity() >= VB_3)
|
|
luis_hdump((char*)pkt, pktlen);
|
|
|
|
/* Check if there is a delayed RCVD string that is waiting to be printed */
|
|
if( (delayedstr=o.getDelayedRcvd(&ev_id))!=NULL ){
|
|
printf("%s", delayedstr);
|
|
free(delayedstr);
|
|
nsock_event_cancel(this->nsp, ev_id, 0);
|
|
}
|
|
return OP_SUCCESS;
|
|
} /* End of nep_recv_echo() */
|
|
|
|
|
|
/** Processes and validates a received NEP_HANDSHAKE_SERVER message. On success
|
|
* it returns OP_SUCCESS. OP_FAILURE is returned in case the received packet
|
|
* is not valid. */
|
|
int EchoClient::parse_hs_server(u8 *pkt, size_t pktlen){
|
|
nping_print(DBG_4, "%s()", __func__);
|
|
EchoHeader h;
|
|
if(pkt==NULL){
|
|
nping_print(DBG_1,"%s(): NULL parameter supplied.", __func__ );
|
|
return OP_FAILURE;
|
|
}
|
|
if(pktlen!=NEP_HANDSHAKE_SERVER_LEN){
|
|
nping_print(DBG_1,"%s(): Unexpected length supplied.", __func__ );
|
|
return OP_FAILURE;
|
|
}
|
|
h.storeRecvData(pkt, pktlen);
|
|
|
|
/* Validate version number */
|
|
if( h.getVersion() != ECHO_CURRENT_PROTO_VER ){
|
|
nping_print(DBG_1, "Expected NEP version %02x but message used %02x", ECHO_CURRENT_PROTO_VER, h.getVersion() );
|
|
return OP_FAILURE;
|
|
}
|
|
|
|
/* Ensure the expected message type was received */
|
|
if(h.getMessageType()!=TYPE_NEP_HANDSHAKE_SERVER){
|
|
nping_print(DBG_1, "Expected NEP_HANDSHAKE_SERVER but received %02X", h.getMessageType() );
|
|
return OP_FAILURE;
|
|
}
|
|
|
|
/* Ensure the received timestamp falls into the allowed time window */
|
|
//if( h.verifyTimestamp()!=OP_SUCCESS ){
|
|
// nping_print(DBG_1, "NEP_HANDSHAKE_SERVER timestamp is too old", h.getMessageType() );
|
|
// return OP_FAILURE;
|
|
//}
|
|
|
|
/* Ensure message length is correct */
|
|
if( h.getTotalLength()!=(NEP_HANDSHAKE_SERVER_LEN/4)){
|
|
nping_print(DBG_1, "Received NEP_HANDSHAKE_SERVER specifies an incorrect length (%u)", h.getTotalLength()*4 );
|
|
return OP_FAILURE;
|
|
}
|
|
|
|
/* Check the authenticity of the received message */
|
|
this->ctx.setServerNonce(h.getServerNonce());
|
|
this->ctx.generateMacKeyS2CInitial();
|
|
if( h.verifyMessageAuthenticationCode(this->ctx.getMacKeyS2C(), MAC_KEY_LEN )!=OP_SUCCESS ){
|
|
nping_print(DBG_1, "NEP_HANDSHAKE_SERVER authentication failed" );
|
|
return OP_FAILURE;
|
|
}
|
|
this->ctx.setLastServerSequence( h.getSequenceNumber() );
|
|
return OP_SUCCESS;
|
|
} /* End of parse_hs_server() */
|
|
|
|
|
|
/** Processes and validates a received NEP_HANDSHAKE_FINAL message. On success
|
|
* it returns OP_SUCCESS. OP_FAILURE is returned in case the received packet
|
|
* is not valid. */
|
|
int EchoClient::parse_hs_final(u8 *pkt, size_t pktlen){
|
|
nping_print(DBG_4, "%s()", __func__);
|
|
EchoHeader h;
|
|
u8 *next_iv=NULL;
|
|
if(pkt==NULL){
|
|
nping_print(DBG_1,"%s(): NULL parameter supplied.", __func__ );
|
|
return OP_FAILURE;
|
|
}
|
|
if(pktlen!=NEP_HANDSHAKE_FINAL_LEN){
|
|
nping_print(DBG_1,"%s(): Unexpected length supplied.", __func__ );
|
|
return OP_FAILURE;
|
|
}
|
|
h.storeRecvData(pkt, pktlen);
|
|
|
|
/* Validate version number */
|
|
if( h.getVersion() != ECHO_CURRENT_PROTO_VER ){
|
|
nping_print(DBG_1, "Expected NEP version %02x but message used %02x", ECHO_CURRENT_PROTO_VER, h.getVersion() );
|
|
return OP_FAILURE;
|
|
}
|
|
|
|
/* Ensure the expected message type was received */
|
|
if(h.getMessageType()!=TYPE_NEP_HANDSHAKE_FINAL){
|
|
nping_print(DBG_1, "Expected NEP_HANDSHAKE_FINAL but received %02X", h.getMessageType() );
|
|
return OP_FAILURE;
|
|
}
|
|
|
|
/* Ensure the received sequence number is the previous+1 */
|
|
if( h.getSequenceNumber()!=(this->ctx.getLastServerSequence()+1)){
|
|
nping_print(DBG_1, "Expected sequence number %d but received %d", this->ctx.getLastServerSequence()+1, h.getSequenceNumber() );
|
|
return OP_FAILURE;
|
|
}else{
|
|
/* Increment next expected sequence number*/
|
|
this->ctx.getNextServerSequence();
|
|
}
|
|
|
|
/* Ensure the received timestamp falls into the allowed time window */
|
|
//if( h.verifyTimestamp()!=OP_SUCCESS ){
|
|
// nping_print(DBG_1, "NEP_HANDSHAKE_FINAL timestamp is too old", h.getMessageType() );
|
|
// return OP_FAILURE;
|
|
//}
|
|
|
|
/* Ensure message length is correct */
|
|
if( h.getTotalLength()!=(NEP_HANDSHAKE_FINAL_LEN/4)){
|
|
nping_print(DBG_1, "Received NEP_HANDSHAKE_FINAL specifies an incorrect length (%u)", h.getTotalLength()*4 );
|
|
return OP_FAILURE;
|
|
}
|
|
|
|
/* Ensure the server echoed the nonce we sent in our NEP_HANDSHAKE_CLIENT */
|
|
if( memcmp(h.getClientNonce(), this->ctx.getClientNonce(), NONCE_LEN)!=0 ){
|
|
nping_print(DBG_1, "Echoed nonce in NEP_HANDSHAKE_FINAL message does not match client generate nonce");
|
|
return OP_FAILURE;
|
|
}
|
|
|
|
/* Decrypt the encrypted part of the message before validating the MAC */
|
|
if((next_iv=h.decrypt(this->ctx.getCipherKeyS2C(), CIPHER_KEY_LEN, this->ctx.getServerNonce(), TYPE_NEP_HANDSHAKE_FINAL))==NULL){
|
|
nping_print(DBG_1, "Failed to decrypt NEP_HANDSHAKE_FINAL data." );
|
|
return OP_FAILURE;
|
|
}
|
|
this->ctx.setNextDecryptionIV(next_iv);
|
|
|
|
/* Check the authenticity of the received message */
|
|
if( h.verifyMessageAuthenticationCode(this->ctx.getMacKeyS2C(), MAC_KEY_LEN )!=OP_SUCCESS ){
|
|
nping_print(DBG_1, "NEP_HANDSHAKE_FINAL authentication failed" );
|
|
return OP_FAILURE;
|
|
}
|
|
|
|
return OP_SUCCESS;
|
|
} /* End of parse_hs_final() */
|
|
|
|
|
|
/** Processes and validates a received NEP_READY message. On success
|
|
* it returns OP_SUCCESS. OP_FAILURE is returned in case the received packet
|
|
* is not valid. */
|
|
int EchoClient::parse_ready(u8 *pkt, size_t pktlen){
|
|
nping_print(DBG_4, "%s()", __func__);
|
|
EchoHeader h;
|
|
u8 *next_iv=NULL;
|
|
if(pkt==NULL){
|
|
nping_print(DBG_1,"%s(): NULL parameter supplied.", __func__ );
|
|
return OP_FAILURE;
|
|
}
|
|
if(pktlen!=NEP_READY_LEN){
|
|
nping_print(DBG_1,"%s(): Unexpected length supplied.", __func__ );
|
|
return OP_FAILURE;
|
|
}
|
|
h.storeRecvData(pkt, pktlen);
|
|
|
|
/* Decrypt message */
|
|
if((next_iv=h.decrypt(this->ctx.getCipherKeyS2C(), CIPHER_KEY_LEN, this->ctx.getNextDecryptionIV(), TYPE_NEP_READY))==NULL){
|
|
nping_print(DBG_1, "Failed to decrypt NEP_READY data." );
|
|
return OP_FAILURE;
|
|
}
|
|
this->ctx.setNextDecryptionIV(next_iv);
|
|
|
|
/* Validate version number */
|
|
if( h.getVersion() != ECHO_CURRENT_PROTO_VER ){
|
|
nping_print(DBG_1, "Expected NEP version %02x but message used %02x", ECHO_CURRENT_PROTO_VER, h.getVersion() );
|
|
return OP_FAILURE;
|
|
}
|
|
|
|
/* Ensure the expected message type was received */
|
|
if(h.getMessageType()!=TYPE_NEP_READY){
|
|
nping_print(DBG_1, "Expected NEP_READY but received %02X", h.getMessageType() );
|
|
return OP_FAILURE;
|
|
}
|
|
|
|
/* Ensure the received sequence number is the previous+1 */
|
|
if( h.getSequenceNumber()!=(this->ctx.getLastServerSequence()+1)){
|
|
nping_print(DBG_1, "Expected sequence number %d but received %d", this->ctx.getLastServerSequence()+1, h.getSequenceNumber() );
|
|
return OP_FAILURE;
|
|
}else{
|
|
/* Increment next expected sequence number*/
|
|
this->ctx.getNextServerSequence();
|
|
}
|
|
|
|
/* Ensure the received timestamp falls into the allowed time window */
|
|
//if( h.verifyTimestamp()!=OP_SUCCESS ){
|
|
// nping_print(DBG_1, "NEP_READY timestamp is too old", h.getMessageType() );
|
|
// return OP_FAILURE;
|
|
//}
|
|
|
|
/* Ensure message length is correct */
|
|
if( h.getTotalLength()!=(NEP_READY_LEN/4)){
|
|
nping_print(DBG_1, "Received NEP_READY specifies an incorrect length (%u)", h.getTotalLength()*4 );
|
|
return OP_FAILURE;
|
|
}
|
|
|
|
/* Check the authenticity of the received message */
|
|
if( h.verifyMessageAuthenticationCode(this->ctx.getMacKeyS2C(), MAC_KEY_LEN )!=OP_SUCCESS ){
|
|
nping_print(DBG_1, "NEP_READY authentication failed" );
|
|
return OP_FAILURE;
|
|
}
|
|
|
|
return OP_SUCCESS;
|
|
} /* End of parse_hs_final() */
|
|
|
|
|
|
/** Processes and validates a received NEP_ECHO message. On success
|
|
* it returns OP_SUCCESS. OP_FAILURE is returned in case the received packet
|
|
* is not valid. */
|
|
int EchoClient::parse_echo(u8 *pkt, size_t pktlen){
|
|
nping_print(DBG_4, "%s()", __func__);
|
|
EchoHeader h;
|
|
u8 *next_iv=NULL;
|
|
if(pkt==NULL){
|
|
nping_print(DBG_1,"%s(): NULL parameter supplied.", __func__ );
|
|
return OP_FAILURE;
|
|
}
|
|
if(pktlen<NEP_ECHO_MIN_LEN){
|
|
nping_print(DBG_1,"%s(): Unexpected length supplied.", __func__ );
|
|
return OP_FAILURE;
|
|
}
|
|
h.storeRecvData(pkt, pktlen);
|
|
|
|
/* Decrypt message */
|
|
if((next_iv=h.decrypt(this->ctx.getCipherKeyS2C(), CIPHER_KEY_LEN, this->ctx.getNextDecryptionIV(), TYPE_NEP_ECHO))==NULL){
|
|
nping_print(DBG_1, "Failed to decrypt NEP_ECHO data." );
|
|
return OP_FAILURE;
|
|
}
|
|
this->ctx.setNextDecryptionIV(next_iv);
|
|
|
|
/* Validate version number */
|
|
if( h.getVersion() != ECHO_CURRENT_PROTO_VER ){
|
|
nping_print(DBG_1, "Expected NEP version %02x but message used %02x", ECHO_CURRENT_PROTO_VER, h.getVersion() );
|
|
return OP_FAILURE;
|
|
}
|
|
|
|
/* Ensure the expected message type was received */
|
|
if(h.getMessageType()!=TYPE_NEP_ECHO){
|
|
nping_print(DBG_1, "Expected NEP_ECHO but received %02X", h.getMessageType() );
|
|
return OP_FAILURE;
|
|
}
|
|
|
|
/* Ensure the received sequence number is the previous+1 */
|
|
if( h.getSequenceNumber()!=(this->ctx.getLastServerSequence()+1)){
|
|
nping_print(DBG_1, "Expected sequence number %d but received %d", this->ctx.getLastServerSequence()+1, h.getSequenceNumber() );
|
|
return OP_FAILURE;
|
|
}else{
|
|
/* Increment next expected sequence number*/
|
|
this->ctx.getNextServerSequence();
|
|
}
|
|
|
|
/* Ensure the received timestamp falls into the allowed time window */
|
|
//if( h.verifyTimestamp()!=OP_SUCCESS ){
|
|
// nping_print(DBG_1, "NEP_ECHO timestamp is too old", h.getMessageType() );
|
|
// return OP_FAILURE;
|
|
//}
|
|
|
|
// /* Ensure message length is correct */
|
|
// if( h.getTotalLength()!=(pktlen/4)){
|
|
// nping_print(DBG_1, "Received NEP_ECHO specifies an incorrect length (%u)", h.getTotalLength()*4 );
|
|
// return OP_FAILURE;
|
|
// }
|
|
|
|
/* Fix the object's internal state, since the ECHO message was not created
|
|
* by the object but from received data. */
|
|
h.updateEchoInternals();
|
|
|
|
/* Check the authenticity of the received message */
|
|
if( h.verifyMessageAuthenticationCode(this->ctx.getMacKeyS2C(), MAC_KEY_LEN )!=OP_SUCCESS ){
|
|
nping_print(DBG_1, "NEP_ECHO authentication failed" );
|
|
return OP_FAILURE;
|
|
}else{
|
|
nping_print(DBG_1, "Received NEP_ECHO was authenticated successfully");
|
|
}
|
|
|
|
/* Overwrite the received buffer with the decrypted data */
|
|
h.dumpToBinaryBuffer(pkt, pktlen);
|
|
|
|
return OP_SUCCESS;
|
|
} /* End of parse_hs_final() */
|
|
|
|
|
|
/** Processes and validates a received NEP_ERROR message. On success
|
|
* it returns OP_SUCCESS. OP_FAILURE is returned in case the received packet
|
|
* is not valid. */
|
|
int EchoClient::parse_error(u8 *pkt, size_t pktlen){
|
|
nping_print(DBG_4, "%s()", __func__);
|
|
return OP_SUCCESS;
|
|
} /* End of parse_hs_final() */
|
|
|
|
|
|
/** Generates a NEP_HANDSHAKE_CLIENT message. On success it returns OP_SUCCESS.
|
|
* OP_FAILURE is returned in case of error. */
|
|
int EchoClient::generate_hs_client(EchoHeader *h){
|
|
nping_print(DBG_4, "%s()", __func__);
|
|
u8 *next_iv=NULL;
|
|
if(h==NULL)
|
|
return OP_FAILURE;
|
|
|
|
/* Craft NEP_HANDSHAKE_CLIENT message */
|
|
h->setMessageType(TYPE_NEP_HANDSHAKE_CLIENT);
|
|
h->setSequenceNumber( this->ctx.getLastClientSequence() );
|
|
h->setTimestamp();
|
|
h->setServerNonce( this->ctx.getServerNonce() );
|
|
h->setClientNonce( this->ctx.getClientNonce() );
|
|
if(this->af==AF_INET6){
|
|
h->setPartnerAddress(this->srvaddr6.sin6_addr);
|
|
}else{
|
|
h->setPartnerAddress(this->srvaddr4.sin_addr);
|
|
}
|
|
h->setTotalLength();
|
|
h->setMessageAuthenticationCode( this->ctx.getMacKeyC2S(), MAC_KEY_LEN);
|
|
|
|
if( (next_iv=h->encrypt(this->ctx.getCipherKeyC2S(), CIPHER_KEY_LEN, this->ctx.getClientNonce()))==NULL )
|
|
return OP_FAILURE;
|
|
this->ctx.setNextEncryptionIV(next_iv);
|
|
|
|
return OP_SUCCESS;
|
|
} /* End of generate_hs_client() */
|
|
|
|
/** Generates a NEP_PACKET_SPEC message. On success it returns OP_SUCCESS.
|
|
* OP_FAILURE is returned in case of error. */
|
|
int EchoClient::generate_packet_spec(EchoHeader *h){
|
|
nping_print(DBG_4, "%s()", __func__);
|
|
int ports=-1;
|
|
u8 nxthdr=0;
|
|
u8 aux8=0;
|
|
u16 aux16=0;
|
|
u16 *p16=NULL;
|
|
u32 aux32=0;
|
|
u8 *next_iv=NULL;
|
|
|
|
if(h==NULL)
|
|
return OP_FAILURE;
|
|
|
|
h->setMessageType(TYPE_NEP_PACKET_SPEC);
|
|
h->setSequenceNumber( this->ctx.getNextClientSequence() );
|
|
h->setTimestamp();
|
|
h->setIPVersion( o.getIPVersion()==AF_INET6 ? 0x06: 0x04 );
|
|
h->setPacketCount( (o.getPacketCount()>0xFFFF) ? 0xFFFF : o.getPacketCount() );
|
|
|
|
/** Insert packet field specifiers */
|
|
if(o.ipv6()){ /* AF_INET6 */
|
|
/* Traffic class */
|
|
aux8=o.getTrafficClass();
|
|
h->addFieldSpec(PSPEC_IPv6_TCLASS, (u8*)&aux8);
|
|
/* Flow label */
|
|
aux32=htonl(o.getFlowLabel());
|
|
h->addFieldSpec(PSPEC_IPv6_FLOW, (u8*)&aux32);
|
|
}else{ /* AF_INET */
|
|
/* IP Identification */
|
|
aux16=htons(o.getIdentification());
|
|
h->addFieldSpec(PSPEC_IPv4_ID, (u8*)&aux16);
|
|
/* Type of Service */
|
|
aux8=o.getTOS();
|
|
h->addFieldSpec(PSPEC_IPv4_TOS, (u8*)&aux8);
|
|
/* Fragment Offset */
|
|
/** @todo Implement this. Nping does not currently offer --fragoff */
|
|
}
|
|
|
|
switch( o.getMode() ){
|
|
|
|
case TCP:
|
|
nxthdr=6;
|
|
h->setProtocol(PSPEC_PROTO_TCP);
|
|
/* Source TCP Port */
|
|
aux16=htons(o.getSourcePort());
|
|
h->addFieldSpec(PSPEC_TCP_SPORT, (u8*)&aux16);
|
|
/* Destination TCP Port */
|
|
if( (p16=o.getTargetPorts(&ports))!=NULL && ports==1 ){
|
|
aux16=htons(*p16);
|
|
h->addFieldSpec(PSPEC_TCP_DPORT, (u8*)&aux16);
|
|
}
|
|
/* Sequence number */
|
|
aux32=htonl(o.getTCPSequence());
|
|
h->addFieldSpec(PSPEC_TCP_SEQ, (u8*)&aux32);
|
|
/* Acknowledgment */
|
|
aux32=htonl(o.getTCPAck());
|
|
h->addFieldSpec(PSPEC_TCP_ACK, (u8*)&aux32);
|
|
/* Flags */
|
|
aux8=o.getTCPFlags();
|
|
h->addFieldSpec(PSPEC_TCP_FLAGS, (u8*)&aux8);
|
|
/* Window size */
|
|
aux16=htons(o.getTCPWindow());
|
|
h->addFieldSpec(PSPEC_TCP_WIN, (u8*)&aux16);
|
|
/* Urgent pointer */
|
|
/** @todo Implement this. Nping does not currently offer --urp */
|
|
break;
|
|
|
|
case UDP:
|
|
nxthdr=17;
|
|
h->setProtocol(PSPEC_PROTO_UDP);
|
|
/* Source UDP Port */
|
|
aux16=htons(o.getSourcePort());
|
|
h->addFieldSpec(PSPEC_UDP_SPORT, (u8*)&aux16);
|
|
/* Destination TCP Port */
|
|
if( (p16=o.getTargetPorts(&ports))!=NULL && ports==1 ){
|
|
aux16=htons(*p16);
|
|
h->addFieldSpec(PSPEC_UDP_DPORT, (u8*)&aux16);
|
|
}
|
|
/* Packet length */
|
|
aux16=htons(8+o.getPayloadLen());
|
|
h->addFieldSpec(PSPEC_UDP_LEN, (u8*)&aux16);
|
|
break;
|
|
|
|
case ICMP:
|
|
nxthdr=1;
|
|
h->setProtocol(PSPEC_PROTO_ICMP);
|
|
aux8=o.getICMPType();
|
|
h->addFieldSpec(PSPEC_ICMP_TYPE, (u8*)&aux8);
|
|
aux8=o.getICMPCode();
|
|
h->addFieldSpec(PSPEC_ICMP_CODE, (u8*)&aux8);
|
|
break;
|
|
|
|
case UDP_UNPRIV:
|
|
case TCP_CONNECT:
|
|
case ARP:
|
|
default:
|
|
nping_fatal(QT_3, "%s packets are not supported in Echo Mode", o.mode2Ascii(o.getMode()) );
|
|
break;
|
|
}
|
|
/* Next protocol number */
|
|
if(o.ipv4())
|
|
h->addFieldSpec(PSPEC_IPv4_PROTO, (u8*)&nxthdr);
|
|
else
|
|
h->addFieldSpec(PSPEC_IPv6_NHDR, (u8*)&nxthdr);
|
|
|
|
if( o.issetPayloadBuffer() && o.getPayloadLen()>0){
|
|
h->addFieldSpec(PSPEC_PAYLOAD_MAGIC, (u8*)o.getPayloadBuffer(), MIN(o.getPayloadLen(), NEP_PAYLOADMAGIC_MAX_BYTES));
|
|
}
|
|
/* Done inserting packet field specifiers, now finish the packet */
|
|
h->setTotalLength();
|
|
h->setMessageAuthenticationCode(this->ctx.getMacKeyC2S(), MAC_KEY_LEN);
|
|
|
|
/* Encrypt message */
|
|
if( (next_iv=h->encrypt(this->ctx.getCipherKeyC2S(), CIPHER_KEY_LEN, this->ctx.getNextEncryptionIV()))==NULL )
|
|
return OP_FAILURE;
|
|
this->ctx.setNextEncryptionIV(next_iv);
|
|
|
|
return OP_SUCCESS;
|
|
} /* End of generate_packet_spec() */
|
|
|
|
|
|
/** Handles reception of a full NEP message. (the common NEP header). Basically
|
|
* it stores received data in the internal buffer and passes the control to
|
|
* the nep_recv_echo() method, which is the one in charge of processing
|
|
* NEP_ECHO packets */
|
|
int EchoClient::nep_echoed_packet_handler(nsock_pool nsp, nsock_event nse, void *arg){
|
|
nping_print(DBG_4, "%s()", __func__);
|
|
EchoHeader pkt_in;
|
|
u8 *recvbuff=NULL;
|
|
int recvbytes=0;
|
|
u8 aux[128];
|
|
u8 *pkt_start=this->lasthdr;
|
|
enum nse_status status=nse_status(nse);
|
|
if (status!=NSE_STATUS_SUCCESS){
|
|
if(status!=NSE_STATUS_KILL){
|
|
nping_warning(QT_2, "===========================================================================");
|
|
nping_warning(QT_2, "ERROR: Server closed the connection. No more CAPT packets will be received.");
|
|
nping_warning(QT_2, "===========================================================================");
|
|
}
|
|
return OP_FAILURE;
|
|
}
|
|
|
|
/* Read the remaining data */
|
|
if( (recvbuff=(u8 *)nse_readbuf(nse, &recvbytes))==NULL ){
|
|
nping_print(DBG_4,"nep_echoed_packet_handler(): nse_readbuf failed!\n");
|
|
return OP_FAILURE;
|
|
}else{
|
|
nping_print(DBG_4, "%s() Received %d bytes", __func__, recvbytes);
|
|
}
|
|
|
|
/* When we get here we'll have part of the packet stored in this->lasthdr and
|
|
* part of it (and possible more packets) stored in recvbuff. */
|
|
while(recvbytes>0){
|
|
|
|
/* Determine if we received the expected number of bytes or we received more
|
|
* than that. For that we need to decrypt the first 16 bytes so we can have
|
|
* a look at packet length */
|
|
Crypto::aes128_cbc_decrypt(pkt_start, 16, aux, this->ctx.getCipherKeyS2C(), CIPHER_KEY_LEN, this->ctx.getNextDecryptionIV());
|
|
pkt_in.storeRecvData(aux, 16);
|
|
int plen=pkt_in.getTotalLength()*4;
|
|
nping_print(DBG_4, "%s() Packet claims to have a length of %d bytes", __func__, plen);
|
|
|
|
/* If the packet is bigger than the maximum NEP packet, discard it. */
|
|
if(plen>MAX_NEP_PACKET_LENGTH){
|
|
nping_warning(DBG_1,"Warning. Received NEP packet (%dB) is bigger than %d bytes.", plen, MAX_NEP_PACKET_LENGTH);
|
|
return OP_FAILURE;
|
|
}
|
|
|
|
/* If we have read the whole packet, give it to nep_recv_echo for processing */
|
|
if (plen==((int)this->readbytes+recvbytes)){
|
|
memcpy(this->lasthdr+this->readbytes, recvbuff, recvbytes);
|
|
this->readbytes+=recvbytes;
|
|
nping_print(DBG_4,"%s(): Received exact length (%d).", __func__, recvbytes);
|
|
this->nep_recv_echo(this->lasthdr, this->readbytes);
|
|
nsock_readbytes(this->nsp, this->nsi, recv_std_header_handler, NSOCK_INFINITE, NULL, STD_NEP_HEADER_LEN);
|
|
return OP_SUCCESS;
|
|
|
|
/* This one can't happen in the first iteration since we scheduled the
|
|
* event with the exact amount of bytes, but may happen after that if we
|
|
* received more data and one of the packets is incomplete */
|
|
}else if(recvbytes<plen){
|
|
memcpy(this->lasthdr, recvbuff, recvbytes);
|
|
this->readbytes=recvbytes;
|
|
nping_print(DBG_4,"%s(): Missing %d bytes. Scheduled read operation for remaining bytes", __func__, plen-recvbytes);
|
|
nsock_readbytes(nsp, nsi, echoed_packet_handler, NSOCK_INFINITE, NULL, plen-recvbytes);
|
|
return OP_SUCCESS;
|
|
|
|
}else{ /* Received more than one packet */
|
|
nping_print(DBG_4,"%s(): Received more than one packet", __func__);
|
|
memcpy(this->lasthdr+this->readbytes, recvbuff, plen-this->readbytes);
|
|
this->nep_recv_echo(this->lasthdr, plen);
|
|
recvbuff+=plen-this->readbytes;
|
|
recvbytes-=plen-this->readbytes;
|
|
this->readbytes=0;
|
|
pkt_start=recvbuff;
|
|
}
|
|
|
|
}
|
|
return OP_SUCCESS;
|
|
} /* End of nep_echoed_packet_handler() */
|
|
|
|
|
|
/** Handles reception of the first 16 bytes (the common NEP header). Basically
|
|
* it checks the Total Length field of the header to determine how many bytes
|
|
* are left to read to get the entire packet. If there are more bytes to be
|
|
* receives, a read event is scheduled. However, we may have read them all when
|
|
* this handler is called (due to nsock behaviour) so in that case we just pass
|
|
* control to nep_recv_echo(), which is the one in charge of processing
|
|
* NEP_ECHO packets */
|
|
int EchoClient::nep_recv_std_header_handler(nsock_pool nsp, nsock_event nse, void *arg){
|
|
nping_print(DBG_4, "%s()", __func__);
|
|
nsock_iod nsi = nse_iod(nse);
|
|
EchoHeader pkt_in;
|
|
u8 *recvbuff=NULL;
|
|
int recvbytes=0;
|
|
u8 aux[128];
|
|
enum nse_status status=nse_status(nse);
|
|
if (status!=NSE_STATUS_SUCCESS){
|
|
if(status!=NSE_STATUS_KILL){
|
|
nping_warning(QT_2, "===========================================================================");
|
|
nping_warning(QT_2, "ERROR: Server closed the connection. No more CAPT packets will be received.");
|
|
nping_warning(QT_2, "===========================================================================");
|
|
}
|
|
return OP_FAILURE;
|
|
}
|
|
/* Read data */
|
|
if( (recvbuff=(u8 *)nse_readbuf(nse, &recvbytes))==NULL ){
|
|
nping_print(DBG_4,"%s(): nse_readbuf failed.", __func__);
|
|
return OP_FAILURE;
|
|
}else{
|
|
nping_print(DBG_4, "%s() Received %d bytes", __func__, recvbytes);
|
|
}
|
|
|
|
/* Here there are different possibilities. We may have received exactly one
|
|
* packet, we may have received more than one packet (as there is no way to
|
|
* make Nsock return an exact amount of bytes), or we may have received
|
|
* less than one packet. In the last case, we determine the number of bytes
|
|
* left and schedule another read event. */
|
|
while(recvbytes>0){
|
|
|
|
/* Decrypt the first 16 bytes so we can have a look at packet length */
|
|
Crypto::aes128_cbc_decrypt(recvbuff, 16, aux, this->ctx.getCipherKeyS2C(), CIPHER_KEY_LEN, this->ctx.getNextDecryptionIV());
|
|
pkt_in.storeRecvData(aux, 16);
|
|
int plen=pkt_in.getTotalLength()*4;
|
|
|
|
/* If the packet is bigger than the maximum NEP packet, discard it. */
|
|
if(plen>MAX_NEP_PACKET_LENGTH){
|
|
nping_warning(DBG_1,"Warning. Received NEP packet (%dB) is bigger than %d bytes.", plen, MAX_NEP_PACKET_LENGTH);
|
|
return OP_FAILURE;
|
|
}
|
|
|
|
/* If we have read the whole packet, give it to nep_recv_echo for processing */
|
|
if (plen==recvbytes){
|
|
nping_print(DBG_4,"%s(): Received exact length (%d).", __func__, recvbytes);
|
|
this->nep_recv_echo(recvbuff, recvbytes);
|
|
nsock_readbytes(this->nsp, this->nsi, recv_std_header_handler, NSOCK_INFINITE, NULL, STD_NEP_HEADER_LEN);
|
|
return OP_SUCCESS;
|
|
|
|
}else if(recvbytes<plen){
|
|
memcpy(this->lasthdr, recvbuff, recvbytes);
|
|
this->readbytes=recvbytes;
|
|
nping_print(DBG_4,"%s(): Missing %d bytes. Scheduled read operation for remaining bytes", __func__, plen-recvbytes);
|
|
nsock_readbytes(nsp, nsi, echoed_packet_handler, NSOCK_INFINITE, NULL, plen-recvbytes);
|
|
return OP_SUCCESS;
|
|
|
|
}else{ /* Received more than one packet */
|
|
nping_print(DBG_4,"%s(): Received more than one packet", __func__);
|
|
this->nep_recv_echo(recvbuff, plen);
|
|
recvbuff+=plen;
|
|
recvbytes-=plen;
|
|
}
|
|
|
|
}
|
|
|
|
/* Schedule another read event for the next echo packet */
|
|
nsock_readbytes(this->nsp, this->nsi, recv_std_header_handler, NSOCK_INFINITE, NULL, STD_NEP_HEADER_LEN);
|
|
|
|
return OP_SUCCESS;
|
|
} /* End of nep_recv_std_header_handler() */
|
|
|
|
|
|
/** Handles reception of NEP_HANDSHAKE_SERVER message. It handles the received
|
|
* data provided by nsock and passes it to the parse_hs_server() which is the
|
|
* one in charge of validating NEP_HANDSHAKE_SERVER packets and updating
|
|
* the internal context accordingly. Returns OP_SUCCESS on success and
|
|
* OP_FAILURE in case of error. */
|
|
int EchoClient::nep_recv_hs_server_handler(nsock_pool nsp, nsock_event nse, void *arg){
|
|
nping_print(DBG_4, "%s()", __func__);
|
|
u8 *inbuff=NULL;
|
|
int inlen=0;
|
|
/* Ask nsock to provide received data */
|
|
if( (inbuff=(u8 *)nse_readbuf(nse, &inlen))==NULL )
|
|
return OP_FAILURE;
|
|
/* Process the NEP_HANDSHAKE_SERVER message */
|
|
if ( this->parse_hs_server(inbuff, (size_t)inlen)!=OP_SUCCESS ){
|
|
return OP_FAILURE;
|
|
}
|
|
return OP_SUCCESS;
|
|
} /* End of nep_recv_hs_server_handler() */
|
|
|
|
|
|
/** Handles reception of NEP_HANDSHAKE_FINAL message. It handles the received
|
|
* data provided by nsock and passes it to the parse_hs_final() which is the
|
|
* one in charge of validating NEP_HANDSHAKE_FINAL packets and updating
|
|
* the internal context accordingly. Returns OP_SUCCESS on success and
|
|
* OP_FAILURE in case of error. */
|
|
int EchoClient::nep_recv_hs_final_handler(nsock_pool nsp, nsock_event nse, void *arg){
|
|
nping_print(DBG_4, "%s()", __func__);
|
|
u8 *inbuff=NULL;
|
|
int inlen=0;
|
|
/* Ask nsock to provide received data */
|
|
if( (inbuff=(u8 *)nse_readbuf(nse, &inlen))==NULL )
|
|
return OP_FAILURE;
|
|
/* Process the NEP_HANDSHAKE_SERVER message */
|
|
if ( this->parse_hs_final(inbuff, (size_t)inlen)!=OP_SUCCESS ){
|
|
return OP_FAILURE;
|
|
}
|
|
return OP_SUCCESS;
|
|
} /* End of nep_recv_hs_final_handler() */
|
|
|
|
|
|
/** Handles reception of NEP_READY message. It handles the received
|
|
* data provided by nsock and passes it to the parse_ready() which is the
|
|
* one in charge of validating NEP_HANDSHAKE_FINAL packets and updating
|
|
* the internal context accordingly. Returns OP_SUCCESS on success and
|
|
* OP_FAILURE in case of error. */
|
|
int EchoClient::nep_recv_ready_handler(nsock_pool nsp, nsock_event nse, void *arg){
|
|
nping_print(DBG_4, "%s()", __func__);
|
|
u8 *inbuff=NULL;
|
|
int inlen=0;
|
|
/* Ask nsock to provide received data */
|
|
if( (inbuff=(u8 *)nse_readbuf(nse, &inlen))==NULL )
|
|
return OP_FAILURE;
|
|
/* Process the NEP_HANDSHAKE_SERVER message */
|
|
if ( this->parse_ready(inbuff, (size_t)inlen)!=OP_SUCCESS ){
|
|
return OP_FAILURE;
|
|
}
|
|
return OP_SUCCESS;
|
|
} /* End of nep_recv_ready_handler() */
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
/**** HANDLER WRAPPERS ********************************************************/
|
|
/******************************************************************************/
|
|
|
|
/** This handler is a wrapper for the EchoClient::nep_echoed_packet_handler()
|
|
* method. We need this because C++ does not allow to use class methods as
|
|
* callback functions for things like signal() or the Nsock lib. */
|
|
void echoed_packet_handler(nsock_pool nsp, nsock_event nse, void *arg){
|
|
nping_print(DBG_4, "%s()", __func__);
|
|
ec.nep_echoed_packet_handler(nsp, nse, arg);
|
|
return;
|
|
} /* End of echoed_packet_handler() */
|
|
|
|
|
|
/** This handler is a wrapper for the EchoClient::nep_recv_std_header_handler()
|
|
* method. We need this because C++ does not allow to use class methods as
|
|
* callback functions for things like signal() or the Nsock lib. */
|
|
void recv_std_header_handler(nsock_pool nsp, nsock_event nse, void *arg){
|
|
nping_print(DBG_4, "%s()", __func__);
|
|
ec.nep_recv_std_header_handler(nsp, nse, arg);
|
|
return;
|
|
} /* End of recv_std_header_handler() */
|
|
|
|
|
|
/** Simple wrapper for TCP connection establishment. In this case we don't need
|
|
* to do anything special, just detect it the connection was successful. If
|
|
* it was, we call nsock_loop_quit(), which indicates the success to
|
|
* the method that scheduled the event and called nsock_loop() */
|
|
void connect_done_handler(nsock_pool nsp, nsock_event nse, void *arg){
|
|
nping_print(DBG_4, "%s()", __func__);
|
|
enum nse_status status=nse_status(nse);
|
|
if (status!=NSE_STATUS_SUCCESS){
|
|
nping_print(DBG_4, "%s(): Failed to connect.", __func__);
|
|
}else{
|
|
nsock_loop_quit(nsp);
|
|
}
|
|
return;
|
|
} /* End of connect_done_handler() */
|
|
|
|
|
|
/** Really simple wrapper for write calls where we don't need to perform any
|
|
* special operations. It just checks if the write even was successful and
|
|
* in that case it calls nsock_loop_quit(), which indicates the success to
|
|
* the method that scheduled the event and called nsock_loop() */
|
|
void write_done_handler(nsock_pool nsp, nsock_event nse, void *arg){
|
|
nping_print(DBG_4, "%s()", __func__);
|
|
enum nse_status status=nse_status(nse);
|
|
if (status!=NSE_STATUS_SUCCESS){
|
|
nping_print(DBG_4, "%s(): Write operation failed.", __func__);
|
|
}else{
|
|
nsock_loop_quit(nsp);
|
|
}
|
|
return;
|
|
} /* End of connect_done_handler() */
|
|
|
|
|
|
/** This handler is a wrapper for the EchoClient::recv_hs_server_handler()
|
|
* method. We need this because C++ does not allow to use class methods as
|
|
* callback functions for things like signal() or the Nsock lib. */
|
|
void recv_hs_server_handler(nsock_pool nsp, nsock_event nse, void *arg){
|
|
nping_print(DBG_4, "%s()", __func__);
|
|
enum nse_status status=nse_status(nse);
|
|
if (status!=NSE_STATUS_SUCCESS){
|
|
nping_print(DBG_4, "%s(): Read operation failed.", __func__);
|
|
}else if(ec.nep_recv_hs_server_handler(nsp, nse, arg)==OP_SUCCESS){
|
|
nsock_loop_quit(nsp);
|
|
}
|
|
return;
|
|
} /* End of recv_hs_server_handler() */
|
|
|
|
|
|
/** This handler is a wrapper for the EchoClient::recv_hs_final_handler()
|
|
* method. We need this because C++ does not allow to use class methods as
|
|
* callback functions for things like signal() or the Nsock lib. */
|
|
void recv_hs_final_handler(nsock_pool nsp, nsock_event nse, void *arg){
|
|
nping_print(DBG_4, "%s()", __func__);
|
|
enum nse_status status=nse_status(nse);
|
|
if (status!=NSE_STATUS_SUCCESS){
|
|
nping_print(DBG_4, "%s(): Read operation failed.", __func__);
|
|
}else if(ec.nep_recv_hs_final_handler(nsp, nse, arg)==OP_SUCCESS){
|
|
nsock_loop_quit(nsp);
|
|
}
|
|
return;
|
|
} /* End of recv_hs_server_handler() */
|
|
|
|
|
|
|
|
/** This handler is a wrapper for the EchoClient::nep_recv_ready_handler()
|
|
* method. We need this because C++ does not allow to use class methods as
|
|
* callback functions for things like signal() or the Nsock lib. */
|
|
void recv_ready_handler(nsock_pool nsp, nsock_event nse, void *arg){
|
|
nping_print(DBG_4, "%s()", __func__);
|
|
enum nse_status status=nse_status(nse);
|
|
if (status!=NSE_STATUS_SUCCESS){
|
|
nping_print(DBG_4, "%s(): Read operation failed.", __func__);
|
|
}else if(ec.nep_recv_ready_handler(nsp, nse, arg)==OP_SUCCESS){
|
|
nsock_loop_quit(nsp);
|
|
}
|
|
return;
|
|
} /* End of recv_hs_server_handler() */
|
|
|