aboutsummaryrefslogtreecommitdiffstats
path: root/README
Commit message (Expand)AuthorAgeFilesLines
* Update and reformat.Matthias Andree2009-05-241-35/+53
* Add a pointer to INSTALL.Matthias Andree2005-09-251-0/+2
* Update links to fetchmail home page.Matthias Andree2005-04-271-2/+2
* Version bump.Eric S. Raymond2003-02-281-7/+2
* Note about the TODO.Eric S. Raymond2001-09-241-0/+3
* Authentication is orthogonal now.Eric S. Raymond2001-02-251-8/+8
* First cut at ODMR support.Eric S. Raymond2001-02-071-2/+3
* Ready to merge in Julian Haight's changes.Eric S. Raymond2000-06-071-1/+1
* BeOS support.Eric S. Raymond2000-04-081-6/+4
* Added a TODO list.Eric S. Raymond2000-02-121-1/+2
* The real 5.2.0.Eric S. Raymond1999-12-191-1/+1
* Added IRIX.Eric S. Raymond1999-09-301-1/+1
* Plotting is working pretty well.Eric S. Raymond1999-09-241-5/+6
* Feature update.Eric S. Raymond1999-06-131-5/+2
* Added socks library support.Eric S. Raymond1998-11-201-3/+4
* Added experimental SDPS support.Eric S. Raymond1998-08-031-3/+3
* Changed WWW hosts.Eric S. Raymond1998-05-141-1/+1
* Henrik Storner's patch to support mimedecode.Eric S. Raymond1998-03-201-0/+2
* Note OS/2 port.Eric S. Raymond1998-01-181-0/+3
* Doc fix.Eric S. Raymond1997-12-181-2/+2
* Note that GSSAPI is supported.Eric S. Raymond1997-12-151-2/+2
* Typo fix.Eric S. Raymond1997-11-291-1/+1
* Integrated RPA support.Eric S. Raymond1997-09-301-2/+3
* More portation changes.Eric S. Raymond1997-09-171-4/+7
* First round of changes for OPIE support.Eric S. Raymond1997-07-171-3/+6
* We run on AmigaOS.Eric S. Raymond1997-07-031-1/+1
* Reorganization for Web page structure.Eric S. Raymond1997-06-131-99/+10
* Add exim 501 response.Eric S. Raymond1997-06-021-6/+7
* Added blurbs on new features.Eric S. Raymond1997-05-281-1/+9
* Caseblind comparison of names, more explicitness about Kerberos.Eric S. Raymond1997-05-131-1/+1
* Added forcecr option.Eric S. Raymond1997-04-171-2/+2
* These features are in.Eric S. Raymond1997-04-011-1/+3
* Aded the `received' option.Eric S. Raymond1997-02-141-4/+6
* Added RFC1985 ETRN support.Eric S. Raymond1997-02-101-0/+2
* Add stripcr option.Eric S. Raymond1997-01-301-0/+3
* Break down features by major release.Eric S. Raymond1997-01-221-19/+22
* Remove obsolete caveat.Eric S. Raymond1997-01-221-4/+0
* General ESMTP support seems pretty good now.Eric S. Raymond1997-01-221-14/+16
* Support for EHLO and 8BITMIME extension.Eric S. Raymond1997-01-221-0/+2
* Update beta list number.Eric S. Raymond1997-01-211-1/+1
* RPOP support is back.Eric S. Raymond1997-01-201-1/+3
* Ready to ship to George Sipe.Eric S. Raymond1997-01-171-1/+1
* Feature clarification.Eric S. Raymond1997-01-161-4/+4
* Port info.Eric S. Raymond1997-01-101-4/+5
* Document new features.Eric S. Raymond1997-01-101-4/+21
* Mods to George Sipe's interface option.Eric S. Raymond1996-12-251-5/+8
* Ready to release.Eric S. Raymond1996-12-211-3/+4
* Add full IMAP4 capability.Eric S. Raymond1996-12-091-4/+5
* Version bump.Eric S. Raymond1996-12-091-1/+1
* Ready to ship.Eric S. Raymond1996-11-291-24/+10
'>555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800
/*
 * socket.c -- socket library functions
 *
 * Copyright 1998 by Eric S. Raymond.
 * For license terms, see the file COPYING in this directory.
 */

#include "config.h"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif /* HAVE_MEMORY_H */
#include <sys/types.h>
#ifndef HAVE_NET_SOCKET_H
#include <sys/socket.h>
#else
#include <net/socket.h>
#endif
#include <netinet/in.h>
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#include <netdb.h>
#if defined(STDC_HEADERS)
#include <stdlib.h>
#endif
#if defined(HAVE_UNISTD_H)
#include <unistd.h>
#endif
#if defined(HAVE_STDARG_H)
#include <stdarg.h>
#else
#include <varargs.h>
#endif
#include "socket.h"
#include "fetchmail.h"
#include "i18n.h"

/* Defines to allow BeOS to play nice... */
#ifdef __BEOS__
static char peeked;
#define fm_close(a)  closesocket(a)
#define fm_write(a,b,c)  send(a,b,c,0)
#define fm_peek(a,b,c)   recv(a,b,c,0)
#define fm_read(a,b,c)   recv(a,b,c,0)
#else
#define fm_close(a)  close(a)
#define fm_write(a,b,c)  write(a,b,c)
#define fm_peek(a,b,c)   recv(a,b,c, MSG_PEEK)
#define fm_read(a,b,c)   read(a,b,c)
#endif

/* We need to define h_errno only if it is not already */
#ifndef h_errno

#ifdef HAVE_RES_SEARCH
/* some versions of FreeBSD should declare this but don't */
extern int h_errno;
#else
/* pretend we have h_errno to avoid some #ifdef's later */
static int h_errno;
#endif

#endif /* ndef h_errno */

#if NET_SECURITY
#include <net/security.h>
#endif /* NET_SECURITY */

#ifdef HAVE_SOCKETPAIR
const char **parse_plugin(const char *plugin, const char *host, const char *service)
{	const char **argvec;
	char *c, *p, *plugin_copy;
	unsigned int s = 2 * sizeof(char*), i;

	plugin_copy = strdup(plugin);
	if (!plugin_copy)
	{
		report(stderr, _("fetchmail: malloc failed\n"));
		return NULL;
	}
	for (c = p = plugin_copy; *c; c++)
	{	if (isspace(*c) && !isspace(*p))
			s += sizeof(char*);
		p = c;
	}
	argvec = malloc(s);
	if (!argvec)
	{
		report(stderr, _("fetchmail: malloc failed\n"));
		return NULL;
	}
	memset(argvec, 0, s);
	for (c = p = plugin_copy, i = 0; *c; c++)
	{	if ((!isspace(*c)) && (c == p ? 1 : isspace(*p))) {
			argvec[i] = c;
			i++;
		}
		p = c;
	}
	for (c = plugin_copy; *c; c++)
	{	if (isspace(*c))
			*c = 0;
	}
	for (i = 0; argvec[i]; i++)
	{	if (strcmp(argvec[i], "%h") == 0)
			argvec[i] = host;
		if (strcmp(argvec[i], "%p") == 0)
			argvec[i] = service;
	}
	return argvec;
}

static int handle_plugin(const char *host,
			 const char *service, const char *plugin)
/* get a socket mediated through a given external command */
{
    int fds[2];
    char *const *argvec;
    if (socketpair(AF_UNIX,SOCK_STREAM,0,fds))
    {
	report(stderr, _("fetchmail: socketpair failed\n"));
	return -1;
    }
    switch (fork()) {
	case -1:
		/* error */
		report(stderr, _("fetchmail: fork failed\n"));
		return -1;
		break;
	case 0:	/* child */
		/* fds[1] is the parent's end; close it for proper EOF
		** detection */
		(void) close(fds[1]);
		if ( (dup2(fds[0],0) == -1) || (dup2(fds[0],1) == -1) ) {
			report(stderr, _("dup2 failed\n"));
			exit(1);
		}
		/* fds[0] is now connected to 0 and 1; close it */
		(void) close(fds[0]);
		if (outlevel >= O_VERBOSE)
		    report(stderr, _("running %s %s %s\n"), plugin, host, service);
		argvec = (char *const *)parse_plugin(plugin,host,service);
		execvp(*argvec, argvec);
		report(stderr, _("execvp(%s) failed\n"), *argvec);
		exit(0);
		break;
	default:	/* parent */
		/* NOP */
		break;
    }
    /* fds[0] is the child's end; close it for proper EOF detection */
    (void) close(fds[0]);
    return fds[1];
}
#endif /* HAVE_SOCKETPAIR */

#ifdef __UNUSED__
#include <sys/time.h>

int SockCheckOpen(int fd)
/* poll given socket; is it selectable? */
{
    fd_set r, w, e;
    int rt;
    struct timeval tv;
  
    for (;;) 
    {
	FD_ZERO(&r); FD_ZERO(&w); FD_ZERO(&e);
	FD_SET(fd, &e);
    
	tv.tv_sec = 0; tv.tv_usec = 0;
	rt = select(fd+1, &r, &w, &e, &tv);
	if (rt == -1 && (errno != EAGAIN && errno != EINTR))
	    return 0;
	if (rt != -1)
	    return 1;
    }
}
#endif /* __UNUSED__ */

#if INET6_ENABLE
int SockOpen(const char *host, const char *service, const char *options,
	     const char *plugin)
{
    struct addrinfo *ai, *ai0, req;
    int i;
#if NET_SECURITY
    void *request = NULL;
    int requestlen;
#endif /* NET_SECURITY */

#ifdef HAVE_SOCKETPAIR
    if (plugin)
	return handle_plugin(host,service,plugin);
#endif /* HAVE_SOCKETPAIR */
    memset(&req, 0, sizeof(struct addrinfo));
    req.ai_socktype = SOCK_STREAM;

    if (getaddrinfo(host, service, &req, &ai0)) {
	report(stderr, _("fetchmail: getaddrinfo(%s.%s)\n"), host,service);
	return -1;
    }

#if NET_SECURITY
    if (!options)
	requestlen = 0;
    else
	if (net_security_strtorequest((char *)options, &request, &requestlen))
	    goto ret;

    i = inner_connect(ai0, request, requestlen, NULL, NULL, "fetchmail", NULL);
    if (request)
	free(request);

 ret:
#else /* NET_SECURITY */
#ifdef HAVE_INNER_CONNECT
    i = inner_connect(ai0, NULL, 0, NULL, NULL, "fetchmail", NULL);
    if (i >= 0)
	break;
#else
    i = -1;
    for (ai = ai0; ai; ai = ai->ai_next) {
	i = socket(ai->ai_family, ai->ai_socktype, 0);
	if (i < 0)
	    continue;
	if (connect(i, (struct sockaddr *) ai->ai_addr, ai->ai_addrlen) < 0) {
	    fm_close(i);
	    i = -1;
	    continue;
	}
	break;
    }
#endif
#endif /* NET_SECURITY */

    freeaddrinfo(ai0);

    return i;
}
#else /* INET6_ENABLE */
#ifndef HAVE_INET_ATON
#ifndef  INADDR_NONE
#ifdef   INADDR_BROADCAST
#define  INADDR_NONE	INADDR_BROADCAST
#else
#define	 INADDR_NONE	-1
#endif
#endif
#endif /* HAVE_INET_ATON */

int SockOpen(const char *host, int clientPort, const char *options,
	     const char *plugin)
{
    int sock = -1;	/* pacify -Wall */
#ifndef HAVE_INET_ATON
    unsigned long inaddr;
#endif /* HAVE_INET_ATON */
    struct sockaddr_in ad, **pptr;
    struct hostent *hp;

#ifdef HAVE_SOCKETPAIR
    if (plugin) {
      char buf[10];
      sprintf(buf,"%d",clientPort);
      return handle_plugin(host,buf,plugin);
    }
#endif /* HAVE_SOCKETPAIR */

    memset(&ad, 0, sizeof(ad));
    ad.sin_family = AF_INET;

    /* we'll accept a quad address */
#ifndef HAVE_INET_ATON
    inaddr = inet_addr(host);
    if (inaddr != INADDR_NONE)
    {
        memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr));
#else
    if (inet_aton(host, &ad.sin_addr))
    {
#endif /* HAVE_INET_ATON */
        ad.sin_port = htons(clientPort);

        sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock < 0)
        {
            h_errno = 0;
            return -1;
        }
        if (connect(sock, (struct sockaddr *) &ad, sizeof(ad)) < 0)
        {
            int olderr = errno;
            fm_close(sock);	/* don't use SockClose, no traffic yet */
            h_errno = 0;
            errno = olderr;
            return -1;
        }
#ifndef HAVE_INET_ATON
    }
#else
    }
#endif /* HAVE_INET_ATON */
    else {
        hp = gethostbyname(host);

        if (hp == NULL)
	{
	    errno = 0;
	    return -1;
	}
	/*
	 * Add a check to make sure the address has a valid IPv4 or IPv6
	 * length.  This prevents buffer spamming by a broken DNS.
	 */
	if(hp->h_length != 4 && hp->h_length != 8)
	{
	    h_errno = errno = 0;
	    report(stderr, 
		   _("fetchmail: illegal address length received for host %s\n"),host);
	    return -1;
	}
	/*
	 * Try all addresses of a possibly multihomed host until we get
	 * a successful connect or until we run out of addresses.
	 */
	pptr = (struct sockaddr_in **)hp->h_addr_list;
	for(; *pptr != NULL; pptr++)
	{
	    sock = socket(AF_INET, SOCK_STREAM, 0);
	    if (sock < 0)
	    {
		h_errno = 0;
		return -1;
	    }
	    ad.sin_port = htons(clientPort);
	    memcpy(&ad.sin_addr, *pptr, sizeof(struct in_addr));
	    if (connect(sock, (struct sockaddr *) &ad, sizeof(ad)) == 0)
		break; /* success */
	    fm_close(sock);	/* don't use SockClose, no traffic yet */
	    memset(&ad, 0, sizeof(ad));
	    ad.sin_family = AF_INET;
	}
	if(*pptr == NULL)
	{
	    int olderr = errno;
	    fm_close(sock);	/* don't use SockClose, no traffic yet */
	    h_errno = 0;
	    errno = olderr;
	    return -1;
	}
    }

    return(sock);
}
#endif /* INET6_ENABLE */


#if defined(HAVE_STDARG_H)
int SockPrintf(int sock, const char* format, ...)
{
#else
int SockPrintf(sock,format,va_alist)
int sock;
char *format;
va_dcl {
#endif

    va_list ap;
    char buf[8192];

#if defined(HAVE_STDARG_H)
    va_start(ap, format) ;
#else
    va_start(ap);
#endif
#ifdef HAVE_VSNPRINTF
    vsnprintf(buf, sizeof(buf), format, ap);
#else
    vsprintf(buf, format, ap);
#endif
    va_end(ap);
    return SockWrite(sock, buf, strlen(buf));

}

#ifdef SSL_ENABLE
#include "openssl/ssl.h"
#include "openssl/err.h"
#include "openssl/pem.h"
#include "openssl/x509.h"

static	SSL_CTX *_ctx = NULL;
static	SSL *_ssl_context[FD_SETSIZE];

SSL	*SSLGetContext( int );
#endif /* SSL_ENABLE */

int SockWrite(int sock, char *buf, int len)
{
    int n, wrlen = 0;
#ifdef	SSL_ENABLE
    SSL *ssl;
#endif

    while (len)
    {
#ifdef SSL_ENABLE
	if( NULL != ( ssl = SSLGetContext( sock ) ) )
		n = SSL_write(ssl, buf, len);
	else
       	n = fm_write(sock, buf, len);
#else
        n = fm_write(sock, buf, len);
#endif
        if (n <= 0)
            return -1;
        len -= n;
	wrlen += n;
	buf += n;
    }
    return wrlen;
}

int SockRead(int sock, char *buf, int len)
{
    char *newline, *bp = buf;
    int n;
#ifdef	SSL_ENABLE
    SSL *ssl;
#endif

    if (--len < 1)
	return(-1);
#ifdef __BEOS__
    if (peeked != 0){
        (*bp) = peeked;
        bp++;
        len--;
        peeked = 0;
    }
#endif        
    do {
	/* 
	 * The reason for these gymnastics is that we want two things:
	 * (1) to read \n-terminated lines,
	 * (2) to return the true length of data read, even if the
	 *     data coming in has embedded NULS.
	 */
#ifdef	SSL_ENABLE
	if( NULL != ( ssl = SSLGetContext( sock ) ) ) {
		/* Hack alert! */
		/* OK...  SSL_peek works a little different from MSG_PEEK
			Problem is that SSL_peek can return 0 if there
			is no data currently available.  If, on the other
			hand, we loose the socket, we also get a zero, but
			the SSL_read then SEGFAULTS!  To deal with this,
			we'll check the error code any time we get a return
			of zero from SSL_peek.  If we have an error, we bail.
			If we don't, we read one character in SSL_read and
			loop.  This should continue to work even if they
			later change the behavior of SSL_peek
			to "fix" this problem...  :-(	*/
		if ((n = SSL_peek(ssl, bp, len)) < 0) {
			return(-1);
		}
		if( 0 == n ) {
			/* SSL_peek says no data...  Does he mean no data
			or did the connection blow up?  If we got an error
			then bail! */
			if( 0 != ( n = ERR_get_error() ) ) {
				return -1;
			}
			/* We didn't get an error so read at least one
				character at this point and loop */
			n = 1;
			/* Make sure newline start out NULL!
			 * We don't have a string to pass through
			 * the strchr at this point yet */
			newline = NULL;
		} else if ((newline = memchr(bp, '\n', n)) != NULL)
			n = newline - bp + 1;
		if ((n = SSL_read(ssl, bp, n)) == -1) {
			return(-1);
		}
		/* Check for case where our single character turned out to
		 * be a newline...  (It wasn't going to get caught by
		 * the strchr above if it came from the hack...  ). */
		if( NULL == newline && 1 == n && '\n' == *bp ) {
			/* Got our newline - this will break
				out of the loop now */
			newline = bp;
		}
	} else {
		if ((n = fm_peek(sock, bp, len)) <= 0)
			return(-1);
		if ((newline = memchr(bp, '\n', n)) != NULL)
			n = newline - bp + 1;
		if ((n = fm_read(sock, bp, n)) == -1)
			return(-1);
	}
#else

#ifdef __BEOS__
    if ((n = fm_read(sock, bp, 1)) <= 0)
#else
    if ((n = fm_peek(sock, bp, len)) <= 0)
#endif
        return (-1);
	if ((newline = memchr(bp, '\n', n)) != NULL)
	    n = newline - bp + 1;
#ifndef __BEOS__
	if ((n = fm_read(sock, bp, n)) == -1)
	    return(-1);
#endif /* __BEOS__ */
#endif
	bp += n;
	len -= n;
    } while 
	    (!newline && len);
    *bp = '\0';
    return bp - buf;
}

int SockPeek(int sock)
/* peek at the next socket character without actually reading it */
{
    int n;
    char ch;
#ifdef	SSL_ENABLE
    SSL *ssl;
#endif

#ifdef	SSL_ENABLE
	if( NULL != ( ssl = SSLGetContext( sock ) ) ) {
		n = SSL_peek(ssl, &ch, 1);
		if( 0 == n ) {
			/* This code really needs to implement a "hold back"
			 * to simulate a functioning SSL_peek()...  sigh...
			 * Has to be coordinated with the read code above.
			 * Next on the list todo...	*/

			/* SSL_peek says no data...  Does he mean no data
			or did the connection blow up?  If we got an error
			then bail! */
			if( 0 != ( n = ERR_get_error() ) ) {
				return -1;
			}

			/* Haven't seen this case actually occur, but...
			   if the problem in SockRead can occur, this should
			   be possible...  Just not sure what to do here.
			   This should be a safe "punt" the "peek" but don't
			   "punt" the "session"... */

			return 0;	/* Give him a '\0' character */
		}
	} else {
    		n = fm_peek(sock, &ch, 1);
	}
#else

        n = fm_peek(sock, &ch, 1);

#endif /* SSL_ENABLE */
	if (n == -1)
		return -1;

#ifdef __BEOS__
    peeked = ch;
#endif
    return(ch);
}

#ifdef SSL_ENABLE

static	char *_ssl_server_cname = NULL;

SSL *SSLGetContext( int sock )
{
	/* If SSLOpen has never initialized - just return NULL */
	if( NULL == _ctx )
		return NULL;

	if( sock < 0 || sock > FD_SETSIZE )
		return NULL;
	return _ssl_context[sock];
}


int SSL_verify_callback( int ok_return, X509_STORE_CTX *ctx )
{
	char buf[260];
	char cbuf[260];
	char ibuf[260];
	char *str_ptr;
	X509 *x509_cert;
	int err, depth;

	x509_cert = X509_STORE_CTX_get_current_cert(ctx);
	err = X509_STORE_CTX_get_error(ctx);
	depth = X509_STORE_CTX_get_error_depth(ctx);

	X509_NAME_oneline(X509_get_subject_name(x509_cert), buf, 256);
	X509_NAME_oneline(X509_get_issuer_name(x509_cert), ibuf, 256);

	/* Just to be sure those buffers are terminated...  I think the
		X509 libraries do, but... */
	buf[256] = ibuf[256] = '\0';

	if (depth == 0) {
		if( ( str_ptr = strstr( ibuf, "/O=" ) ) ) {
			str_ptr += 3;
			strcpy( cbuf, str_ptr );
			if( ( str_ptr = strchr(cbuf, '/' ) ) ) {
				*str_ptr = '\0';
			}
			if (outlevel == O_VERBOSE)
				report(stdout, "Issuer Organization: %s\n", cbuf );
		} else {
			if (outlevel == O_VERBOSE)
				report(stdout, "Unknown Organization\n", cbuf );
		}
		if( ( str_ptr = strstr( ibuf, "/CN=" ) ) ) {
			str_ptr += 4;
			strcpy( cbuf, str_ptr );
			if( ( str_ptr = strchr(cbuf, '/' ) ) ) {
				*str_ptr = '\0';
			}
			if (outlevel == O_VERBOSE)
				report(stdout, "Issuer CommonName: %s\n", cbuf );
		} else {
			if (outlevel == O_VERBOSE)
				report(stdout, "Unknown Issuer CommonName\n", cbuf );
		}
		if( ( str_ptr = strstr( buf, "/CN=" ) ) ) {
			str_ptr += 4;
			strcpy( cbuf, str_ptr );
			if( ( str_ptr = strchr(cbuf, '/' ) ) ) {
				*str_ptr = '\0';
			}
			if (outlevel == O_VERBOSE)
				report(stdout, "Server CommonName: %s\n", cbuf );
			/* Should we have some wildcarding here? */
			if ( NULL != _ssl_server_cname
			     && 0 != strcasecmp( cbuf, _ssl_server_cname ) ) {
				report(stdout,
				       "Server CommonName mismatch: %s != %s\n",
				       cbuf, _ssl_server_cname );
			}
		} else {
			if (outlevel == O_VERBOSE)
				report(stdout, "Unknown Server CommonName\n", cbuf );
		}
	}

	switch (ctx->error) {
	case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
		X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, 256);
		report(stdout, _("unknown issuer= %s"), buf);
		break;
	case X509_V_ERR_CERT_NOT_YET_VALID:
	case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
		report(stderr, _("Server Certificate not yet valid"));
		break;
	case X509_V_ERR_CERT_HAS_EXPIRED:
	case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
		report(stderr, _("Server Certificate expired"));
		break;
	}
	/* We are not requiring or validating server or issuer id's as yet */
	/* Always return OK from here */
	ok_return = 1;
	return( ok_return );
}


/* performs initial SSL handshake over the connected socket
 * uses SSL *ssl global variable, which is currently defined
 * in this file
 */
int SSLOpen(int sock, char *mycert, char *mykey, char *servercname )
{
	SSL_load_error_strings();
	SSLeay_add_ssl_algorithms();
	
	if( sock < 0 || sock > FD_SETSIZE ) {
		report(stderr, "File descriptor out of range for SSL" );
		return( -1 );
	}

	if( ! _ctx ) {
		/* Be picky and make sure the memory is cleared */
		memset( _ssl_context, 0, sizeof( _ssl_context ) );
		_ctx = SSL_CTX_new(SSLv23_client_method());
		if(_ctx == NULL) {
			ERR_print_errors_fp(stderr);
			return(-1);
		}
	}
	
	_ssl_context[sock] = SSL_new(_ctx);
	
	if(_ssl_context[sock] == NULL) {
		ERR_print_errors_fp(stderr);
		return(-1);
	}
	
	/* This static is for the verify callback */
	_ssl_server_cname = servercname;

        SSL_CTX_set_verify(_ctx, SSL_VERIFY_PEER, SSL_verify_callback);

	if( mycert || mykey ) {

	/* Ok...  He has a certificate file defined, so lets declare it.  If
	 * he does NOT have a separate certificate and private key file then
	 * assume that it's a combined key and certificate file.
	 */
		if( !mykey )
			mykey = mycert;
		if( !mycert )
			mycert = mykey;
        	SSL_use_certificate_file(_ssl_context[sock], mycert, SSL_FILETYPE_PEM);
        	SSL_use_RSAPrivateKey_file(_ssl_context[sock], mykey, SSL_FILETYPE_PEM);
	}

	SSL_set_fd(_ssl_context[sock], sock);
	
	if(SSL_connect(_ssl_context[sock]) == -1) {
		ERR_print_errors_fp(stderr);
		return(-1);
	}
	
	return(0);
}
#endif

int SockClose(int sock)
/* close a socket gracefully */
{
#ifdef	SSL_ENABLE
    SSL *ssl;

    if( NULL != ( ssl = SSLGetContext( sock ) ) ) {
        /* Clean up the SSL stack */
        SSL_free( _ssl_context[sock] );
        _ssl_context[sock] = NULL;
    }
#endif

#ifdef __UNUSED__
    /* 
     * This hangs in RedHat 6.2 after fetchmail runs for a while a
     * FIN_WAIT2 comes up in netstat and fetchmail never returns from
     * the recv system call. (Reported from jtnews
     * <jtnews@bellatlantic.net>, Wed, 24 May 2000 21:26:02.)
     *
     * Half-close the connection first so the other end gets notified.
     *
     * This stops sends but allows receives (effectively, it sends a
     * TCP <FIN>).  */
    if (shutdown(sock, 1) == 0) {
	char ch;
	/* If there is any data still waiting in the queue, discard it.
	 * Call recv() until either it returns 0 (meaning we received a FIN)
	 * or any error occurs.  This makes sure all data sent by the other
	 * side is acknowledged at the TCP level.
	 */
	if (fm_peek(sock, &ch, 1) > 0)
	    while (fm_read(sock, &ch, 1) > 0)
		continue;
    }
#endif /* __UNUSED__ */

    /* if there's an error closing at this point, not much we can do */
    return(fm_close(sock));	/* this is guarded */
}

#ifdef MAIN
/*
 * Use the chargen service to test input buffering directly.
 * You may have to uncomment the `chargen' service description in your
 * inetd.conf (and then SIGHUP inetd) for this to work.  */
main()
{
    int	 	sock = SockOpen("localhost", 19, NULL);
    char	buf[80];

    while (SockRead(sock, buf, sizeof(buf)-1))
	SockWrite(1, buf, strlen(buf));
    SockClose(sock);
}
#endif /* MAIN */

/* socket.c ends here */