/* diffie-socket.c: socket wrapper with key exchange and authentication - main file
   This has nothing to do with cryptography.
   Copyright (C) 1998 Paul Sheer

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   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
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */


#include "mostincludes.h"
#include <assert.h>
#include <stdarg.h>
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
#include <sys/types.h>
#if defined(HAVE_UNISTD_H)
#include <unistd.h>
#endif
#if HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#include <netdb.h>		/* struct hostent */
#include <sys/socket.h>		/* AF_INET */
#include <netinet/in.h>		/* struct in_addr */
#ifdef HAVE_SETSOCKOPT
#include <netinet/ip.h>		/* IP options */
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_ARPA_FTP_H
#include <arpa/ftp.h>
#endif
#ifdef HAVE_ARPA_TELNET_H
#include <arpa/telnet.h>
#endif
#ifndef SCO_FLAVOR
#include <sys/time.h>		/* alex: this redefines struct timeval */
#endif				/* SCO_FLAVOR */
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif

#ifdef USE_TERMNET
#include <termnet.h>
#endif

#include <sys/file.h>

#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64
#endif

#include "huge-number.h"
#include "diffie-socket.h"
#include "field.h"
#include "random.h"
#include "parse.h"
#include "arc.h"
#include "diffie/compat.h"
#include "src/mad.h"

#undef accept
#undef connect
#undef rcmd
#undef send
#undef recv
#undef read
#undef write
#undef shutdown
#ifdef WIN32
#undef closesocket
#else
#undef close
#endif
#undef dup
#undef dup2

int diffie_errno = 0;

/*
 * PROTOCOL:
 *
 *   Server:
 *
 *   0r - recv DIFFIE_ARC_SOCKET_MAGIC + protocol version + exchange type diffie +  +
 *          remote pub key
 *   0s - send DIFFIE_ARC_SOCKET_MAGIC_REPLY + pub key + 
 *          (turn encryption on) signature pub key +
 *          signed r + signed s
 *
 *   1r - recv data
 *   etc.
 *
 *   Client:
 *
 *   0s - send DIFFIE_ARC_SOCKET_MAGIC + protocol version +
 *          pub key
 *   0r - recv DIFFIE_ARC_SOCKET_MAGIC_REPLY + remote pub key +
 *          (turn decryption on) remote signature pub key +
 *          remote signed r + remote signed s
 *
 *   check remote signature pub key matches database
 *   decompose r and s and check with private key
 *
 *   1s - send data
 *   etc.
 *
 */

#define MAX_FDS_PER_CONN 32

typedef struct arcsocket {
/* two encryption streams: one for reading, and one for writing */
    Arc arc_read;
    Arc arc_write;
    int fd[MAX_FDS_PER_CONN];
    int n_fd;
    int shutdown;
    struct arcsocket *next;
    struct arcsocket *prev;
} ArcSocket;

struct parse_info {
    int fd;
    int type;			/* size of prime to be used */
    struct in_addr addr;
    Arc *arc_read;
    Arc *arc_write;
};

static int recv_all (int s, unsigned char *buf, int len, unsigned int flags)
{
    int count;
    int total = 0;
    while (len > 0) {
	count = recv (s, buf, len, flags);
	if (count == -1 && errno == EINTR)
	    continue;
	if (count <= 0)
	    return -1;
	buf += count;
	len -= count;
	total += count;
    }
    return total;
}

static int send_all (int s, unsigned char *buf, int len, unsigned int flags)
{
    int count;
    int total = 0;
    while (len > 0) {
	count = send (s, buf, len, flags);
	if (count == -1 && errno == EINTR)
	    continue;
	if (count <= 0)
	    return -1;
	buf += count;
	len -= count;
	total += count;
    }
    return total;
}

/* list of live connections */
static ArcSocket *connections = 0;

#define free_zero(x,n)					\
    {							\
	if (x) {					\
	    memset (x, 0, n);				\
	    free (x);					\
	    (x) = 0;					\
	}						\
    }

static ArcSocket *arc_socket_index (int fd)
{
    ArcSocket *c;
    for (c = connections; c; c = c->next) {
	int i;
	for (i = 0; i < c->n_fd; i++)
	    if (c->fd[i] == fd)
		return c;
    }
    return 0;
}

#define MY_WAIT_FOR_ALL (1<<30)

static int arc_socket_flags = ARC_SOCKET_FLAGS_PRIME_1024;

void arc_socket_set_flags (unsigned int flags)
{
    arc_socket_flags = flags;
}

unsigned int arc_socket_get_flags (void)
{
    return arc_socket_flags;
}

static int (*arc_socket_warn_callback) (char *, void *) = 0;
static void *arc_socket_user_data = 0;

