/*
 * $Source: /usr/local/MASTER/plexus/kerberosIV/s.c,v $
 * $Author: sanders $
 *
 * Copyright 1989 by the Massachusetts Institute of Technology.
 *
 * For copying and distribution information, please see the file
 * <mit-copyright.h>.
 *
 * Simple UDP-based server application.  For demonstration.
 * This program performs no useful function.
 */

#ifndef	lint
static char rcsid_simple_server_c[] =
"$Header: /usr/local/MASTER/plexus/kerberosIV/s.c,v 2.0 1993/05/19 22:40:06 sanders Exp $";
#endif	lint

#include <mit-copyright.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <des.h>
#include <krb.h>
#include "simple.h"

#define SEND(a,b,c,d,e,f) sendto(a,b,c,d,e,f)
#define RECV(a,b,c,d,e,f) recvfrom(a,b,c,d,e,f)

main()
{
	int len, sock, i;
	int flags = 0;			/* for recvfrom() */
	int any = 0;			/* don't care (krb_rd_req) */
	struct servent *serv;
	struct hostent *host;
	struct sockaddr_in s_sock;	/* server's address */
	KTEXT_ST k;
	KTEXT ktxt = &k;		/* Kerberos data */
	AUTH_DAT ad;			/* authentication data */
	char hostname[64];		/* for hostname */

	/* for krb_rd_safe/priv */
	struct sockaddr_in c_sock;	/* client's address */
	MSG_DAT msg_data;		/* received message */

	/* for krb_rd_priv */
	des_key_schedule sched;		/* session key schedule */

	/* Set up server address */
	bzero((char *)&s_sock, sizeof(s_sock));
	s_sock.sin_family = AF_INET;

	/* Look up service */
	if ((serv = getservbyname(SERVICE, "udp")) == NULL) {
		fprintf(stderr, "server: service unknown: %s/udp\n", SERVICE);
		exit(1);
	}
	s_sock.sin_port = serv->s_port;

	if (gethostname(hostname, sizeof(hostname)) < 0) {
	    perror("gethostname");
	    exit(1);
	}

	if ((host = gethostbyname(hostname)) == (struct hostent *)0) {
	    fprintf(stderr, "server: %s: host unknown\n", hostname);
	    exit(1);
	}
	bcopy(host->h_addr, (char *)&s_sock.sin_addr, host->h_length);

	/* Open socket */
	if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
		perror("opening datagram socket");
		exit(1);
	}

	/* Bind the socket */
	if (bind(sock, (struct sockaddr *)&s_sock, sizeof(s_sock))) {
		perror("binding datagram socket");
		exit(1);
	}

#ifdef DEBUG
	printf("server: socket has port # %d\n", ntohs(s_sock.sin_port));
#endif

	/* GET KRB_MK_REQ MESSAGE */

	i = read(sock, (char *)ktxt->dat, MAX_KTXT_LEN);
	if (i < 0) {
		perror("receiving datagram");
		exit(1);
	}
	fprintf(stderr, "server: Received %d bytes\n", i);
	ktxt->length = i;

	/* Check authentication info */
	i = krb_rd_req(ktxt, SERVICE, HOST, any, &ad, "");
#ifdef DEBUG
	printf("server: krb_rd_req returned %d: %s\n", i, krb_err_txt[i]);
#endif
	if (i != KSUCCESS) {
		fprintf(stderr, "server: %s\n", krb_err_txt[i]);
		exit(1);
	}
	printf("server: Got authentication info from %s%s%s@%s\n", ad.pname,
		*ad.pinst ? "." : "", ad.pinst, ad.prealm);

	/* GET KRB_MK_SAFE MESSAGE */

	/* use "recvfrom" so we know client's address */
	i = sizeof(c_sock);
	i = RECV(sock, (char *)ktxt->dat, MAX_KTXT_LEN, flags,
		     (struct sockaddr *)&c_sock, &i);
	if (i < 0) {
		perror("server: receiving datagram"); exit(1); }
#ifdef DEBUG
	printf("&c_sock.sin_addr is %s\n", inet_ntoa(c_sock.sin_addr));
#endif
	printf("Received %d bytes\n", i);

	/* Verify the checksummed message */
	i = krb_rd_safe(ktxt->dat, i, ad.session, &c_sock,
			&s_sock, &msg_data);
#ifdef DEBUG
	printf("krb_rd_safe returned %d: %s\n", i, krb_err_txt[i]);
#endif
	if (i != KSUCCESS) {
		fprintf(stderr, "%s\n", krb_err_txt[i]);
		exit(1);
	}
	printf("Safe message is: %s\n", msg_data.app_data);

	/* XXX: VERIFY CLIENT */

{
	char buf[32];
	sprintf(buf, "%ld", time(NULL));
#ifdef NOENCRYPTION
	bzero((char *)sched, sizeof(sched));
#else
	/* Get key schedule for session key */
	des_key_sched(ad.session, sched);
#endif
	fprintf(stderr, "server: encoding message %s\n", buf);
	i = krb_mk_priv(buf, ktxt->dat, strlen(buf)+1,
	        sched, ad.session, &s_sock, &c_sock);
	sleep(1);
	fprintf(stderr, "server: sending message %s len=%d\n", buf, i);
	i = SEND(sock, (char *)ktxt->dat, i, flags,
	         (struct sockaddr *)&c_sock, sizeof(c_sock));
	if (i < 0) {
		perror("server: sending datagram"); exit(1); }
	fprintf(stderr, "server: waiting for response\n");
	i = RECV(sock, (char *)ktxt->dat, MAX_KTXT_LEN, flags,
		 (struct sockaddr *)&c_sock, &i);
	if (i < 0) {
		perror("server: receiving datagram"); exit(1); }
        i = krb_rd_safe(ktxt->dat, i, ad.session, &c_sock,
                        &s_sock, &msg_data);
	fprintf(stderr, "server: got %s\n", msg_data.app_data);
        if (i != KSUCCESS) {
                fprintf(stderr, "server: %s\n", krb_err_txt[i]);
                exit(1);
        }
        if (strncmp(msg_data.app_data, buf, 32) != 0) {
                fprintf(stderr, "server: DATA MISMATCH\n");
                exit(1);
	} else {
		fprintf(stderr, "server: client ok\n");
	}
}

	
	/* NOW GET ENCRYPTED MESSAGE */

#ifdef NOENCRYPTION
	bzero((char *)sched, sizeof(sched));
#else
	/* need key schedule for session key */
	des_key_sched(ad.session, sched);
#endif

	/* use "RECV" so we know client's address */
	i = sizeof(c_sock);
	i = RECV(sock, (char *)ktxt->dat, MAX_KTXT_LEN, flags,
		     (struct sockaddr *)&c_sock, &i);
	if (i < 0) {
		perror("receiving datagram");
		exit(1);
	}
	printf("Received %d bytes\n", i);
	i = krb_rd_priv(ktxt->dat, i, sched, ad.session, &c_sock,
			&s_sock, &msg_data);
#ifdef DEBUG
	printf("krb_rd_priv returned %d: %s\n", i, krb_err_txt[i]);
#endif
	if (i != KSUCCESS) {
		fprintf(stderr, "%s\n", krb_err_txt[i]);
		exit(1);
	}
	printf("Decrypted message is: %s\n", msg_data.app_data);
	return(0);
}
