diff options
| author | Sam Varshavchik | 2013-08-19 16:39:41 -0400 | 
|---|---|---|
| committer | Sam Varshavchik | 2013-08-25 14:43:51 -0400 | 
| commit | 9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch) | |
| tree | 7a81a04cb51efb078ee350859a64be2ebc6b8813 /imap/proxy.c | |
| parent | a9520698b770168d1f33d6301463bb70a19655ec (diff) | |
| download | courier-libs-9c45d9ad13fdf439d44d7443ae75da15ea0223ed.tar.bz2 | |
Initial checkin
Imported from subversion report, converted to git. Updated all paths in
scripts and makefiles, reflecting the new directory hierarchy.
Diffstat (limited to 'imap/proxy.c')
| -rw-r--r-- | imap/proxy.c | 721 | 
1 files changed, 721 insertions, 0 deletions
| diff --git a/imap/proxy.c b/imap/proxy.c new file mode 100644 index 0000000..3a73cf1 --- /dev/null +++ b/imap/proxy.c @@ -0,0 +1,721 @@ +/* +** Copyright 2004-2005 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if	HAVE_CONFIG_H +#include	"config.h" +#endif +#include	<stdio.h> +#include	<stdlib.h> +#include	<string.h> +#include	<errno.h> +#include	<ctype.h> +#include	<fcntl.h> +#include	<time.h> +#if	HAVE_UNISTD_H +#include	<unistd.h> +#endif +#if	HAVE_SYS_WAIT_H +#include	<sys/wait.h> +#endif +#include	<sys/socket.h> +#include	<netinet/in.h> +#include	<arpa/inet.h> +#include	<netdb.h> +#if HAVE_SYS_SELECT_H +#include	<sys/select.h> +#endif +#include	<sys/time.h> +#if HAVE_POLL +#include	<sys/poll.h> +#endif +#include	<courierauthdebug.h> +#include	"proxy.h" + + +static int checkhostname(const char *host) +{ +	char hostbuf[256]; +	const char *proxyhostname; + +	if ((proxyhostname=getenv("PROXY_HOSTNAME")) != NULL) +	{ +		if (strcmp(proxyhostname, host) == 0) +		{ +			courier_authdebug_printf("Proxy host %s is me, normal login.", +						 host); +			return -1; +		} +	} + +	if (gethostname(hostbuf, sizeof(hostbuf))) +	{ +		courier_authdebug_printf +			("gethostname failed: %s", +			 strerror(errno)); +		return -1; +	} + +	if (strcmp(hostbuf, host) == 0) +	{ +		courier_authdebug_printf("Proxy host %s is me, normal login.", +					 host); +		return -1; +	} +	return 0; +} + +static int connect_host(struct proxyinfo *pi, const char *host); + +int connect_proxy(struct proxyinfo *pi) +{ +	char *h=strdup(pi->host); +	char *p, *q; +	int fd; + +	if (!h) +	{ +		courier_authdebug_printf("%s", strerror(errno)); +		return -1; +	} + +	for (p=h; *p;) +	{ +		if (*p == ',') +		{ +			++p; +			continue; +		} + +		for (q=p; *q; q++) +			if (*q == ',') +				break; +		if (*q) +			*q++=0; + +		fd=connect_host(pi, p); +		if (fd >= 0) +		{ +			free(h); +			return fd; +		} +		p=q; +	} +	free(h); +	return -1; +} + + +static int proxyconnect(struct proxyinfo *pi, +			int aftype, +			void *addr, +			size_t); + +#if HAVE_GETADDRINFO + +static int connect_host(struct proxyinfo *pi, const char *host) +{ +	int fd; +	char portbuf[40]; +	int errcode; +	struct addrinfo hints, *res, *p; + +	if (checkhostname(host)) +		return (0); + +	sprintf(portbuf, "%d", pi->port); +	memset(&hints, 0, sizeof(hints)); +	hints.ai_family=PF_UNSPEC; +	hints.ai_socktype=SOCK_STREAM; + +	errcode=getaddrinfo(host, portbuf, &hints, &res); + +	if (errcode) +	{ +		courier_authdebug_printf +			("getaddrinfo on proxyhost %s failed: %s", +			 pi->host, gai_strerror(errcode)); +		return (-1); +	} + +	for (p=res; p; p=p->ai_next) +	{ +		if ((fd=proxyconnect(pi, p->ai_family, +				     p->ai_addr, +				     p->ai_addrlen)) +		    >= 0) +		{ +			if ((*pi->connected_func)(fd, host, +						  pi->void_arg)) +			{ +				close(fd); +				courier_authdebug_printf +					("Failed: %s.", strerror(errno)); +				continue; +			} +			freeaddrinfo(res); +			return fd; +		} +	} +	freeaddrinfo(res); +	courier_authdebug_printf +		("Connection to proxyhost %s failed.", host); +	return (-1); +} + +#else +static int connect_host(struct proxyinfo *pi, const char *host) +{ +	struct hostent *he; +	int i; +	int fd; + +	if (checkhostname(host)) +		return (0); + +	he=gethostbyname(host); + +	if (he == NULL) +	{ +		courier_authdebug_printf +			("gethostbyname on proxyhost %s failed.", +			 host); +		return (-1); +	} + +	for (i=0; he->h_addr_list[i]; i++) +	{ +		switch (he->h_addrtype) { +		case AF_INET: +			{ +				struct sockaddr_in sin; + +				memset(&sin, 0, sizeof(sin)); + +				sin.sin_family=AF_INET; + +				memcpy(&sin.sin_addr, he->h_addr_list[i], +				       sizeof(sin.sin_addr)); +				sin.sin_port=htons(pi->port); + +				fd=proxyconnect(pi, PF_INET, &sin, +						sizeof(sin)); +			} +			break; +#ifdef AF_INET6 +		case AF_INET6: +			{ +				struct sockaddr_in6 sin6; + +				memset(&sin6, 0, sizeof(sin6)); + +				sin6.sin6_family=AF_INET6; + +				memcpy(&sin6.sin6_addr, he->h_addr_list[i], +				       sizeof(sin6.sin6_addr)); + +				sin6.sin6_port=htons(pi->port); +				fd=proxyconnect(pi, PF_INET6, &sin6, +						sizeof(sin6)); +			} +			break; +#endif +		default: +			courier_authdebug_printf +				("Unknown address family type %d", +				 he->h_addrtype); +			continue; +		} + +		if (fd >= 0) +		{ +			if ((*pi->connected_func)(fd, host, +						  pi->void_arg)) +			{ +				close(fd); +				courier_authdebug_printf +					("Failed: %s.", strerror(errno)); +				continue; +			} +			return fd; +		} +	} +	courier_authdebug_printf +		("Connection to proxyhost %s failed.", host); +	return (-1); +} +#endif + +#if HAVE_POLL + +static int proxy_waitfd(int fd, int waitWrite, const char *hostnamebuf) +{ +	struct pollfd pfd; + +	memset(&pfd, 0, sizeof(pfd)); + +	pfd.fd=fd; +	pfd.events= waitWrite ? POLLOUT:POLLIN; + +	if (poll(&pfd, 1, 30 * 1000) < 0) +	{ +		courier_authdebug_printf +			("Poll failed while waiting to connect to %s: %s", +			 hostnamebuf, strerror(errno)); +		return -1; +	} + +	if (pfd.revents & (POLLOUT|POLLIN)) +		return 0; + +	courier_authdebug_printf +		("Timeout/error connecting to %s", hostnamebuf); +	return -1; +} + +#else +static int proxy_waitfd(int fd, int waitWrite, const char *hostnamebuf) +{ +	fd_set fds; +	struct timeval tv; + +	FD_ZERO(&fds); +	FD_SET(fd, &fds); +	tv.tv_sec=30; +	tv.tv_usec=0; + +	if (select(fd+1, waitWrite ? NULL:&fds, +		   waitWrite ? &fds:NULL, NULL, &tv) < 0) +	{ +		courier_authdebug_printf +			("Select failed while waiting to connect to %s: %s", +			 hostnamebuf, strerror(errno)); + +		return -1; +	} + +	if (!FD_ISSET(fd, &fds)) +	{ +		courier_authdebug_printf +			("Timeout connecting to %s", hostnamebuf); +		return -1; +	} + +	return 0; +} +#endif + +static int proxyconnect(struct proxyinfo *pi, +			int pf, +			void *addr, +			size_t addrLen) +{ +	int fd; +	char hostnamebuf[256]; +	int errcode; +	socklen_t errcode_l; + +	struct sockaddr *sa=(struct sockaddr *)addr; + +	switch (sa->sa_family) { +	case AF_INET: +		{ +			struct sockaddr_in *sin=(struct sockaddr_in *)sa; + +			strcpy(hostnamebuf, inet_ntoa(sin->sin_addr)); +		} +		break; + +#ifdef AF_INET6 +	case AF_INET6: +		{ +			struct sockaddr_in6 *sin6= +				(struct sockaddr_in6 *)sa; + +			if (inet_ntop(AF_INET6, &sin6->sin6_addr, +				      hostnamebuf, +				      sizeof(hostnamebuf)) == NULL) +				strcpy(hostnamebuf, "inet_ntop() failed"); +		} +		break; +#endif +	} + + +	fd=socket(pf, SOCK_STREAM, 0); + +	if (fd < 0) +	{ +		courier_authdebug_printf("socket: %s", strerror(errno)); +		return (-1); +	} + +	if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0 || +	    fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) +	{ +		close(fd); +		courier_authdebug_printf("fcntl(socket): %s", strerror(errno)); +	} + +	if (connect(fd, addr, addrLen) == 0) +		return fd; + +	if (errno != EINPROGRESS) +	{ +		courier_authdebug_printf +			("Proxy connection to %s failed: %s", +			 hostnamebuf, strerror(errno)); +		close(fd); +		return -1; +	} + +	if (proxy_waitfd(fd, 1, hostnamebuf)) +	{ +		close(fd); +		return -1; +	} + +	errcode_l=sizeof(errcode); + +	if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &errcode_l) < 0) +	{ +		courier_authdebug_printf +			("getsockopt failed: %s", strerror(errno)); +		close(fd); +		return -1; +	} + +	if (errcode) +	{ +		courier_authdebug_printf +			("Proxy connection to %s failed: %s", hostnamebuf, +			 strerror(errcode)); +		close(fd); +		return -1; +	} + +	return fd; +} + +static int proxy_getch(int fd, struct proxybuf *pb) +{ +	if (pb->bufleft == 0) +	{ +		int n; + +		if (proxy_waitfd(fd, 0, "server")) +			return -1; + +		pb->bufptr=pb->buffer; + +		n=read(fd, pb->buffer, sizeof(pb->buffer)); + +		if (n < 0) +		{ +			courier_authdebug_printf +				("Connection error: %s", +				 strerror(errno)); +			return -1; +		} + +		if (n == 0) +		{ +			courier_authdebug_printf +				("Connection closed by remote host"); +			return -1; +		} + +		pb->bufleft=(size_t)n; +	} +	--pb->bufleft; +	return ((int)(unsigned char)*pb->bufptr++); +} + +int proxy_readline(int fd, struct proxybuf *pb, +		   char *linebuf, +		   size_t linebuflen, +		   int imapmode) +{ +	size_t i; +	int ch; +	int prevch; + +	i=0; + +	ch=0; + +	do +	{ +		prevch=ch; + +		ch=proxy_getch(fd, pb); + +		if (ch < 0) +			return -1; + +		if (i < linebuflen) +			linebuf[i++]=(char)ch; +	} while (ch != '\n' || (imapmode && prevch != '\r')); + +	if (i) +		linebuf[--i]=0; + +	if (i && linebuf[i-1] == '\r') +		linebuf[--i]=0; + +	DPRINTF("Received: %s", linebuf); + +	return 0; +} + +int proxy_write(int fd, const char *hostname, +		const char *buf, size_t buf_len) +{ +	DPRINTF("Sending: %s", buf); + +	while (buf_len) +	{ +		int n; + +		if (proxy_waitfd(fd, 1, hostname)) +			return -1; + +		n=write(fd, buf, buf_len); + +		if (n < 0) +		{ +			courier_authdebug_printf +				("Error sending to %s: %s", +				 hostname, strerror(errno)); +			return -1; +		} + +		if (n == 0) +		{ +			courier_authdebug_printf +				("Connection close by %s", hostname); +			return -1; +		} + +		buf_len -= n; +		buf += n; +	} +	return 0; +} + +#if HAVE_POLL +void proxyloop(int fd) +{ +	char stdin_buf[BUFSIZ]; +	char stdout_buf[BUFSIZ]; + +	char *stdin_ptr=NULL; +	char *stdout_ptr=NULL; +	size_t stdin_left=0; +	size_t stdout_left=0; +	int n; + +	struct pollfd pfd[2]; + +	if (fcntl(0, F_SETFL, O_NONBLOCK) || +	    fcntl(1, F_SETFL, O_NONBLOCK)) +	{ +		courier_authdebug_printf("fcntl: %s", +					 strerror(errno)); +		return; +	} + +	do +	{ +		memset(&pfd, 0, sizeof(pfd)); + +		if (stdin_left == 0) +		{ +			pfd[0].fd=0; +			pfd[0].events=POLLIN; +		} +		else +		{ +			pfd[0].fd=fd; +			pfd[0].events=POLLOUT; +		} + +		if (stdout_left == 0) +		{ +			pfd[1].fd=fd; +			pfd[1].events=POLLIN; +		} +		else +		{ +			pfd[1].fd=1; +			pfd[1].events=POLLOUT; +		} + +		n=1; + +		if (poll(pfd, 2, -1) < 0) +		{ +			courier_authdebug_printf("poll: %s", +						 strerror(errno)); +			continue; +		} + +		if (stdin_left == 0) +		{ +			if (pfd[0].revents) +			{ +				n=read(0, stdin_buf, sizeof(stdin_buf)); + +				if (n > 0) +				{ +					stdin_ptr=stdin_buf; +					stdin_left=(size_t)n; +				} +			} +		} +		else if (pfd[0].revents) +		{ +			n=write(fd, stdin_ptr, stdin_left); + +			if (n > 0) +			{ +				stdin_ptr += n; +				stdin_left -= n; +			} +		} + +		if (n > 0) +		{ +			if (stdout_left == 0) +			{ +				if (pfd[1].revents) +				{ +					n=read(fd, stdout_buf, +					       sizeof(stdout_buf)); +				 +					if (n > 0) +					{ +						stdout_ptr=stdout_buf; +						stdout_left=(size_t)n; +					} +				} +			} else if (pfd[1].revents) +			{ +				n=write(1, stdout_ptr, stdout_left); + +				if (n > 0) +				{ +					stdout_ptr += n; +					stdout_left -= n; +				} +			} +		} +	} while (n > 0); + +	if (n < 0) +		courier_authdebug_printf("%s", strerror(errno)); +} + +#else +void proxyloop(int fd) +{ +	char stdin_buf[BUFSIZ]; +	char stdout_buf[BUFSIZ]; + +	char *stdin_ptr=NULL; +	char *stdout_ptr=NULL; +	size_t stdin_left=0; +	size_t stdout_left=0; +	int n; + +	fd_set fdr, fdw; + +	if (fcntl(0, F_SETFL, O_NONBLOCK) || +	    fcntl(1, F_SETFL, O_NONBLOCK)) +	{ +		courier_authdebug_printf("fcntl: %s", +					 strerror(errno)); +		return; +	} + +	do +	{ +		FD_ZERO(&fdr); +		FD_ZERO(&fdw); + +		if (stdin_left == 0) +			FD_SET(0, &fdr); +		else +			FD_SET(fd, &fdw); + +		if (stdout_left == 0) +			FD_SET(fd, &fdr); +		else +			FD_SET(1, &fdw); + +		n=1; + +		if (select(fd+1, &fdr, &fdw, NULL, NULL) < 0) +		{ +			courier_authdebug_printf("select: %s", +						 strerror(errno)); +			continue; +		} + +		if (stdin_left == 0) +		{ +			if (FD_ISSET(0, &fdr)) +			{ +				n=read(0, stdin_buf, sizeof(stdin_buf)); + +				if (n > 0) +				{ +					stdin_ptr=stdin_buf; +					stdin_left=(size_t)n; +				} +			} +		} +		else if (FD_ISSET(fd, &fdw)) +		{ +			n=write(fd, stdin_ptr, stdin_left); + +			if (n > 0) +			{ +				stdin_ptr += n; +				stdin_left -= n; +			} +		} + +		if (n > 0) +		{ +			if (stdout_left == 0) +			{ +				if (FD_ISSET(fd, &fdr)) +				{ +					n=read(fd, stdout_buf, +					       sizeof(stdout_buf)); +				 +					if (n > 0) +					{ +						stdout_ptr=stdout_buf; +						stdout_left=(size_t)n; +					} +				} +			} else if (FD_ISSET(1, &fdw)) +			{ +				n=write(1, stdout_ptr, stdout_left); + +				if (n > 0) +				{ +					stdout_ptr += n; +					stdout_left -= n; +				} +			} +		} +	} while (n > 0); + +	if (n < 0) +		courier_authdebug_printf("%s", strerror(errno)); +} +#endif | 