/* returns null on error */
static Huge *huge_read (int fd, int encrypted)
{
    Huge *v;
    unsigned char lc[2];
    int li;
    unsigned char buf[MAX_FEASABLE_HUGE + 1];
    if (encrypted) {
	if (arc_socket_recv (fd, lc, 2, MY_WAIT_FOR_ALL) != 2)
	    return 0;
    } else {
	if (recv_all (fd, lc, 2, 0) != 2)
	    return 0;
    }
    li = (int) lc[0];
    li |= (int) lc[1] << 8;
    if (li > MAX_FEASABLE_HUGE) {
	diffie_errno = DE_TOHUGE;
	return 0;
    }
    if (encrypted) {
	if (arc_socket_recv (fd, buf, li, MY_WAIT_FOR_ALL) != li) {
	    memset (buf, 0, 256);
	    diffie_errno = DE_READ;
	    return 0;
	}
    } else {
	if (recv_all (fd, buf, li, 0) != li) {
	    memset (buf, 0, 256);
	    diffie_errno = DE_READ;
	    return 0;
	}
    }
    v = huge_from_binary (buf, li);
    memset (buf, 0, 256);
    return v;
}

/* returns 1 on error */
static int huge_write (int fd, Huge * v, int encrypted)
{
    char *c;
    unsigned char lc[2];
    int li;
    c = huge_as_binary (v, &li);
    lc[0] = (unsigned char) li & 0xFF;
    lc[1] = (unsigned char) (li >> 8) & 0xFF;
    if (encrypted) {
	if (arc_socket_send (fd, lc, 2, MY_WAIT_FOR_ALL) != 2) {
	    free_zero (c, li);
	    diffie_errno = DE_WRITE;
	    return 1;
	}
	if (arc_socket_send (fd, c, li, MY_WAIT_FOR_ALL) != li) {
	    free_zero (c, li);
	    diffie_errno = DE_WRITE;
	    return 1;
	}
    } else {
	if (send_all (fd, lc, 2, 0) != 2) {
	    free_zero (c, li);
	    diffie_errno = DE_WRITE;
	    return 1;
	}
	if (send_all (fd, (unsigned char *) c, li, 0) != li) {
	    free_zero (c, li);
	    diffie_errno = DE_WRITE;
	    return 1;
	}
    }
    free_zero (c, li);
    return 0;
}

static int huge_write_file (int fd, Huge * v);
static Huge *huge_read_file (int fd);

static char *load_file (char *f)
{
    char *r, *result;
    int fd;
    struct stat s;
    if (stat (f, &s))
	return 0;
    if (s.st_size < 3)
	return 0;
    result = r = malloc (s.st_size + 1);
    fd = open (f, O_RDONLY);
    result[s.st_size] = '\0';
    while (s.st_size > 0) {
	int count;
	count = read (fd, r, s.st_size);
	if (count < 0 && errno == EINTR)
	    continue;
	if (count <= 0)
	    break;
	s.st_size -= count;
	r += count;
    }
    close (fd);
    return result;
}

static int lock_file_wait (int f, int type)
{
#ifdef F_SETLKW
    struct flock l;
    l.l_type = type;
    l.l_whence = SEEK_SET;
    l.l_start = 0;
    l.l_len = 1024;
    l.l_pid = getpid ();
    return fcntl (f, F_SETLKW, &l);
#else
    return 0;
#endif
}


static int create_etc_key_dir (void)
{
    struct stat s;

/* create an /usr/etc directory to hold the public and private keys */
    if (lstat ((const char *) KEY_DIR, (struct stat *) &s)) {
	if (errno != ENOENT) {	/* some other error, abort to be safe */
	    diffie_errno = DE_HOSTSIGNDIR;
	    return 1;
	}
	if (mkdir (KEY_DIR, 0755)) {	/* directory never created, so create it now */
	    diffie_errno = DE_HOSTSIGNDIR;
	    return 1;
	}
    } else {
	if (!S_ISDIR (s.st_mode)) {	/* what this? not a directory - some funny stuff going on */
	    diffie_errno = DE_HOSTSIGNDIR;
	    return 1;
	}
    }

/* create an /usr/etc private key directory */
    if (lstat (PRIVATE_KEY_DIR, &s)) {
	if (errno != ENOENT) {	/* some other error, abort to be safe */
	    diffie_errno = DE_HOSTSIGNDIR;
	    return 1;
	}
	if (mkdir (PRIVATE_KEY_DIR, 0700)) {	/* directory never created, so create it now */
	    diffie_errno = DE_HOSTSIGNDIR;
	    return 1;
	}
    } else {
	if (!S_ISDIR (s.st_mode)) {	/* what this? not a directory - some funny stuff going on */
	    diffie_errno = DE_HOSTSIGNDIR;
	    return 1;
	}
    }

/* create an /usr/etc public key directory */
    if (lstat (PUBLIC_KEY_DIR, &s)) {
	if (errno != ENOENT) {	/* some other error, abort to be safe */
	    diffie_errno = DE_HOSTSIGNDIR;
	    return 1;
	}
	if (mkdir (PUBLIC_KEY_DIR, 0755)) {	/* directory never created, so create it now */
	    diffie_errno = DE_HOSTSIGNDIR;
	    return 1;
	}
    } else {
	if (!S_ISDIR (s.st_mode)) {	/* what this? not a directory - some funny stuff going on */
	    diffie_errno = DE_HOSTSIGNDIR;
	    return 1;
	}
    }
    return 0;
}

