summaryrefslogtreecommitdiffstats
path: root/imap/proxy.c
diff options
context:
space:
mode:
authorSam Varshavchik2013-08-19 16:39:41 -0400
committerSam Varshavchik2013-08-25 14:43:51 -0400
commit9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch)
tree7a81a04cb51efb078ee350859a64be2ebc6b8813 /imap/proxy.c
parenta9520698b770168d1f33d6301463bb70a19655ec (diff)
downloadcourier-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.c721
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