1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-14 03:39:02 +00:00
Files
nmap/nping/EchoClient.cc
david ed2ba4e168 Copy nping, nsock, nbase, zenmap, ncat from their homes in /.
If you have trouble updating after this revision you need to follow
these instructions. You have probably just seen an error like this:

svn: URL 'svn://svn.insecure.org/nping' of existing directory 'nping'
does not match expected URL 'svn://svn.insecure.org/nmap/nping'

This is caused by the replacement of SVN externals.

Here's what you need to do. First, save any local changes you might have
in the nping, nsock, nbase, ncat, and zenmap directories. (For example
by running "cd nping; svn diff > ../nping.diff".) If you don't have any
local changes you can skip this step.

Then run these commands:

rm -rf nping/ nsock/ nbase/ ncat/ zenmap/
svn update
svn cleanup

If all else fails, you can just delete your whole working directory and
check out anew:

svn co --username guest --password "" svn://svn.insecure.org/nmap

There may be further discussion in the mailing list thread at
http://seclists.org/nmap-dev/2011/q4/303.
2011-11-16 21:49:44 +00:00

1129 lines
45 KiB
C++

/***************************************************************************
* EchoClient.cc -- *
* *
***********************IMPORTANT NMAP LICENSE TERMS************************
* *
* The Nmap Security Scanner is (C) 1996-2011 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 with the clarifications and exceptions described *
* below. 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@insecure.com). Dozens of software vendors already *
* license Nmap technology such as host discovery, port scanning, OS *
* detection, and version detection. *
* *
* Note that the GPL places important restrictions on "derived works", yet *
* it does not provide a detailed definition of that term. To avoid *
* misunderstandings, we consider an application to constitute a *
* "derivative work" for the purpose of this license if it does any of the *
* following: *
* o Integrates source code from Nmap *
* o Reads or includes Nmap copyrighted data files, such as *
* nmap-os-db or nmap-service-probes. *
* o Executes Nmap and parses the results (as opposed to typical shell or *
* execution-menu apps, which simply display raw Nmap output and so are *
* not derivative works.) *
* o Integrates/includes/aggregates Nmap into a proprietary executable *
* installer, such as those produced by InstallShield. *
* o Links to a library or executes a program that does any of the above *
* *
* The term "Nmap" should be taken to also include any portions or derived *
* works of Nmap. This list is not exclusive, but is meant to clarify our *
* interpretation of derived works with some common examples. Our *
* interpretation applies only to Nmap--we don't speak for other people's *
* GPL works. *
* *
* If you have any questions about the GPL licensing restrictions on using *
* Nmap in non-GPL works, we would be 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 as well as helping to *
* fund the continued development of Nmap technology. Please email *
* sales@insecure.com for further information. *
* *
* As a 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. You must obey the GNU GPL in all *
* respects for all of the code used other than OpenSSL. If you modify *
* this file, you may extend this exception to your version of the file, *
* but you are not obligated to do so. *
* *
* If you received these files with a written license agreement or *
* contract stating terms other than the terms above, then that *
* alternative license agreement takes precedence over these comments. *
* *
* 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 (none *
* have been found so far). *
* *
* 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 nmap-dev@insecure.org for possible incorporation into the main *
* distribution. By sending these changes to Fyodor or one of the *
* Insecure.Org development mailing lists, it is assumed 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 GNU *
* General Public License v2.0 for more details at *
* http://www.gnu.org/licenses/gpl-2.0.html , or in the COPYING file *
* included with Nmap. *
* *
***************************************************************************/
#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){
outPrint(DBG_4, "%s(%p, %u)", __func__, target, port);
/* Init Nsock in the probe engine */
if( this->probe.init_nsock() != OP_SUCCESS ){
outError(QT_2, "Couln't initialize Nsock.");
return OP_FAILURE;
}else{
/* Extract the nsock pool handler and store it here */
this->nsp=this->probe.getNsockPool();
this->nsi=nsi_new(this->nsp, NULL);
}
/* Schedule a TCP connection attempt */
if( this->nep_connect(target, port) != OP_SUCCESS ){
outError(QT_2, "Connection failed.");
return OP_FAILURE;
}
/* Perform NEP authentication handshake */
if( this->nep_handshake() != OP_SUCCESS ){
outError(QT_2, "Handshake failed.");
return OP_FAILURE;
}
/* Send packet specification */
if( this->nep_send_packet_spec() != OP_SUCCESS ){
outError(QT_2, "Couldn't send packet specification.");
return OP_FAILURE;
}
/* Wait for confirmation */
if( this->nep_recv_ready() != OP_SUCCESS ){
outError(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){
outPrint(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)
outFatal(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 */
nsi_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 */
nsi_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(){
outPrint(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();
outPrint(DBG_4,"Session Key MAC_C2S:"); print_hexdump(DBG_4,ctx.getMacKeyC2S(), MAC_KEY_LEN);
outPrint(DBG_4,"Session Key MAC_S2C:"); print_hexdump(DBG_4,ctx.getMacKeyS2C(), MAC_KEY_LEN);
outPrint(DBG_4,"Session Key CIPHER_C2S:"); print_hexdump(DBG_4,ctx.getCipherKeyC2S(), MAC_KEY_LEN);
outPrint(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;
outPrint(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(){
outPrint(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(){
outPrint(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){
outPrint(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){
outPrint(VB_0, "Unexpected error dealing with the NEP_ECHO message,");
return OP_FAILURE;
}
if((pkt=pkt_in.getEchoedPacket(&pktlen))==NULL){
outPrint(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);
outPrint(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){
outPrint(DBG_4, "%s()", __func__);
EchoHeader h;
if(pkt==NULL){
outPrint(DBG_1,"%s(): NULL parameter supplied.", __func__ );
return OP_FAILURE;
}
if(pktlen!=NEP_HANDSHAKE_SERVER_LEN){
outPrint(DBG_1,"%s(): Unexpected length supplied.", __func__ );
return OP_FAILURE;
}
h.storeRecvData(pkt, pktlen);
/* Validate version number */
if( h.getVersion() != ECHO_CURRENT_PROTO_VER ){
outPrint(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){
outPrint(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 ){
// outPrint(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)){
outPrint(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 ){
outPrint(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){
outPrint(DBG_4, "%s()", __func__);
EchoHeader h;
u8 *next_iv=NULL;
if(pkt==NULL){
outPrint(DBG_1,"%s(): NULL parameter supplied.", __func__ );
return OP_FAILURE;
}
if(pktlen!=NEP_HANDSHAKE_FINAL_LEN){
outPrint(DBG_1,"%s(): Unexpected length supplied.", __func__ );
return OP_FAILURE;
}
h.storeRecvData(pkt, pktlen);
/* Validate version number */
if( h.getVersion() != ECHO_CURRENT_PROTO_VER ){
outPrint(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){
outPrint(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)){
outPrint(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 ){
// outPrint(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)){
outPrint(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 ){
outPrint(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){
outPrint(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 ){
outPrint(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){
outPrint(DBG_4, "%s()", __func__);
EchoHeader h;
u8 *next_iv=NULL;
if(pkt==NULL){
outPrint(DBG_1,"%s(): NULL parameter supplied.", __func__ );
return OP_FAILURE;
}
if(pktlen!=NEP_READY_LEN){
outPrint(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){
outPrint(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 ){
outPrint(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){
outPrint(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)){
outPrint(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 ){
// outPrint(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)){
outPrint(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 ){
outPrint(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){
outPrint(DBG_4, "%s()", __func__);
EchoHeader h;
u8 *next_iv=NULL;
if(pkt==NULL){
outPrint(DBG_1,"%s(): NULL parameter supplied.", __func__ );
return OP_FAILURE;
}
if(pktlen<NEP_ECHO_MIN_LEN){
outPrint(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){
outPrint(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 ){
outPrint(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){
outPrint(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)){
outPrint(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 ){
// outPrint(DBG_1, "NEP_ECHO timestamp is too old", h.getMessageType() );
// return OP_FAILURE;
//}
// /* Ensure message length is correct */
// if( h.getTotalLength()!=(pktlen/4)){
// outPrint(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 ){
outPrint(DBG_1, "NEP_ECHO authentication failed" );
return OP_FAILURE;
}else{
outPrint(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){
outPrint(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){
outPrint(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){
outPrint(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:
outFatal(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){
outPrint(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){
outError(QT_2, "===========================================================================");
outError(QT_2, "ERROR: Server closed the connection. No more CAPT packets will be received.");
outError(QT_2, "===========================================================================");
}
return OP_FAILURE;
}
/* Read the remaining data */
if( (recvbuff=(u8 *)nse_readbuf(nse, &recvbytes))==NULL ){
outPrint(DBG_4,"nep_echoed_packet_handler(): nse_readbuf failed!\n");
return OP_FAILURE;
}else{
outPrint(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;
outPrint(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){
outError(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;
outPrint(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;
outPrint(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 */
outPrint(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){
outPrint(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){
outError(QT_2, "===========================================================================");
outError(QT_2, "ERROR: Server closed the connection. No more CAPT packets will be received.");
outError(QT_2, "===========================================================================");
}
return OP_FAILURE;
}
/* Read data */
if( (recvbuff=(u8 *)nse_readbuf(nse, &recvbytes))==NULL ){
outPrint(DBG_4,"%s(): nse_readbuf failed.", __func__);
return OP_FAILURE;
}else{
outPrint(DBG_4, "%s() Received %d bytes", __func__, recvbytes);
}
/* Here there are different possibilites. 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){
outError(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){
outPrint(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;
outPrint(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 */
outPrint(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){
outPrint(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){
outPrint(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_readyl() 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){
outPrint(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){
outPrint(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){
outPrint(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){
outPrint(DBG_4, "%s()", __func__);
enum nse_status status=nse_status(nse);
if (status!=NSE_STATUS_SUCCESS){
outPrint(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){
outPrint(DBG_4, "%s()", __func__);
enum nse_status status=nse_status(nse);
if (status!=NSE_STATUS_SUCCESS){
outPrint(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){
outPrint(DBG_4, "%s()", __func__);
enum nse_status status=nse_status(nse);
if (status!=NSE_STATUS_SUCCESS){
outPrint(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){
outPrint(DBG_4, "%s()", __func__);
enum nse_status status=nse_status(nse);
if (status!=NSE_STATUS_SUCCESS){
outPrint(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){
outPrint(DBG_4, "%s()", __func__);
enum nse_status status=nse_status(nse);
if (status!=NSE_STATUS_SUCCESS){
outPrint(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() */