
/***************************************************************************
 * 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-2014 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() */