/* this will create public and private keys the first time it is run */
static int save_signature_keys (Huge * signature_private_key, Huge * signature_public_key, int type)
{
    char f[1024];
    int fd;

    if (create_etc_key_dir ())
	return 1;

/* create a private key file */
    if (signature_private_key) {
	sprintf (f, PRIVATE_KEY_FILE, field_size (type));
	if ((fd = creat (f, S_IWUSR | S_IRUSR)) < 0) {
	    diffie_errno = DE_HOSTSIGNPRIV;
	    return 1;
	}
#ifdef F_WRLCK
	if (lock_file_wait (fd, F_WRLCK)) {
	    diffie_errno = DE_HOSTSIGNPRIV;
	    close (fd);
	    return 1;
	}
#endif
	huge_write_file (fd, signature_private_key);
	close (fd);
    }
/* create a public key file */
    if (signature_public_key) {
	sprintf (f, PUBLIC_KEY_FILE, field_size (type));
	if ((fd = creat (f, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) < 0) {
	    diffie_errno = DE_HOSTSIGNPUBL;
	    return 1;
	}
#ifdef F_WRLCK
	if (lock_file_wait (fd, F_WRLCK)) {
	    diffie_errno = DE_HOSTSIGNPUBL;
	    close (fd);
	    return 1;
	}
#endif
	huge_write_file (fd, signature_public_key);
	close (fd);
    }
    return 0;
}

/* this will create public and private keys the first time it is run */
static int load_signature_keys (Huge ** signature_private_key, Huge ** signature_public_key, int type)
{
    struct stat s;
    char f[1024];
    int fd;

    if (create_etc_key_dir ())
	return 1;

/* read a private key file */
    sprintf (f, PRIVATE_KEY_FILE, field_size (type));
    if (lstat (f, &s)) {
	return 0;	/* simply does not exist yet */
    } else {
	if (!S_ISREG (s.st_mode)) {
	    diffie_errno = DE_HOSTSIGNPRIV;
	    return 1;
	}
	if ((fd = open (f, O_RDONLY)) < 0) {
	    diffie_errno = DE_HOSTSIGNPRIV;
	    return 1;
	}
#ifdef F_RDLCK
	if (lock_file_wait (fd, F_RDLCK)) {
	    diffie_errno = DE_HOSTSIGNPRIV;
	    close (fd);
	    return 1;
	}
#endif
    }
    *signature_private_key = huge_read_file (fd);
    close (fd);

/* read a public key file */
    sprintf (f, PUBLIC_KEY_FILE, field_size (type));
    if (lstat (f, &s)) {
	return 0;	/* simply does not exist yet */
    } else {
	if (!S_ISREG (s.st_mode)) {
	    diffie_errno = DE_HOSTSIGNPUBL;
	    return 1;
	}
	if ((fd = open (f, O_RDONLY)) < 0) {
	    diffie_errno = DE_HOSTSIGNPUBL;
	    return 1;
	}
#ifdef F_RDLCK
	if (lock_file_wait (fd, F_RDLCK)) {
	    diffie_errno = DE_HOSTSIGNPUBL;
	    close (fd);
	    return 1;
	}
#endif
    }
    *signature_public_key = huge_read_file (fd);
    close (fd);
    return 0;
}

/* replacement for accept */
int arc_socket_accept (int sock, struct sockaddr *addr, unsigned int *addrlen)
{
    int fd;
    fd = accept (sock, addr, (unsigned int *) addrlen);
    if (fd < 0)
	return fd;
    return arc_socket_accept_fd (fd);
}

/* call this if fd has just come from an accept */
int arc_socket_accept_fd (int fd)
{
    struct parse_info p;
    ArcSocket *c = 0;
    void *s = 0;
    char *f = 0;
    int result = 0;
    Value heap[32];
    if (fd < 0)
	return fd;
/* normal accept */
    if ((arc_socket_flags & ARC_SOCKET_FLAGS_CRYPTO_OFF))
	return fd;
/* new connection in list */
    c = malloc (sizeof (ArcSocket));
    memset (c, 0, sizeof (ArcSocket));
    c->next = connections;
    if (c->next)
	c->next->prev = c;
    connections = c;
    c->fd[c->n_fd++] = fd;
/* used by script functions */
    p.fd = fd;
    memset (&p.addr, 0, sizeof (p.addr));	/* not used on the host side */
    p.arc_read = &c->arc_read;
    p.arc_write = &c->arc_write;
/* compile and interpret script */
    if (!create_etc_key_dir ()) {
	result = parser_evaluate (s = parser_compile (f = load_file (SCRIPT_ACCEPT), heap), &p);
	if (!s) {
	    fprintf (stderr, "diffie: script compilation failed - have your scripts been downloaded?\n");
	    result = -1;
	}
	parser_free (s, heap);
	if (f)
	    free (f);
    }
/* check success */
    if (result <= 0) {
	close (fd);
	errno = EFAULT;
	connections = c->next;
	if (connections)
	    connections->prev = 0;
/* FIXME: does not free arc struct */
	free_zero (c, sizeof (ArcSocket));
	return -1;
    }
    if (!c->arc_read.initialised || !c->arc_write.initialised) {
	/* accept script never turn on encrpytion, reverting to ordinary insecure accept() */
	connections = c->next;
	if (connections)
	    connections->prev = 0;
	free_zero (c, sizeof (ArcSocket *));
    }
    return fd;
}

#undef min
#define min(x,y) ((x) < (y) ? (x) : (y))

static char *strncpy_lots (char *r, int max,...)
{
    char *result = r;
    char *p;
    va_list ap;
    va_start (ap, max);
    *r = '\0';
    while (max > 0 && (p = va_arg (ap, char *))) {
	strncpy (r, p, max - 1);
	r[max - 1] = '\0';
	max -= strlen (r);
	r += strlen (r);
    }
    va_end (ap);
    return result;
}


/* why bother with key management, just store a key in a file with the ip as the name */
static int verify_signature_public_key (struct in_addr addr, Huge * signature_public_key, int type)
{
    char f[1024];
    char size[16];
    int fd, new_public = 0;
    struct stat s;
    int result = 0;
    Huge *verifier_key;
    char *home_dir;

    home_dir = (getpwuid (getuid ()))->pw_dir;

    sprintf (size, "%d", field_size (type));

/* create a directory for public keys in user's HOME */
    strncpy_lots (f, 1024, home_dir, PATH_SEP_STR USER_KEY_DIR, 0);
    if (lstat (f, &s)) {
	if (errno != ENOENT) {	/* some other error, abort to be safe FIXME */
	    diffie_errno = DE_USERSIGNDIR;
	    return 1;
	}
	if (mkdir (f, 0700)) {	/* directory never created, so create it now */
	    diffie_errno = DE_USERSIGNDIR;
	    return 1;
	}
    } else {
	if (!S_ISDIR (s.st_mode)) {	/* what this? not a directory - some funny stuff going on */
	    diffie_errno = DE_USERSIGNDIR;
	    return 1;
	}
    }

/* create a directory for public keys in user's HOME */
    strncpy_lots (f, 1024, home_dir, PATH_SEP_STR USER_KEY_DIR PATH_SEP_STR, inet_ntoa (addr), 0);
    mkdir (f, 0700);
    if (lstat (f, &s)) {
	if (errno != ENOENT) {	/* some other error, abort to be safe FIXME */
	    diffie_errno = DE_USERSIGNDIR;
	    return 1;
	}
	if (mkdir (f, 0700)) {	/* directory never created, so create it now */
	    diffie_errno = DE_USERSIGNDIR;
	    return 1;
	}
    } else {
	if (!S_ISDIR (s.st_mode)) {	/* what this? not a directory - some funny stuff going on */
	    diffie_errno = DE_USERSIGNDIR;
	    return 1;
	}
    }

/* create, or read a user public key file */
    strncpy_lots (f, 1024, home_dir, PATH_SEP_STR USER_KEY_DIR PATH_SEP_STR, inet_ntoa (addr), PATH_SEP_STR, size, 0);
    if (lstat (f, &s)) {
	if (errno != ENOENT) {
	    diffie_errno = DE_USERSIGNFILE;
	    return 1;
	}
	if (arc_socket_warn_callback)
	    if ((*arc_socket_warn_callback) (f, arc_socket_user_data))
		return 1;
    }
    if (lstat (f, &s)) {
	if (errno != ENOENT) {
	    diffie_errno = DE_USERSIGNFILE;
	    return 1;
	}
	if ((fd = creat (f, S_IWUSR | S_IRUSR)) < 0) {
	    diffie_errno = DE_USERSIGNFILE;
	    return 1;
	}
	new_public = 1;
    } else {
	if (!S_ISREG (s.st_mode)) {
	    diffie_errno = DE_USERSIGNFILE;
	    return 1;
	}
	if ((fd = open (f, O_RDONLY)) < 0) {
	    diffie_errno = DE_USERSIGNFILE;
	    return 1;
	}
    }

/* read public if in the file */
    if (!new_public) {
	if (!(verifier_key = huge_read_file (fd))) {
	    diffie_errno = DE_USERPUBFMT;
	    result = 1;
	} else {
	    result = (huge_compare (verifier_key, signature_public_key) != 0);
	    if (result)
		diffie_errno = DE_USERPUBKEY;
	}
	huge_free (verifier_key);
    } else {
	huge_write_file (fd, signature_public_key);
    }

    close (fd);
    return result;
}

static int arc_socket_init_fd (int fd, struct in_addr addr);

int arc_socket_connect (int fd, struct sockaddr *addr, int addrlen)
{
    int result;
    struct sockaddr_in s;
    result = connect (fd, addr, addrlen);
    if (result < 0)
	return result;
    if ((arc_socket_flags & ARC_SOCKET_FLAGS_CRYPTO_OFF))
	return result;

    memset (&s, 0, sizeof (struct sockaddr_in));
    memcpy (&s, addr, min (sizeof (struct sockaddr_in), sizeof (struct sockaddr)));

    return arc_socket_init_fd (fd, s.sin_addr);
}

static int arc_socket_init_fd (int fd, struct in_addr addr)
{
    ArcSocket *c = 0;
    int result = 0;
    struct parse_info p;
    void *s = 0;
    char *f = 0;
    Value heap[32];
/* new connection in list */
    c = malloc (sizeof (ArcSocket));
    memset (c, 0, sizeof (ArcSocket));
    c->next = connections;
    if (c->next)
	c->next->prev = c;
    connections = c;
    c->fd[c->n_fd++] = fd;
/* used by script functions */
    p.fd = fd;
    p.type = arc_socket_flags & ARC_SOCKET_FLAGS_PRIME;
    p.addr = addr;		/* used to look up the hosts public key */
    p.arc_read = &c->arc_read;
    p.arc_write = &c->arc_write;
/* compile and interpret script */
    if (!create_etc_key_dir ()) {
	result = parser_evaluate (s = parser_compile (f = load_file (SCRIPT_CONNECT), heap), &p);
	if (!s) {
	    fprintf (stderr, "diffie: script compilation failed - have your scripts been downloaded?\n");
	    result = -1;
	}
	parser_free (s, heap);
	if (f)
	    free (f);
    }
/* check success */
    if (result <= 0) {
	close (fd);
	errno = EFAULT;
	connections = c->next;
	if (connections)
	    connections->prev = 0;
	free_zero (c, sizeof (ArcSocket));
	return -1;
    } else {
	return fd;
    }
}

int arc_socket_send (int s, const void *msg, int len, unsigned int flags)
{
    ArcSocket *c;
    Arc arcsave;
    unsigned char *t;
    int result;
    c = arc_socket_index (s);
    if (!c || (flags & MSG_OOB))	/* not opened with arc_socket */
	return send (s, msg, len, flags);
    arcsave = c->arc_write;
    t = malloc (len + 1);
    memcpy (t, msg, len);
    arc_encrypt (&c->arc_write, t, len);
    if (flags & MY_WAIT_FOR_ALL)
	result = send_all (s, t, len, flags & ~MY_WAIT_FOR_ALL);
    else
	result = send (s, t, len, flags);
/* send may not have sent all, so we must rerun our arc to the currect offset */
    if (result != len) {
	c->arc_write = arcsave;
	if (result > 0)
	    arc_encrypt (&c->arc_write, t, result);
    }
    free (t);
    return result;
}

int arc_socket_write (int s, void *msg, int len)
{
    ArcSocket *c;
    Arc arcsave;
    unsigned char *t;
    int result;
    c = arc_socket_index (s);
    if (!c)			/* not opened with arc_socket */
	return write (s, msg, len);
    arcsave = c->arc_write;
    t = malloc (len + 1);
    memcpy (t, msg, len);
    arc_encrypt (&c->arc_write, t, len);
    result = write (s, t, len);
/* send may not have sent all, so we must rerun our arc to the currect offset */
    if (result != len) {
	c->arc_write = arcsave;
	if (result > 0)
	    arc_encrypt (&c->arc_write, t, result);
    }
    free (t);
    return result;
}

int arc_socket_recv (int s, void *buf, int len, unsigned int flags)
{
    ArcSocket *c;
    int result;
    c = arc_socket_index (s);
    if (!c || (flags & MSG_OOB))
	return recv (s, buf, len, flags);
    if (flags & MY_WAIT_FOR_ALL)
	result = recv_all (s, (unsigned char *) buf, len, flags & ~MY_WAIT_FOR_ALL);
    else
	result = recv (s, buf, len, flags);
    if (result > 0) {
	if (flags & MSG_PEEK) {
	    Arc arcsave;
	    memcpy (&arcsave, &c->arc_read, sizeof (arcsave));
	    arc_decrypt (&c->arc_read, buf, result);
	    memcpy (&c->arc_read, &arcsave, sizeof (arcsave));
	} else {
	    arc_decrypt (&c->arc_read, buf, result);
	}
    }
    return result;
}

int arc_socket_read (int s, void *buf, int len)
{
    ArcSocket *c;
    int result;
    c = arc_socket_index (s);
    if (!c)			/* not opened with arc_socket */
	return read (s, buf, len);
    result = read (s, buf, len);
    if (result > 0)
	arc_decrypt (&c->arc_read, buf, result);
    return result;
}

void arc_socket_remove_connection (ArcSocket * c, int fd)
{
    int i;
    for (i = 0; i < c->n_fd; i++)
	if (c->fd[i] == fd) {
	    memcpy (&c->fd[i], &c->fd[i + 1], (c->n_fd - i - 1) * sizeof (int));
	    c->n_fd--;
	    if (c->n_fd)	/* still fds bound to this arc */
		return;
	    break;
	}
    arc_shut (&c->arc_read);
    arc_shut (&c->arc_write);
    if (c->next)
	c->next->prev = c->prev;
    if (c->prev)
	c->prev->next = c->next;
    if ((unsigned long) c == (unsigned long) connections)
	connections = connections->next;
    free_zero (c, sizeof (ArcSocket *));
}

static int arc_socket_xdup (int fd, int fd_new)
{
    ArcSocket *c;
    if (fd_new < 0)
	return fd_new;
    c = arc_socket_index (fd);
    if (!c)
	return fd_new;
    if (c->n_fd >= MAX_FDS_PER_CONN) {
	close (fd_new);
	errno = EMFILE;
	return -1;
    }
    c->fd[c->n_fd++] = fd_new;
    return fd_new;
}

int arc_socket_dup2 (int fd, int fd_new)
{
    int f;
    f = dup2 (fd, fd_new);
    return arc_socket_xdup (fd, f);
}

int arc_socket_dup (int fd)
{
    int f;
    f = dup (fd);
    return arc_socket_xdup (fd, f);
}

int arc_socket_shutdown (int s, int how)
{
    ArcSocket *c;
    int r;
    c = arc_socket_index (s);
    if (!c)
	return shutdown (s, how);
    c->shutdown |= (how == 0) | ((how == 1) * 2) | ((how == 2) * 3);
    if (c->shutdown == 3)
	arc_socket_remove_connection (c, s);
    r = shutdown (s, how);
    return r;
}

int arc_socket_close (int s)
{
    ArcSocket *c;
    c = arc_socket_index (s);
    if (!c)
#ifdef WIN32
	return closesocket (s);
#else
	return close (s);
#endif
    arc_socket_remove_connection (c, s);
    return close (s);
}

char *arc_socket_error_strings[128] =
{
    "no error",
    "permission or other problem with hosts " SYSCONFDIR " key directory",
    "permission or other problem with hosts private key file",
    "format problem with hosts private key file",
    "permission or other problem with hosts public key file",
    "format problem with hosts public key file",
    "error reading magic number from client",
    "error writing public key for dh key exchange",
    "pnew signature scheme failed to generate a verified key",
    "something wrong with calculated dh private key",
    "error reading protocol version",
    "bad protocol version",
    "error sending or recieving hosts public signature",
    "error reading from socket",
    "error reading public key for dh key exchange",
    "error reading or writing signed dh key",
    "a huge number somewhere was to big",
    "format problem with users public signature key file of a host",
    "users public signature key file of a host does not match received",
    "permission or other problem with users signature key directory",
    "permission or other problem with public signature key file",
    "verified signed key does not match",
    "error writing to socket"
};

char *arc_socket_error_string (int e)
{
    return arc_socket_error_strings[e];
}

/* your warn_callback function must return 1 to quit the connection */
void arc_socket_add_warning_callback (int (*warn_callback) (char *, void *), void *user_data)
{
    arc_socket_warn_callback = warn_callback;
    arc_socket_user_data = user_data;
}

static int op_readhuge (struct parse_info *s, ValueStack ** v)
{
    Value value1;
    int r = 0;
    Huge *h;
    value1 = parser_value_pop (v);
    h = huge_read (s->fd, value1.v.i);
    if (!h) {
	h = huge_from_long (0L);
	r = 1;
    }
    parser_push_huge_free (v, h);
    parser_value_free (value1);
    return r;
}

static int op_writehuge (struct parse_info *s, ValueStack ** v)
{
    Value value1, value2;
    int r = 0;
    value2 = parser_value_pop (v);
    value1 = parser_value_pop (v);
    r = huge_write (s->fd, value1.v.h, value2.v.i);
    parser_push_int (v, r);
    parser_value_free (value1);
    parser_value_free (value2);
    return 0;
}

static int op_checksavedkey (struct parse_info *s, ValueStack ** v)
{
    Value value1, value2;
    value2 = parser_value_pop (v);
    value1 = parser_value_pop (v);
    parser_push_int (v, verify_signature_public_key (s->addr, value1.v.h, value2.v.i));
    parser_value_free (value1);
    parser_value_free (value2);
    return 0;
}

static int op_loadkeys (struct parse_info *s, ValueStack ** v)
{
    Value value1, value2, value3;
    Huge *x = 0, *y = 0;
    Value *p;
    value3 = parser_value_pop (v);
    value2 = parser_value_pop (v);
    value1 = parser_value_pop (v);
    parser_push_int (v, load_signature_keys (&x, &y, value3.v.i));

    p = (Value *) value1.v.p;
    parser_value_free (*p);
    (*p).type = VALUE_HUGE | VALUE_FREE;
    (*p).v.h = x;

    p = (Value *) value2.v.p;
    parser_value_free (*p);
    (*p).type = VALUE_HUGE | VALUE_FREE;
    (*p).v.h = y;

    parser_value_free (value1);
    parser_value_free (value2);
    parser_value_free (value3);
    return 0;
}

static int op_savekeys (struct parse_info *s, ValueStack ** v)
{
    Value value1, value2, value3;
    value3 = parser_value_pop (v);
    value2 = parser_value_pop (v);
    value1 = parser_value_pop (v);
    parser_push_int (v, save_signature_keys (value1.v.h, value2.v.h, value3.v.i));
    parser_value_free (value1);
    parser_value_free (value2);
    parser_value_free (value3);
    return 0;
}

extern void *watch_free_pointer;

static int op_recv (struct parse_info *s, ValueStack ** v)
{
    Value value1, value2, value3;
    char *c;
    int l;
    Value *p;
    value3 = parser_value_pop (v);
    value2 = parser_value_pop (v);
    value1 = parser_value_pop (v);

    c = malloc (value2.v.i + 1);
    c[0] = '\0';
    parser_push_int (v, l = recv_all (s->fd, (unsigned char *) c, value2.v.i, value3.v.i));
    c[value2.v.i] = '\0';

    p = (Value *) value1.v.p;
    parser_value_free (*p);
    (*p).type = VALUE_STRING | VALUE_FREE | ((l < 0 ? 0 : l) & VALUE_LEN);
    (*p).v.s = c;

    parser_value_free (value1);
    parser_value_free (value2);
    parser_value_free (value3);
    return 0;
}

static int op_typeoption (struct parse_info *s, ValueStack ** v)
{
    parser_push_int (v, s->type);
    return 0;
}

static int op_send (struct parse_info *s, ValueStack ** v)
{
    Value value1, value2, value3;
    value3 = parser_value_pop (v);
    value2 = parser_value_pop (v);
    value1 = parser_value_pop (v);
    parser_push_int (v, send_all (s->fd, (unsigned char *) value1.v.s, (int) value2.v.i, (unsigned int) value3.v.i));
    parser_value_free (value1);
    parser_value_free (value2);
    parser_value_free (value3);
    return 0;
}

static int op_huge2bin (struct parse_info *s, ValueStack ** v)
{
    Value value1, value2, value3;
    int l = 0;
    Value *p;
    value3 = parser_value_pop (v);
    value2 = parser_value_pop (v);
    value1 = parser_value_pop (v);
    parser_push_int (v, 0);

    p = (Value *) value2.v.p;
    parser_value_free (*p);
    (*p).v.s = huge_as_binary (value1.v.h, &l);
    (*p).type = VALUE_STRING | VALUE_FREE | (l & VALUE_LEN);

    p = (Value *) value3.v.p;
    parser_value_free (*p);
    (*p).type = VALUE_LONG;
    (*p).v.i = l;

    parser_value_free (value1);
    parser_value_free (value2);
    return 0;
}

static int op_initarcwr (struct parse_info *s, ValueStack ** v)
{
    Value value1, value2;
    value2 = parser_value_pop (v);
    value1 = parser_value_pop (v);
    parser_push_int (v, 0);
    arc_init (s->arc_write, (unsigned char *) value1.v.s, value2.v.i);
    parser_value_free (value1);
    parser_value_free (value2);
    return 0;
}

static int op_initarcrd (struct parse_info *s, ValueStack ** v)
{
    Value value1, value2;
    value2 = parser_value_pop (v);
    value1 = parser_value_pop (v);
    parser_push_int (v, 0);
    arc_init (s->arc_read, (unsigned char *) value1.v.s, value2.v.i);
    parser_value_free (value1);
    parser_value_free (value2);
    return 0;
}

static int op_prime (struct parse_info *s, ValueStack ** v)
{
    Value value1;
    value1 = parser_value_pop (v);
    parser_push_huge_free (v, field_prime (value1.v.i));
    parser_value_free (value1);
    return 0;
}

static int op_random (struct parse_info *s, ValueStack ** v)
{
    Value value1;
    value1 = parser_value_pop (v);
    parser_push_huge_free (v, get_random_huge (value1.v.i));
    parser_value_free (value1);
    return 0;
}

static int op_typesize (struct parse_info *s, ValueStack ** v)
{
    Value value1;
    value1 = parser_value_pop (v);
    parser_push_int (v, field_size (value1.v.i));
    parser_value_free (value1);
    return 0;
}

#ifndef HAVE_BUILTIN_ARC

static int op_key (Arc * arc, ValueStack ** v)
{
    Value value;
    value.v.s = arc->key;
    value.type = VALUE_STRING | arc->key_size;
    parser_value_push (v, value);
    return 0;
}

static int op_data (Arc * arc, ValueStack ** v)
{
    Value value1;
    Value *p;
    value1 = parser_value_pop (v);
    parser_push_int (v, 0);
    p = (Value *) value1.v.p;
    parser_value_free (*p);
    (*p).type = VALUE_STRING | (arc->len & VALUE_LEN);
    (*p).v.s = arc->data;
    parser_value_free (value1);
    return 0;
}

static int op_box (Arc * arc, ValueStack ** v)
{
    Value value;
    value.v.p = arc->s;
    value.type = VALUE_LONG | VALUE_P0;
    parser_value_push (v, value);
    return 0;
}

#endif

static Operator operators[] =
{
    {0, "readhuge", 0, OP_USER_FUNCTION, 1, (Func) op_readhuge, 0, 1},
    {0, "writehuge", 0, OP_USER_FUNCTION, 1, (Func) op_writehuge, 0, 2},
    {0, "loadkeys", 0, OP_USER_FUNCTION, 1, (Func) op_loadkeys, 0, 3},
    {0, "savekeys", 0, OP_USER_FUNCTION, 1, (Func) op_savekeys, 0, 3},
    {0, "send", 0, OP_USER_FUNCTION, 1, (Func) op_send, 0, 3},
    {0, "recv", 0, OP_USER_FUNCTION, 1, (Func) op_recv, 0, 3},
    {0, "typeoption", 0, OP_USER_FUNCTION, 1, (Func) op_typeoption, 0, 0},
    {0, "checksavedkey", 0, OP_USER_FUNCTION, 1, (Func) op_checksavedkey, 0, 2},
    {0, "huge2bin", 0, OP_USER_FUNCTION, 1, (Func) op_huge2bin, 0, 3},
    {0, "initarcrd", 0, OP_USER_FUNCTION, 1, (Func) op_initarcrd, 0, 2},
    {0, "initarcwr", 0, OP_USER_FUNCTION, 1, (Func) op_initarcwr, 0, 2},
    {0, "prime", 0, OP_USER_FUNCTION, 1, (Func) op_prime, 0, 1},
    {0, "random", 0, OP_USER_FUNCTION, 1, (Func) op_random, 0, 1},
    {0, "typesize", 0, OP_USER_FUNCTION, 1, (Func) op_typesize, 0, 1},
#ifndef HAVE_BUILTIN_ARC
    {0, "box", 0, OP_USER_FUNCTION, 1, (Func) op_box, 0, 0},
    {0, "data", 0, OP_USER_FUNCTION, 1, (Func) op_data, 0, 1},
    {0, "key", 0, OP_USER_FUNCTION, 1, (Func) op_key, 0, 0},
#endif
    {0, "MSG_PEEK", 0, OP_CONSTANT, 1, (Func) MSG_PEEK, 0, 0}
};

static int n_operator = sizeof (operators) / sizeof (Operator);

void diffie_add_functions (void)
{
    int i;
    for (i = 0; i < n_operator; i++)
	parser_add_operator (operators + i);
}

#undef read
#undef write

/* returns null on error */
static Huge *huge_read_file (int fd)
{
    Huge *v;
    unsigned char lc[2];
    int li;
    unsigned char buf[MAX_FEASABLE_HUGE + 1];
    if (read (fd, lc, 2) != 2)
	return 0;
    li = (int) lc[0];
    li |= (int) lc[1] << 8;
    if (li > MAX_FEASABLE_HUGE) {
	diffie_errno = DE_TOHUGE;
	return 0;
    }
    if (read (fd, buf, li) != li) {
	memset (buf, 0, 256);
	diffie_errno = DE_READ;
	return 0;
    }
    v = huge_from_binary (buf, li);
    memset (buf, 0, 256);
    return v;
}

/* returns 1 on error */
static int huge_write_file (int fd, Huge * v)
{
    char *c;
    unsigned char lc[2];
    int li;
    c = huge_as_binary (v, &li);
    lc[0] = (unsigned char) li & 0xFF;
    lc[1] = (unsigned char) (li >> 8) & 0xFF;
    if (write (fd, lc, 2) != 2) {
	free_zero (c, li);
	diffie_errno = DE_WRITE;
	return 1;
    }
    if (write (fd, c, li) != li) {
	free_zero (c, li);
	diffie_errno = DE_WRITE;
	return 1;
    }
    free_zero (c, li);
    return 0;
}

