
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <gnutls/gnutls.h>
#include <gnutls/extra.h>

#define SRP_PASSWD "tpasswd"
#define SRP_PASSWD_CONF "tpasswd.conf"

#define KEYFILE "key.pem"
#define CERTFILE "cert.pem"
#define CAFILE "ca.pem"

/* This is a sample TLS-SRP echo server.
 */

#define SA struct sockaddr
#define SOCKET_ERR(err,s) if(err==-1) {perror(s);return(1);}
#define MAX_BUF 1024
#define PORT 5556               /* listen to 5556 port */

/* These are global */
gnutls_srp_server_credentials srp_cred;
gnutls_certificate_credentials cert_cred;

gnutls_session initialize_tls_session()
{
   gnutls_session session;
   const int kx_priority[] = { GNUTLS_KX_SRP, GNUTLS_KX_SRP_DSS,
      GNUTLS_KX_SRP_RSA, 0 };

   gnutls_init(&session, GNUTLS_SERVER);

   gnutls_set_default_priority(session);
   gnutls_kx_set_priority(session, kx_priority);

   gnutls_credentials_set(session, GNUTLS_CRD_SRP, srp_cred);
   /* for the certificate authenticated ciphersuites.
    */
   gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, cert_cred);

   /* request client certificate if any.
    */
   gnutls_certificate_server_set_request( session, GNUTLS_CERT_IGNORE);

   return session;
}

int main()
{
   int err, listen_sd, i;
   int sd, ret;
   struct sockaddr_in sa_serv;
   struct sockaddr_in sa_cli;
   int client_len;
   char topbuf[512];
   gnutls_session session;
   char buffer[MAX_BUF + 1];
   int optval = 1;
   char name[256];

   strcpy(name, "Echo Server");

   /* these must be called once in the program
    */
   gnutls_global_init();
   gnutls_global_init_extra(); /* for SRP */

   /* SRP_PASSWD a password file (created with the included srpcrypt utility) 
    */
   gnutls_srp_allocate_server_credentials(&srp_cred);
   gnutls_srp_set_server_credentials_file(srp_cred, SRP_PASSWD, SRP_PASSWD_CONF);

   gnutls_certificate_allocate_credentials(&cert_cred);
   gnutls_certificate_set_x509_trust_file(cert_cred, CAFILE, GNUTLS_X509_FMT_PEM);
   gnutls_certificate_set_x509_key_file(cert_cred, CERTFILE, KEYFILE,
                                        GNUTLS_X509_FMT_PEM);

   /* TCP socket operations
    */
   listen_sd = socket(AF_INET, SOCK_STREAM, 0);
   SOCKET_ERR(listen_sd, "socket");

   memset(&sa_serv, '\0', sizeof(sa_serv));
   sa_serv.sin_family = AF_INET;
   sa_serv.sin_addr.s_addr = INADDR_ANY;
   sa_serv.sin_port = htons(PORT);  /* Server Port number */

   setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int));

   err = bind(listen_sd, (SA *) & sa_serv, sizeof(sa_serv));
   SOCKET_ERR(err, "bind");
   err = listen(listen_sd, 1024);
   SOCKET_ERR(err, "listen");

   printf("%s ready. Listening to port '%d'.\n\n", name, PORT);

   client_len = sizeof(sa_cli);
   for (;;) {
      session = initialize_tls_session();

      sd = accept(listen_sd, (SA *) & sa_cli, &client_len);

      printf("- connection from %s, port %d\n",
             inet_ntop(AF_INET, &sa_cli.sin_addr, topbuf,
                       sizeof(topbuf)), ntohs(sa_cli.sin_port));

      gnutls_transport_set_ptr( session, (gnutls_transport_ptr)sd);
      ret = gnutls_handshake( session);
      if (ret < 0) {
         close(sd);
         gnutls_deinit(session);
         fprintf(stderr, "*** Handshake has failed (%s)\n\n",
                 gnutls_strerror(ret));
         continue;
      }
      printf("- Handshake was completed\n");

      /* print_info(session); */

      i = 0;
      for (;;) {
         bzero(buffer, MAX_BUF + 1);
         ret = gnutls_record_recv( session, buffer, MAX_BUF);

         if (ret == 0) {
            printf
                ("\n- Peer has closed the GNUTLS connection\n");
            break;
         } else if (ret < 0) {
            fprintf(stderr,
                    "\n*** Received corrupted data(%d). Closing the connection.\n\n",
                    ret);
            break;
         } else if (ret > 0) {
            /* echo data back to the client
             */
            gnutls_record_send( session, buffer,
                         strlen(buffer));
         }
      }
      printf("\n");
      gnutls_bye( session, GNUTLS_SHUT_WR); /* do not wait for
                                 * the peer to close the connection.
                                 */

      close(sd);
      gnutls_deinit(session);

   }
   close(listen_sd);

   gnutls_srp_free_server_credentials(srp_cred);
   gnutls_certificate_free_credentials(cert_cred);

   gnutls_global_deinit();

   return 0;

}

