mirror of
https://github.com/nmap/nmap.git
synced 2025-12-06 04:31:29 +00:00
1035 lines
34 KiB
C++
Executable File
1035 lines
34 KiB
C++
Executable File
|
|
/***************************************************************************
|
|
* EchoHeader.cc -- The EchoHeader Class represents packets of the Nping *
|
|
* Echo Protocol. It contains the appropriate methods to set/get all *
|
|
* header fields. In general these methods do error checking and perform *
|
|
* byte order conversions. *
|
|
* *
|
|
***********************IMPORTANT NMAP LICENSE TERMS************************
|
|
* *
|
|
* The Nmap Security Scanner is (C) 1996-2013 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 (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 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 "EchoHeader.h"
|
|
#include "nping.h"
|
|
#include "output.h"
|
|
#include <time.h>
|
|
#include <assert.h>
|
|
#include "Crypto.h"
|
|
|
|
EchoHeader::EchoHeader(){
|
|
this->reset();
|
|
} /* End of EchoHeader constructor */
|
|
|
|
|
|
EchoHeader::~EchoHeader(){
|
|
|
|
} /* End of EchoHeader destructor */
|
|
|
|
|
|
/** Sets every attribute to its default value. */
|
|
void EchoHeader::reset() {
|
|
memset(&this->h, 0, sizeof(echohdr_t) );
|
|
this->data_hsserv=(nep_hs_serv_data_t *)this->h.data;
|
|
this->data_hsclnt=(nep_hs_clnt_data_t *)this->h.data;
|
|
this->data_hsfinal=(nep_hs_final_data_t *)this->h.data;
|
|
this->data_pspec=(nep_packet_spec_data_t *)this->h.data;
|
|
this->data_ready=(nep_ready_data_t *)this->h.data;
|
|
this->data_echo=(nep_echo_data_t *)this->h.data;
|
|
this->data_error=(nep_error_data_t *)this->h.data;
|
|
this->fs_off=(u8 *)this->data_pspec->packetspec;
|
|
this->fs_bytes=0;
|
|
this->echo_mac=(u8 *)this->data_echo->payload_and_mac;
|
|
this->echo_bytes=0;
|
|
|
|
/* Some safe initializations */
|
|
this->setVersion(ECHO_CURRENT_PROTO_VER);
|
|
this->setTotalLength(STD_NEP_HEADER_LEN + MAC_LENGTH);
|
|
this->length=STD_NEP_HEADER_LEN + MAC_LENGTH; /* Sets length in PacketElement superclass */
|
|
} /* End of reset() */
|
|
|
|
|
|
/** @warning This method is essential for the superclass getBinaryBuffer()
|
|
* method to work. Do NOT change a thing unless you know what you're doing. */
|
|
u8 * EchoHeader::getBufferPointer(){
|
|
return (u8*)(&h);
|
|
} /* End of getBufferPointer() */
|
|
|
|
|
|
/** Stores supplied packet in the internal buffer so the information
|
|
* can be accessed using the standard get & set methods. */
|
|
int EchoHeader::storeRecvData(const u8 *buf, size_t len){
|
|
if(buf==NULL || len>(STD_NEP_HEADER_LEN+MAX_DATA_LEN)){
|
|
return OP_FAILURE;
|
|
}else{
|
|
this->reset(); /* Re-init the object, just in case the caller had used it already */
|
|
this->length=len;
|
|
memcpy(&(this->h), buf, len);
|
|
}
|
|
return OP_SUCCESS;
|
|
} /* End of storeRecvData() */
|
|
|
|
|
|
/* Returns a protocol identifier. This is used by packet parsing functions
|
|
* that return linked lists of PacketElement objects, to determine the protocol
|
|
* the object represents. */
|
|
int EchoHeader::protocol_id() const {
|
|
return HEADER_TYPE_NEP;
|
|
} /* End of protocol_id() */
|
|
|
|
|
|
/** Sets Version.
|
|
* @return OP_SUCCESS on success and OP_FAILURE in case of error. */
|
|
int EchoHeader::setVersion(u8 val){
|
|
this->h.echo_ver=val;
|
|
return OP_SUCCESS;
|
|
} /* End of setVersion() */
|
|
|
|
|
|
/** Returns value of attribute h.echo_ver */
|
|
u8 EchoHeader::getVersion(){
|
|
return this->h.echo_ver;
|
|
} /* End of getVersion() */
|
|
|
|
|
|
/** Sets MessageType.
|
|
* @return OP_SUCCESS on success and OP_FAILURE in case of error. */
|
|
int EchoHeader::setMessageType(u8 val){
|
|
this->h.echo_mtype=val;
|
|
return OP_SUCCESS;
|
|
} /* End of setMessageType() */
|
|
|
|
|
|
/** Returns value of attribute h.echo_mtype */
|
|
u8 EchoHeader::getMessageType(){
|
|
return this->h.echo_mtype;
|
|
} /* End of getsetMessageType() */
|
|
|
|
|
|
/** Sets Total Length.
|
|
* @return OP_SUCCESS on success and OP_FAILURE in case of error.
|
|
* @warning the length is expressed in 32bit words. */
|
|
int EchoHeader::setTotalLength(u16 val){
|
|
this->h.echo_tlen=htons(val);
|
|
this->length=val*4; /* Also, set superclass length attribute */
|
|
return OP_SUCCESS;
|
|
} /* End of setTotalLength() */
|
|
|
|
|
|
/** Sets Total Length.
|
|
* @return OP_SUCCESS on success and OP_FAILURE in case of error. */
|
|
int EchoHeader::setTotalLength(){
|
|
switch( this->getMessageType() ){
|
|
case TYPE_NEP_HANDSHAKE_SERVER:
|
|
this->setTotalLength(NEP_HANDSHAKE_SERVER_LEN/4);
|
|
break;
|
|
case TYPE_NEP_HANDSHAKE_CLIENT:
|
|
this->setTotalLength(NEP_HANDSHAKE_CLIENT_LEN/4);
|
|
break;
|
|
case TYPE_NEP_HANDSHAKE_FINAL:
|
|
this->setTotalLength(NEP_HANDSHAKE_FINAL_LEN/4);
|
|
break;
|
|
case TYPE_NEP_PACKET_SPEC:
|
|
this->setTotalLength(NEP_PACKETSPEC_LEN/4);
|
|
break;
|
|
case TYPE_NEP_READY:
|
|
this->setTotalLength(NEP_READY_LEN/4);
|
|
break;
|
|
case TYPE_NEP_ECHO:
|
|
this->setTotalLength( (STD_NEP_HEADER_LEN + 4 + MAC_LENGTH + this->echo_bytes)/4 );
|
|
break;
|
|
case TYPE_NEP_ERROR:
|
|
this->setTotalLength(NEP_ERROR_LEN/4);
|
|
break;
|
|
default:
|
|
return OP_FAILURE;
|
|
break;
|
|
}
|
|
return OP_SUCCESS;
|
|
} /* End of setTotalLength() */
|
|
|
|
|
|
/** Returns value of attribute h.echo_tlen
|
|
* @warning Returned length is expressed in 32bit words. To get a byte count
|
|
* it must be multiplied by four */
|
|
u16 EchoHeader::getTotalLength(){
|
|
return ntohs(this->h.echo_tlen);
|
|
} /* End of getTotalLength() */
|
|
|
|
|
|
/** Sets SequenceNumber.
|
|
* @return OP_SUCCESS on success and OP_FAILURE in case of error. */
|
|
int EchoHeader::setSequenceNumber(u32 val){
|
|
this->h.echo_seq=htonl(val);
|
|
return OP_SUCCESS;
|
|
} /* End of setSequenceNumber() */
|
|
|
|
|
|
/** Returns value of attribute h.echo_seq */
|
|
u32 EchoHeader::getSequenceNumber(){
|
|
return ntohl(this->h.echo_seq);
|
|
} /* End of getSequenceNumber() */
|
|
|
|
|
|
/** Sets Timestamp.
|
|
* @return OP_SUCCESS on success and OP_FAILURE in case of error. */
|
|
int EchoHeader::setTimestamp(u32 val){
|
|
this->h.echo_ts=htonl(val);
|
|
return OP_SUCCESS;
|
|
} /* End of setTimestamp() */
|
|
|
|
|
|
/** Sets Timestamp.
|
|
* @return OP_SUCCESS on success and OP_FAILURE in case of error. */
|
|
int EchoHeader::setTimestamp(){
|
|
u32 t=(u32)time(NULL); /* TODO: Make sure this does not cause problems */
|
|
this->h.echo_ts=htonl(t);
|
|
return OP_SUCCESS;
|
|
} /* End of setTimestamp() */
|
|
|
|
|
|
/** Returns value of attribute h.echo_ts*/
|
|
u32 EchoHeader::getTimestamp(){
|
|
return ntohl(this->h.echo_ts);
|
|
} /* End of getTimestamp() */
|
|
|
|
|
|
/** Sets Reserved.
|
|
* @return OP_SUCCESS on success and OP_FAILURE in case of error. */
|
|
int EchoHeader::setReserved(u32 val){
|
|
this->h.echo_res=htonl(val);
|
|
return OP_SUCCESS;
|
|
} /* End of setReserved() */
|
|
|
|
|
|
/** Returns value of attribute h.echo_res */
|
|
u32 EchoHeader::getReserved(){
|
|
return this->h.echo_res;
|
|
} /* End of getReserved() */
|
|
|
|
|
|
int EchoHeader::setMessageAuthenticationCode(u8 *key, size_t keylen){
|
|
u8 *macpnt=NULL;
|
|
u8 *from=(u8 *)&(this->h);
|
|
size_t bytes=0;
|
|
|
|
/* Determine where the MAC field is and the length of the data that needs
|
|
* to be authenticated, based on message type. */
|
|
switch( this->getMessageType() ){
|
|
case TYPE_NEP_HANDSHAKE_SERVER:
|
|
macpnt=this->data_hsserv->mac;
|
|
bytes=NEP_HANDSHAKE_SERVER_LEN-MAC_LENGTH;
|
|
break;
|
|
case TYPE_NEP_HANDSHAKE_CLIENT:
|
|
macpnt=this->data_hsclnt->mac;
|
|
bytes=NEP_HANDSHAKE_CLIENT_LEN-MAC_LENGTH;
|
|
break;
|
|
case TYPE_NEP_HANDSHAKE_FINAL:
|
|
macpnt=this->data_hsfinal->mac;
|
|
bytes=NEP_HANDSHAKE_FINAL_LEN-MAC_LENGTH;
|
|
break;
|
|
case TYPE_NEP_PACKET_SPEC:
|
|
macpnt=this->data_pspec->mac;
|
|
bytes=NEP_PACKETSPEC_LEN-MAC_LENGTH;
|
|
break;
|
|
case TYPE_NEP_READY:
|
|
macpnt=this->data_ready->mac;
|
|
bytes=NEP_READY_LEN-MAC_LENGTH;
|
|
break;
|
|
case TYPE_NEP_ECHO:
|
|
macpnt=this->echo_mac;
|
|
bytes=STD_NEP_HEADER_LEN + 4 + this->echo_bytes;
|
|
break;
|
|
case TYPE_NEP_ERROR:
|
|
macpnt=this->data_error->mac;
|
|
bytes=NEP_ERROR_LEN-MAC_LENGTH;
|
|
break;
|
|
default:
|
|
return OP_FAILURE;
|
|
break;
|
|
}
|
|
/* Compute the code */
|
|
Crypto::hmac_sha256(from, bytes, macpnt, key, keylen);
|
|
return OP_SUCCESS;
|
|
} /* End of setMessageAuthenticationCode() */
|
|
|
|
|
|
u8 *EchoHeader::getMessageAuthenticationCode(){
|
|
switch( this->getMessageType() ){
|
|
case TYPE_NEP_HANDSHAKE_SERVER:
|
|
return this->data_hsserv->mac;
|
|
break;
|
|
case TYPE_NEP_HANDSHAKE_CLIENT:
|
|
return this->data_hsclnt->mac;
|
|
break;
|
|
case TYPE_NEP_HANDSHAKE_FINAL:
|
|
return this->data_hsfinal->mac;
|
|
break;
|
|
case TYPE_NEP_PACKET_SPEC:
|
|
return this->data_pspec->mac;
|
|
break;
|
|
case TYPE_NEP_READY:
|
|
return this->data_ready->mac;
|
|
break;
|
|
case TYPE_NEP_ECHO:
|
|
this->updateEchoInternals();
|
|
return this->echo_mac;
|
|
break;
|
|
case TYPE_NEP_ERROR:
|
|
return this->data_error->mac;
|
|
break;
|
|
default:
|
|
return NULL;
|
|
break;
|
|
}
|
|
return NULL;
|
|
} /* End of getMessageAuthenticationCode() */
|
|
|
|
|
|
|
|
int EchoHeader::verifyMessageAuthenticationCode(u8 *key, size_t keylen){
|
|
u8 mac_backup[MAC_LENGTH];
|
|
u8 *aux;
|
|
|
|
/* Make a copy of the current MAC */
|
|
if( (aux=this->getMessageAuthenticationCode())==NULL )
|
|
return OP_FAILURE;
|
|
memcpy(mac_backup, aux, MAC_LENGTH);
|
|
|
|
/* Recompute the MAC */
|
|
memset(aux, 0, MAC_LENGTH);
|
|
this->setMessageAuthenticationCode(key, keylen);
|
|
|
|
/* Try to match both MACs*/
|
|
if( (aux=this->getMessageAuthenticationCode())==NULL )
|
|
return OP_FAILURE;
|
|
if( memcmp(mac_backup, aux, MAC_LENGTH)==0 ){
|
|
return OP_SUCCESS;
|
|
}else{
|
|
/* Restore original MAC */
|
|
memcpy(aux, mac_backup, MAC_LENGTH);
|
|
return OP_FAILURE;
|
|
}
|
|
} /* End of verifyMessageAuthenticationCode() */
|
|
|
|
/******************************************************************************/
|
|
/* NEP_HANDSHAKE methods */
|
|
/******************************************************************************/
|
|
|
|
int EchoHeader::setServerNonce(u8 *nonce){
|
|
assert(nonce);
|
|
switch( this->getMessageType() ){
|
|
case TYPE_NEP_HANDSHAKE_SERVER:
|
|
memcpy(this->data_hsserv->server_nonce, nonce, NONCE_LEN);
|
|
break;
|
|
|
|
case TYPE_NEP_HANDSHAKE_CLIENT:
|
|
memcpy(this->data_hsclnt->server_nonce, nonce, NONCE_LEN);
|
|
break;
|
|
|
|
default:
|
|
return OP_FAILURE;
|
|
break;
|
|
}
|
|
return OP_SUCCESS;
|
|
} /* End of getServerNonce() */
|
|
|
|
|
|
u8 *EchoHeader::getServerNonce(){
|
|
switch( this->getMessageType() ){
|
|
case TYPE_NEP_HANDSHAKE_SERVER:
|
|
return this->data_hsserv->server_nonce;
|
|
break;
|
|
|
|
case TYPE_NEP_HANDSHAKE_CLIENT:
|
|
return this->data_hsclnt->server_nonce;
|
|
break;
|
|
|
|
default:
|
|
return NULL;
|
|
break;
|
|
}
|
|
} /* End of getServerNonce() */
|
|
|
|
|
|
int EchoHeader::setClientNonce(u8 *nonce){
|
|
assert(nonce);
|
|
switch( this->getMessageType() ){
|
|
case TYPE_NEP_HANDSHAKE_CLIENT:
|
|
memcpy(this->data_hsclnt->client_nonce, nonce, NONCE_LEN);
|
|
break;
|
|
|
|
case TYPE_NEP_HANDSHAKE_FINAL:
|
|
memcpy(this->data_hsfinal->client_nonce , nonce, NONCE_LEN);
|
|
break;
|
|
|
|
default:
|
|
return OP_FAILURE;
|
|
break;
|
|
}
|
|
return OP_SUCCESS;
|
|
} /* End of getClientNonce() */
|
|
|
|
|
|
u8 *EchoHeader::getClientNonce(){
|
|
switch( this->getMessageType() ){
|
|
case TYPE_NEP_HANDSHAKE_CLIENT:
|
|
return this->data_hsclnt->client_nonce;
|
|
break;
|
|
|
|
case TYPE_NEP_HANDSHAKE_FINAL:
|
|
return this->data_hsfinal->client_nonce;
|
|
break;
|
|
|
|
default:
|
|
return NULL;
|
|
break;
|
|
}
|
|
} /* End of getClientNonce() */
|
|
|
|
|
|
int EchoHeader::setPartnerAddress(struct in_addr val){
|
|
switch( this->getMessageType() ){
|
|
case TYPE_NEP_HANDSHAKE_CLIENT:
|
|
memset(this->data_hsclnt->partner_ip, 0, 16);
|
|
memcpy(this->data_hsclnt->partner_ip , &val, sizeof(struct in_addr));
|
|
break;
|
|
|
|
case TYPE_NEP_HANDSHAKE_FINAL:
|
|
memset(this->data_hsfinal->partner_ip, 0, 16);
|
|
memcpy(this->data_hsfinal->partner_ip , &val, sizeof(struct in_addr));
|
|
break;
|
|
|
|
default:
|
|
return OP_FAILURE;
|
|
break;
|
|
}
|
|
this->setIPVersion(0x04);
|
|
return OP_SUCCESS;
|
|
} /* End of setPartnerAddress() */
|
|
|
|
|
|
int EchoHeader::setPartnerAddress(struct in6_addr val){
|
|
switch( this->getMessageType() ){
|
|
case TYPE_NEP_HANDSHAKE_CLIENT:
|
|
memset(this->data_hsclnt->partner_ip, 0, 16);
|
|
memcpy(this->data_hsclnt->partner_ip , &val, sizeof(struct in6_addr));
|
|
break;
|
|
|
|
case TYPE_NEP_HANDSHAKE_FINAL:
|
|
memset(this->data_hsfinal->partner_ip, 0, 16);
|
|
memcpy(this->data_hsfinal->partner_ip , &val, sizeof(struct in6_addr));
|
|
break;
|
|
|
|
default:
|
|
return OP_FAILURE;
|
|
break;
|
|
}
|
|
this->setIPVersion(0x06);
|
|
return OP_SUCCESS;
|
|
} /* End of setPartnerAddress() */
|
|
|
|
|
|
int EchoHeader::getPartnerAddress(struct in_addr *dst){
|
|
switch( this->getMessageType() ){
|
|
case TYPE_NEP_HANDSHAKE_CLIENT:
|
|
memcpy(dst, this->data_hsclnt->partner_ip,sizeof(struct in_addr));
|
|
break;
|
|
|
|
case TYPE_NEP_HANDSHAKE_FINAL:
|
|
memcpy(dst, this->data_hsfinal->partner_ip,sizeof(struct in_addr));
|
|
break;
|
|
|
|
default:
|
|
return OP_FAILURE;
|
|
break;
|
|
}
|
|
return OP_SUCCESS;
|
|
} /* End of getPartnerAddress() */
|
|
|
|
|
|
int EchoHeader::getPartnerAddress(struct in6_addr *dst){
|
|
switch( this->getMessageType() ){
|
|
case TYPE_NEP_HANDSHAKE_CLIENT:
|
|
memcpy(dst, this->data_hsclnt->partner_ip,sizeof(struct in6_addr));
|
|
break;
|
|
|
|
case TYPE_NEP_HANDSHAKE_FINAL:
|
|
memcpy(dst, this->data_hsfinal->partner_ip,sizeof(struct in6_addr));
|
|
break;
|
|
|
|
default:
|
|
return OP_FAILURE;
|
|
break;
|
|
}
|
|
return OP_SUCCESS;
|
|
} /* End of getPartnerAddress() */
|
|
|
|
|
|
/* On failure, it returns 0xAB */
|
|
u8 EchoHeader::getIPVersion(){
|
|
switch( this->getMessageType() ){
|
|
case TYPE_NEP_HANDSHAKE_CLIENT:
|
|
return this->data_hsclnt->ip_version;
|
|
break;
|
|
|
|
case TYPE_NEP_HANDSHAKE_FINAL:
|
|
return this->data_hsfinal->ip_version;
|
|
break;
|
|
|
|
case TYPE_NEP_PACKET_SPEC:
|
|
return this->data_pspec->ip_version;
|
|
break;
|
|
|
|
default:
|
|
return 0xAB;
|
|
break;
|
|
}
|
|
} /* End of getIPVersion() */
|
|
|
|
|
|
int EchoHeader::setIPVersion(u8 ver){
|
|
switch( this->getMessageType() ){
|
|
case TYPE_NEP_HANDSHAKE_CLIENT:
|
|
this->data_hsclnt->ip_version=ver;
|
|
break;
|
|
|
|
case TYPE_NEP_HANDSHAKE_FINAL:
|
|
this->data_hsfinal->ip_version=ver;
|
|
break;
|
|
|
|
case TYPE_NEP_PACKET_SPEC:
|
|
this->data_pspec->ip_version=ver;
|
|
break;
|
|
|
|
default:
|
|
return OP_FAILURE;
|
|
break;
|
|
}
|
|
return OP_SUCCESS;
|
|
} /* End of setIPVersion() */
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
/* NEP_PACKET_SPEC methods */
|
|
/******************************************************************************/
|
|
|
|
int EchoHeader::setProtocol(u8 proto){
|
|
switch( this->getMessageType() ){
|
|
case TYPE_NEP_PACKET_SPEC:
|
|
this->data_pspec->protocol=proto;
|
|
break;
|
|
|
|
default:
|
|
return OP_FAILURE;
|
|
break;
|
|
}
|
|
return OP_SUCCESS;
|
|
} /* End of setProtocol() */
|
|
|
|
|
|
/* On failure, it returns 0xAB */
|
|
u8 EchoHeader::getProtocol(){
|
|
switch( this->getMessageType() ){
|
|
case TYPE_NEP_PACKET_SPEC:
|
|
return this->data_pspec->protocol;
|
|
break;
|
|
|
|
default:
|
|
return 0xAB;
|
|
break;
|
|
}
|
|
} /* End of setProtocol() */
|
|
|
|
|
|
int EchoHeader::setPacketCount(u16 c){
|
|
switch( this->getMessageType() ){
|
|
case TYPE_NEP_PACKET_SPEC:
|
|
this->data_pspec->packet_count=htons(c);
|
|
break;
|
|
|
|
default:
|
|
return OP_FAILURE;
|
|
break;
|
|
}
|
|
return OP_SUCCESS;
|
|
} /* End of setPacketCount() */
|
|
|
|
|
|
/* On failure, it returns 0 */
|
|
u16 EchoHeader::getPacketCount(){
|
|
switch( this->getMessageType() ){
|
|
case TYPE_NEP_PACKET_SPEC:
|
|
return ntohs(this->data_pspec->packet_count);
|
|
break;
|
|
|
|
default:
|
|
return 0;
|
|
break;
|
|
}
|
|
} /* End of getPacketCount() */
|
|
|
|
|
|
int EchoHeader::getFieldLength(u8 field){
|
|
switch(field){
|
|
/* 8bit fields */
|
|
case PSPEC_IPv4_TOS:
|
|
case PSPEC_IPv4_PROTO:
|
|
case PSPEC_IPv6_FLOW:
|
|
case PSPEC_IPv6_NHDR:
|
|
case PSPEC_TCP_FLAGS:
|
|
case PSPEC_ICMP_TYPE:
|
|
case PSPEC_ICMP_CODE:
|
|
return 1;
|
|
break;
|
|
|
|
/* 16bit fields */
|
|
case PSPEC_IPv4_ID:
|
|
case PSPEC_IPv4_FRAGOFF:
|
|
case PSPEC_TCP_SPORT:
|
|
case PSPEC_TCP_DPORT:
|
|
case PSPEC_TCP_WIN:
|
|
case PSPEC_TCP_URP:
|
|
case PSPEC_UDP_SPORT:
|
|
case PSPEC_UDP_DPORT:
|
|
case PSPEC_UDP_LEN:
|
|
return 2;
|
|
break;
|
|
|
|
/* 24bit fields */
|
|
case PSPEC_IPv6_TCLASS:
|
|
return 3;
|
|
break;
|
|
|
|
/* 32bit fields */
|
|
case PSPEC_TCP_SEQ:
|
|
case PSPEC_TCP_ACK:
|
|
return 4;
|
|
break;
|
|
|
|
/* Error */
|
|
case PSPEC_PAYLOAD_MAGIC:
|
|
default:
|
|
return -1;
|
|
break;
|
|
}
|
|
} /* End of getFieldLength() */
|
|
|
|
|
|
int EchoHeader::addFieldSpec(u8 field, u8 *val){
|
|
int flen;
|
|
/* Determine the length of the field */
|
|
if( (flen=this->getFieldLength(field))==-1 || val==NULL )
|
|
return OP_FAILURE;
|
|
else{
|
|
return this->addFieldSpec(field, val, flen);
|
|
}
|
|
} /* End of addFieldSpec() */
|
|
|
|
|
|
int EchoHeader::addFieldSpec(u8 field, u8 *val, size_t flen){
|
|
if( val==NULL ){
|
|
return OP_FAILURE;
|
|
}else{
|
|
/* Store the field spec and update internal pointers and counts */
|
|
if( (this->fs_bytes+flen) < PACKETSPEC_FIELD_LEN ){
|
|
*(this->fs_off)=field;
|
|
if(field==PSPEC_PAYLOAD_MAGIC){
|
|
/* Check length again since this field requires an extra byte */
|
|
if(this->fs_bytes+flen+1 < PACKETSPEC_FIELD_LEN){
|
|
*(this->fs_off+1)=flen;
|
|
memcpy(this->fs_off+2, val, flen);
|
|
this->fs_off+=(flen+2);
|
|
this->fs_bytes+=(flen+2);
|
|
}else{
|
|
return OP_FAILURE;
|
|
}
|
|
}else{
|
|
memcpy(this->fs_off+1, val, flen);
|
|
this->fs_off+=(flen+1);
|
|
this->fs_bytes+=(flen+1);
|
|
}
|
|
}else{
|
|
return OP_FAILURE;
|
|
}
|
|
}
|
|
return OP_SUCCESS;
|
|
} /* End of addFieldSpec() */
|
|
|
|
|
|
int EchoHeader::rewindFieldSpecCounters(){
|
|
this->fs_off=(u8 *)this->data_pspec->packetspec;
|
|
this->fs_bytes=0;
|
|
return OP_SUCCESS;
|
|
} /* rewindFieldSpecCounters */
|
|
|
|
/** @warning dst_buff must be able to hold at least (PACKETSPEC_FIELD_LEN-2) bytes. */
|
|
int EchoHeader::getNextFieldSpec(u8 *field, u8 *dst_buff, size_t *final_len){
|
|
u8 nfield=0;
|
|
int nlen=0;
|
|
if(field==NULL || dst_buff==NULL || this->fs_bytes>=PACKETSPEC_FIELD_LEN)
|
|
return OP_FAILURE;
|
|
/* Determine which is the next field specifier */
|
|
nfield=*(this->fs_off);
|
|
if(nfield==PSPEC_PAYLOAD_MAGIC){
|
|
nlen=(int)*(this->fs_off+1); /* Read length from the packet */
|
|
if(nlen<=0 || nlen>(PACKETSPEC_FIELD_LEN-2) )
|
|
return OP_FAILURE;
|
|
else if( this->fs_bytes+2+nlen>PACKETSPEC_FIELD_LEN)
|
|
return OP_FAILURE;
|
|
else
|
|
memcpy(dst_buff, this->fs_off+2, nlen);
|
|
this->fs_off+=(nlen+2);
|
|
this->fs_bytes+=(nlen+2);
|
|
}else{
|
|
if((nlen=this->getFieldLength(nfield))<=0) /* Determine field length */
|
|
return OP_FAILURE;
|
|
else if(this->fs_bytes+1+nlen>PACKETSPEC_FIELD_LEN)
|
|
return OP_FAILURE;
|
|
else
|
|
memcpy(dst_buff, this->fs_off+1, nlen);
|
|
this->fs_off+=(nlen+1);
|
|
this->fs_bytes+=(nlen+2);
|
|
}
|
|
/* Store data */
|
|
*field=nfield;
|
|
if(final_len!=NULL)
|
|
*final_len=nlen;
|
|
return OP_SUCCESS;
|
|
} /* End of getNextFieldSpec() */
|
|
|
|
|
|
/******************************************************************************/
|
|
/* NEP_PACKET_ECHO methods */
|
|
/******************************************************************************/
|
|
int EchoHeader::setDLT(u16 dlt){
|
|
this->data_echo->dlt_type=htons(dlt);
|
|
return OP_SUCCESS;
|
|
} /* End of setDLT() */
|
|
|
|
|
|
u16 EchoHeader::getDLT(){
|
|
return ntohs(this->data_echo->dlt_type);
|
|
} /* End of getDLT() */
|
|
|
|
|
|
int EchoHeader::setPacketLength(u16 len){
|
|
this->data_echo->packet_len=htons(len);
|
|
return OP_SUCCESS;
|
|
} /* End of setPacketLength() */
|
|
|
|
|
|
u16 EchoHeader::getPacketLength(){
|
|
return ntohs(this->data_echo->packet_len);
|
|
} /* End of setPacketLength() */
|
|
|
|
|
|
int EchoHeader::setEchoedPacket(const u8 *pkt, size_t pktlen){
|
|
int padding=0;
|
|
if(pkt==NULL)
|
|
return OP_FAILURE;
|
|
if(pktlen>MAX_ECHOED_PACKET_LEN){
|
|
pktlen=MAX_ECHOED_PACKET_LEN;
|
|
}
|
|
memcpy(this->data_echo->payload_and_mac, pkt, pktlen);
|
|
if((pktlen+4)%16!=0){
|
|
padding=16-((pktlen+4)%16);
|
|
memset(this->data_echo->payload_and_mac+pktlen, 0, padding);
|
|
}
|
|
this->echo_bytes=pktlen+padding;
|
|
this->echo_mac+=pktlen+padding;
|
|
/* Set the packet length field automatically */
|
|
this->setPacketLength((u16)pktlen);
|
|
this->length = STD_NEP_HEADER_LEN + 4 + this->echo_bytes + MAC_LENGTH;
|
|
assert(this->length%16==0);
|
|
return OP_SUCCESS;
|
|
} /* End of setEchoedPacket() */
|
|
|
|
|
|
/* @warning value stored in final_len is not exactly the actual length of the
|
|
* returned buffer but the value stored in the "Packet Length" field of the
|
|
* NEP_ECHO message. The caller is supposed to validate received packets before
|
|
* trusting that length */
|
|
u8 *EchoHeader::getEchoedPacket(u16 *final_len){
|
|
if(final_len!=NULL)
|
|
*final_len=this->getPacketLength();
|
|
return this->data_echo->payload_and_mac;
|
|
} /* End of getEchoedPacket() */
|
|
|
|
|
|
u8 *EchoHeader::getEchoedPacket(){
|
|
return this->getEchoedPacket(NULL);
|
|
} /* End of getEchoedPacket() */
|
|
|
|
|
|
/** This method tries to update the object's internal counters for a NEP_ECHO
|
|
* packet. This should be used when storing a received NEP_ECHO message in
|
|
* the object. In that case, the internal pointers will not be set up
|
|
* correctly, as the object did not construct the message. Calling this method
|
|
* should fix the internal state of the object and make things like
|
|
* verifyMessageAuthenticationCode() work. */
|
|
int EchoHeader::updateEchoInternals(){
|
|
if( this->getMessageType()!=TYPE_NEP_ECHO )
|
|
return OP_FAILURE;
|
|
|
|
/* Fix echo bytes length */
|
|
this->echo_bytes=this->getPacketLength();
|
|
if((this->echo_bytes+4)%16!=0){
|
|
this->echo_bytes+=16-((this->echo_bytes+4)%16);
|
|
}
|
|
/* Fix MAC offset */
|
|
this->echo_mac=((u8 *)this->data_echo->payload_and_mac)+this->echo_bytes;
|
|
return OP_SUCCESS;
|
|
} /* End of updateEchoInternals() */
|
|
|
|
|
|
/******************************************************************************/
|
|
/* NEP_ERROR methods */
|
|
/******************************************************************************/
|
|
|
|
/** @warning error strings longer than MAX_NEP_ERROR_MSG_LEN-1 will be truncated */
|
|
int EchoHeader::setErrorMessage(const char *err){
|
|
if(err==NULL){
|
|
return OP_FAILURE;
|
|
}else{
|
|
strncpy((char *)this->data_error->errmsg, err, ERROR_MSG_LEN);
|
|
this->data_error->errmsg[ERROR_MSG_LEN-1]='\0';
|
|
}
|
|
return OP_SUCCESS;
|
|
} /* End of setErrorMessage() */
|
|
|
|
/* @warning Returned pointer, points to the start of the "Error Message" field
|
|
* of the NEP_ERROR message. When receiving this kind of messages, there is no
|
|
* guarantee that the field contains printable characters, or that it is NULL
|
|
* terminated. The caller should validate it's contents. It is safe to read
|
|
* MAX_NEP_ERROR_MSG_LEN bytes from the start of the returned buffer pointer. */
|
|
char *EchoHeader::getErrorMessage(){
|
|
return (char *)this->data_error->errmsg;
|
|
} /* End of getErrorMessage() */
|
|
|
|
|
|
/******************************************************************************/
|
|
/* CRYPTOGRAPHY */
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
u8 *EchoHeader::getCiphertextBounds(size_t *final_len){
|
|
return this->getCiphertextBounds(final_len, this->getMessageType());
|
|
}
|
|
|
|
|
|
u8 *EchoHeader::getCiphertextBounds(size_t *final_len, int message_type){
|
|
u8 *start=NULL;
|
|
size_t len=0;
|
|
|
|
switch( message_type ){
|
|
case TYPE_NEP_HANDSHAKE_SERVER: /* this msg is never transmitted encrypted */
|
|
len=0;
|
|
start=(u8 *)&this->h;
|
|
break;
|
|
case TYPE_NEP_HANDSHAKE_CLIENT:
|
|
start=this->data_hsclnt->partner_ip;
|
|
len=32;
|
|
break;
|
|
case TYPE_NEP_HANDSHAKE_FINAL:
|
|
start=this->data_hsfinal->partner_ip;
|
|
len=32;
|
|
break;
|
|
case TYPE_NEP_PACKET_SPEC:
|
|
start=(u8 *)(&this->h);
|
|
len=NEP_PACKETSPEC_LEN-MAC_LENGTH;
|
|
break;
|
|
case TYPE_NEP_READY:
|
|
start=(u8 *)(&this->h);
|
|
len=NEP_READY_LEN-MAC_LENGTH;
|
|
break;
|
|
case TYPE_NEP_ECHO:
|
|
start=(u8 *)(&this->h);
|
|
len=this->length-MAC_LENGTH;
|
|
break;
|
|
case TYPE_NEP_ERROR:
|
|
start=(u8 *)(&this->h);
|
|
len=NEP_ERROR_LEN-MAC_LENGTH;
|
|
break;
|
|
default:
|
|
return NULL;
|
|
break;
|
|
}
|
|
|
|
if(final_len!=NULL)
|
|
*final_len=len;
|
|
return start;
|
|
} /* End of getCiphertextBounds() */
|
|
|
|
|
|
|
|
/** Encrypts the NEP message using the supplied key and initialization vector.
|
|
* On success it returns a pointer to the beginning of the last ciphertext
|
|
* block. This should be stored by the caller and used as the IV for the
|
|
* next encrypted data. It returns NULL in case of error. */
|
|
u8 *EchoHeader::encrypt(u8 *key, size_t key_len, u8 *iv){
|
|
nping_print(DBG_4, "%s(%p, %lu, %p)", __func__, key, (long unsigned)key_len, iv);
|
|
u8 *start=NULL;
|
|
size_t len=0;
|
|
|
|
if(key==NULL || key_len==0 || iv==NULL)
|
|
return NULL;
|
|
|
|
if((start=this->getCiphertextBounds(&len))==NULL)
|
|
return NULL;
|
|
|
|
if(len>=CIPHER_BLOCK_SIZE){
|
|
if( Crypto::aes128_cbc_encrypt(start, len, (u8 *)(&this->h_tmp), key, key_len, iv) != OP_SUCCESS )
|
|
return NULL;
|
|
else{
|
|
memcpy(start, &this->h_tmp, len);
|
|
return (start+(len-CIPHER_BLOCK_SIZE));
|
|
}
|
|
}else{
|
|
return NULL;
|
|
}
|
|
} /* End of encrypt() */
|
|
|
|
|
|
u8 *EchoHeader::decrypt(u8 *key, size_t key_len, u8 *iv, int message_type){
|
|
nping_print(DBG_4, "%s(%p, %lu, %p)", __func__, key, (long unsigned)key_len, iv);
|
|
u8 *start=NULL;
|
|
size_t len=0;
|
|
static u8 lastblock[CIPHER_BLOCK_SIZE];
|
|
|
|
if(key==NULL || key_len==0 || iv==NULL)
|
|
return NULL;
|
|
|
|
if((start=this->getCiphertextBounds(&len, message_type))==NULL)
|
|
return NULL;
|
|
|
|
if(len>=CIPHER_BLOCK_SIZE){
|
|
/* Keep a copy of the last ciphertext block */
|
|
memcpy(lastblock, start+len-CIPHER_BLOCK_SIZE, CIPHER_BLOCK_SIZE);
|
|
if( Crypto::aes128_cbc_decrypt(start, len, (u8 *)(&this->h_tmp), key, key_len, iv) != OP_SUCCESS )
|
|
return NULL;
|
|
else{
|
|
memcpy(start, &this->h_tmp, len);
|
|
return lastblock;
|
|
}
|
|
}else{
|
|
return NULL;
|
|
}
|
|
} /* End of decrypt() */
|