/*
 * © Copyright 1996-2012 ECMWF.
 * 
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
 * In applying this licence, ECMWF does not waive the privileges and immunities 
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

/*
** File: ecmarscert.c
**
** Purpose: Shared definitions and code for member state MARS client and
**          ECMWF MARS server used by routines handling the creation and
**          validation of ECMarsCertificates.
**
** $Date: 1996/03/26 17:06:05 $ $Revision: 1.6 $
*/
#include "eccert.h"

#include <string.h>
#include <unistd.h>

#define ECDIGESTLEN (2 * sizeof(digestType) + 1)
#define ECMAXMARSCERTLEN  512

char *OKECMARSCERT         = NULL;
char EECCMDCLIENTFAILURE[] = "ECCMD client has failed!";
char EECDIGESTMISMATCH  [] = "The message digest does not match data!";
char EECMARSCERTREJECT  [] = "MARS certificate rejected!";
char EECMARSLIMREJECT   [] = "MARS certificate no longer valid!";
char EECMARSUNVERIFIED  [] = "Unverified passcode!";
char EECNOBIND          [] = "Can't bind to socket!";
char EECNOCMDCLIENT     [] = "ECCMD client is not running!";
char EECNOCMDSERVER     [] = "ECCMD server is not running!";
char EECNORESOLVE       [] = "Can't resolve hostname!";
char EECNORESPONSE      [] = "ECCMD client doesn't respond!";
char EECNOSEND          [] = "Can't send to server!";
char EECNOSOCKET        [] = "Can't create socket!";
char EECNOSPACE         [] = "Insufficient memory for MARS certificate!";
char EECPASSCODEINVALID [] = "Passcode no longer valid!";
char EECPASSCODEREJECT  [] = "Passcode rejected!";
char EECSERVERINTERNAL  [] = "Internal server error!";

/* MARS server (running at ECMWF): */
/* =============================== */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/uio.h>

char *
verify_ecmars_certificate(
     char            *data,			/* Data to be certified */
     int             len,			/* its length in bytes */
     char            *marsCert,		/* Certificate Area */
     int             marsCertLen,   /* its length in bytes */
     eCMarsCertReply *reply			/* Structure to receive reply */
) {
    int   sd;

   /* Connect and send mars certificate to ECMWF certification server */
    { struct sockaddr_in client;
      struct hostent     *hent;
      struct sockaddr_in server;

      if ((sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
        return EECNOSOCKET;

      client.sin_family      = AF_INET;
      client.sin_port        = htons(0);
      client.sin_addr.s_addr = htonl(INADDR_ANY);
      if (bind(sd, (struct sockaddr *)&client, sizeof(client))<0)
        return EECNOBIND;

      if (!(hent=gethostbyname(ECQ_SERV_HOST))) 
        return EECNORESOLVE;

      server.sin_family = AF_INET;
      server.sin_port   = htons(ECQ_SERV_PORT);
      server.sin_addr   = *(struct in_addr *)*hent->h_addr_list;

      if (connect(sd, (struct sockaddr *)&server, sizeof(server))<0)
        return EECNOCMDSERVER;

     /* Send the mars certificate and append the data
     ** NOTE: THIS INFORMATION IS EXPECTED TO ARRIVE IN 
     **       A SINGLE PACKET ON THE ECCMD SERVER AND IS THEREFORE SENT
     **       IN A SINGLE REQUEST.
     **/
      { struct iovec iov[2];

        iov[0].iov_base = marsCert; iov[0].iov_len = marsCertLen;
        iov[1].iov_base = data;     iov[1].iov_len = len;
        if (writev(sd, iov, 2)<0) {
          close(sd);
          return EECNOSEND;
        };
      };
    };

   /* Receive reply and check if mars certificate is valid. */
    { short n = read(sd, reply, sizeof(*reply));

      close(sd);

      if (n < sizeof(*reply) || reply->protocolID != ECMARSCERTPROTOCOLID)
        reply->returnCode = RCSINTERNAL;

      switch (reply->returnCode) {
        case RCSPASSCODEREJECT:  return EECPASSCODEINVALID;
        case RCSPASSCODERETRY:   return EECPASSCODEREJECT;
        case RCSFAIL:            return EECMARSCERTREJECT;
        case RCSLIMREJECT:       return EECMARSLIMREJECT;
        case RCSMARSOK:          return OKECMARSCERT;
      };
    };

    return EECSERVERINTERNAL;
}
