components/ftp-proxy/patches/001-solaris.patch
changeset 5618 a7df12d981ea
parent 5565 f678cc44b3d0
child 7791 95f8368a21ec
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/ftp-proxy/patches/001-solaris.patch	Thu Mar 17 00:08:54 2016 -0700
@@ -0,0 +1,642 @@
+# This patch comes from Oracle. It fixes issues preventing ftp-proxy
+# from building and running on Solaris. Especially, we:
+# - disabled features missing support on Solaris (queuing, rtable..)
+#   where adding such support is not reasonable
+# - used workarounds to deal with missing pieces on Solaris (missing
+#   structure members in sockaddr, PF not supporting divert-to,
+#   using Solaris-specific random number generator) 
+#
+# These changes are not going to upstream, they are Solaris-specific.
+
+diff -Naur ORIGINAL/Makefile ftp-proxy-OPENBSD_5_5-OPENBSD_5_5.pre-smf/Makefile
+--- ORIGINAL/Makefile	2006-11-26 03:31:13.000000000 -0800
++++ ftp-proxy-OPENBSD_5_5-OPENBSD_5_5.pre-smf/Makefile	2016-02-10 04:21:21.337202150 -0800
+@@ -1,13 +1,29 @@
+ #	$OpenBSD: Makefile,v 1.3 2006/11/26 11:31:13 deraadt Exp $
+ 
++CFLAGS+= -m64 -errwarn
++
+ PROG=	ftp-proxy
+ SRCS=	ftp-proxy.c filter.c
++OBJS=$(SRCS:.c=.o)
+ MAN=	ftp-proxy.8
+ 
+-CFLAGS+= -I${.CURDIR}
+-CFLAGS+= -Wall -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith \
+-         -Wno-uninitialized
+-LDADD+=	-levent
+-DPADD+= ${LIBEVENT}
++LDADD+=	-levent -luutil
++LDFLAGS+=	-z nolazyload
++
++all:	$(SRCS) $(PROG)
++
++install: $(PROG)
++	$(INSTALL) -d $(PREFIX)/sbin
++	$(INSTALL) -m 755 $(PROG) $(PREFIX)/sbin
++	$(INSTALL) -d $(MANDIR)/man8
++	$(INSTALL) -m 644 $(MAN) $(MANDIR)/man8
++
++$(PROG):	$(OBJS)
++	$(CC) $(CFLAGS) $(OBJS) -o $@ $(LDFLAGS) $(LDADD)
++
++.c.o:
++	$(CC) $(CFLAGS) -c -o $@ $<
+ 
+-.include <bsd.prog.mk>
++clean:
++	rm -rf *.o
++	rm -rf $(PROG)
+diff -Naur ORIGINAL/filter.c ftp-proxy-OPENBSD_5_5-OPENBSD_5_5.pre-smf/filter.c
+--- ORIGINAL/filter.c	2012-09-18 03:11:53.000000000 -0700
++++ ftp-proxy-OPENBSD_5_5-OPENBSD_5_5.pre-smf/filter.c	2016-02-10 04:24:03.599069704 -0800
+@@ -32,6 +32,10 @@
+ #include <stdio.h>
+ #include <string.h>
+ #include <unistd.h>
++#ifdef _SOLARIS_
++/* we need _IOWR */
++#include <sys/ioccom.h>
++#endif	/* _SOLARIS_ */
+ 
+ #include "filter.h"
+ 
+diff -Naur ORIGINAL/ftp-proxy.8 ftp-proxy-OPENBSD_5_5-OPENBSD_5_5.pre-smf/ftp-proxy.8
+--- ORIGINAL/ftp-proxy.8	2012-06-25 04:49:19.000000000 -0700
++++ ftp-proxy-OPENBSD_5_5-OPENBSD_5_5.pre-smf/ftp-proxy.8	2016-02-24 06:31:17.792565815 -0800
+@@ -30,17 +30,16 @@
+ .Op Fl m Ar maxsessions
+ .Op Fl P Ar port
+ .Op Fl p Ar port
+-.Op Fl q Ar queue
+ .Op Fl R Ar address
+ .Op Fl T Ar tag
+ .Op Fl t Ar timeout
+ .Ek
+ .Sh DESCRIPTION
+ .Nm
+-is a proxy for the Internet File Transfer Protocol.
+-FTP control connections should be redirected into the proxy using the
+-.Xr pf 4
+-.Ar divert-to
++is a proxy for the Internet File Transfer Protocol making connections
++over IPv4 NAT possible.
++FTP control connections should be redirected into the proxy using the PF
++.Ar rdr-to
+ command, after which the proxy connects to the server on behalf of
+ the client.
+ .Pp
+@@ -51,22 +50,20 @@
+ Consequently, all connections from the server to the proxy have
+ their destination address rewritten, so they are redirected to the
+ client.
+-The proxy uses the
+-.Xr pf 4
++The proxy uses the PF
+ .Ar anchor
+ facility for this.
+ .Pp
+ Assuming the FTP control connection is from $client to $server, the
+-proxy connected to the server using the $proxy source address, and
+-$port is negotiated, then
++proxy is connected to the server using the $proxy source address, and
++$port is negotiated, the
+ .Nm
+ adds the following rules to the anchor.
+ $server and $orig_server are the same unless
+ .Fl R
+ is used to force a different $server address for all connections.
+-(These example rules use inet, but the proxy also supports inet6.)
+ .Pp
+-In case of active mode (PORT or EPRT):
++In case of active mode (PORT):
+ .Bd -literal -offset 2n
+ pass in from $server to $proxy port $proxy_port \e
+     rdr-to $client port $port
+@@ -74,7 +71,7 @@
+     nat-to $orig_server port $natport
+ .Ed
+ .Pp
+-In case of passive mode (PASV or EPSV):
++In case of passive mode (PASV):
+ .Bd -literal -offset 2n
+ pass in from $client to $orig_server port $proxy_port \e
+     rdr-to $server port $port
+@@ -83,11 +80,6 @@
+ .Pp
+ The options are as follows:
+ .Bl -tag -width Ds
+-.It Fl 6
+-IPv6 mode.
+-The proxy will expect and use IPv6 addresses for all communication.
+-Only the extended FTP modes EPSV and EPRT are allowed with IPv6.
+-The proxy is in IPv4 mode by default.
+ .It Fl A
+ Only permit anonymous FTP connections.
+ Either user "ftp" or user "anonymous" is allowed.
+@@ -96,14 +88,11 @@
+ connection to a server.
+ .It Fl b Ar address
+ Address where the proxy will listen for redirected control connections.
+-The default is 127.0.0.1, or ::1 in IPv6 mode.
++The default is 127.0.0.1.
+ .It Fl D Ar level
+ Debug level, ranging from 0 to 7.
+ Higher is more verbose.
+ The default is 5.
+-(These levels correspond to the
+-.Xr syslog 3
+-levels.)
+ .It Fl d
+ Do not daemonize.
+ The process will stay in the foreground, logging to standard error.
+@@ -120,10 +109,6 @@
+ .It Fl p Ar port
+ Port where the proxy will listen for redirected connections.
+ The default is port 8021.
+-.It Fl q Ar queue
+-Create rules with queue
+-.Ar queue
+-appended, so that data connections can be queued.
+ .It Fl R Ar address
+ Fixed server address, also known as reverse mode.
+ The proxy will always connect to the same server, regardless of
+@@ -142,9 +127,8 @@
+ keyword can be implemented following the
+ .Nm
+ anchor.
+-These rules can use special
+-.Xr pf 4
+-features like route-to, reply-to, label, rtable, overload, etc. that
++These rules can use special PF
++features like route-to, reply-to, label, overload, etc. that
+ .Nm
+ does not implement itself.
+ There must be a matching pass rule after the
+@@ -159,7 +143,9 @@
+ .It Fl v
+ Set the 'log' flag on pf rules committed by
+ .Nm .
+-Use twice to set the 'log all' flag.
++Use twice to set the
++.Sq log all
++flag.
+ The pf rules do not log by default.
+ .El
+ .Sh CONFIGURATION
+@@ -171,27 +157,23 @@
+ necessary.
+ .Bd -literal -offset 2n
+ anchor "ftp-proxy/*"
+-pass in quick inet proto tcp to port ftp divert-to 127.0.0.1 port 8021
++pass in quick inet proto tcp to port ftp rdr-to 127.0.0.1 port 8021
+ pass out inet proto tcp from (self) to any port ftp
+ .Ed
++.Pp
++To run
++.Nm
++in a non-global zone, the
++.Bd -literal -offset indent
++svc:/network/socket-filter:pf_divert
++.Ed
++instance must be online in the global zone.
+ .Sh SEE ALSO
+-.Xr ftp 1 ,
+-.Xr pf 4 ,
+ .Xr pf.conf 5
+ .Sh CAVEATS
+-.Xr pf 4
+-does not allow the ruleset to be modified if the system is running at a
+-.Xr securelevel 7
+-higher than 1.
+-At that level
+-.Nm
+-cannot add rules to the anchors and FTP data connections may get blocked.
+ .Pp
+ Negotiated data connection ports below 1024 are not allowed.
+ .Pp
+ The negotiated IP address for active modes is ignored for security
+ reasons.
+ This makes third party file transfers impossible.
+-.Pp
+-.Nm
+-chroots to "/var/empty" and changes to user "proxy" to drop privileges.
+diff -Naur ORIGINAL/ftp-proxy.c ftp-proxy-OPENBSD_5_5-OPENBSD_5_5.pre-smf/ftp-proxy.c
+--- ORIGINAL/ftp-proxy.c	2013-03-15 06:31:27.000000000 -0700
++++ ftp-proxy-OPENBSD_5_5-OPENBSD_5_5.pre-smf/ftp-proxy.c	2016-02-10 04:12:16.600723376 -0800
+@@ -38,9 +38,20 @@
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
++#ifdef _SOLARIS_
++#include <strings.h>
++#include <sys/types.h>
++#include <time.h>
++#include <libuutil.h>
++#include <sys/random.h>
++#include <inttypes.h>
++#include <priv.h>
++#endif	/* _SOLARIS_ */
+ #include <syslog.h>
+ #include <unistd.h>
++#ifndef _SOLARIS_
+ #include <vis.h>
++#endif	/* !_SOLARIS_ */
+ 
+ #include "filter.h"
+ 
+@@ -60,6 +71,32 @@
+ 
+ #define	sstosa(ss)	((struct sockaddr *)(ss))
+ 
++#ifdef _SOLARIS_
++/*
++ * These constants are used as a range used by pick_proxy_port(). The ftp-proxy
++ * never binds these ports. They are used only within proxy_reply() and add_rdr()
++ * to be put into a FTP-protocol message and to construct the rule to be loaded
++ * into PF, respectively.
++ *
++ * OpenBSD adheres to a convention where these port numbers are reserved for
++ * connections that want to bypass a firewall. Surely, it depends on how the
++ * administrator configures the firewall, too. Let's stick to that convention
++ * here. The idea probably is "if the admin uses this convention, these ports
++ * are not filtered and thus we are not going to clash with current firewall
++ * rules".
++ */
++#define IPPORT_HIFIRSTAUTO	49152
++#define IPPORT_HILASTAUTO	65535
++
++#define getrtable() 0
++
++#ifndef LIST_END
++#define LIST_END(x) NULL
++#endif	/* !LIST_END */
++
++#define	DIVERT_MODULE_NAME "pf_divertf"
++#endif	/* _SOLARIS_ */
++
+ enum { CMD_NONE = 0, CMD_PORT, CMD_EPRT, CMD_PASV, CMD_EPSV };
+ 
+ struct session {
+@@ -115,12 +152,59 @@
+ 
+ struct event listen_ev, pause_accept_ev;
+ struct sockaddr_storage fixed_server_ss, fixed_proxy_ss;
++#ifdef _SOLARIS_
++static socklen_t fixed_server_ss_len;
++static socklen_t fixed_proxy_ss_len;
++#endif	/* _SOLARIS_ */
+ char *fixed_server, *fixed_server_port, *fixed_proxy, *listen_ip, *listen_port,
+     *qname, *tagname;
+ int anonymous_only, daemonize, id_count, ipv6_mode, loglevel, max_sessions,
+     rfc_mode, session_count, timeout, verbose;
+ extern char *__progname;
+ 
++#ifdef	_SOLARIS_
++/*
++ * fake_arc4random_uniform()
++ * This is a fake implementation of arc4random_uniform(). The wrapper
++ * provides so called uniform calculation of pseudo random number with
++ * respect to upper bound.
++ *
++ * Function calculates random numbers until it finds one outside
++ * <0, 2^32 % upper_bound) range. Once random number, `rand`, is selected,
++ * function returns rand % upper_bound.
++ *
++ * Arguments:
++ *	upper_bound	- random number is picked up in range <0, upper_bound)
++ *
++ * Returns:
++ *	random number, uniform with respect to upper bound.
++ *      Returns UINT32_MAX on error.
++ */
++static u_int32_t
++fake_arc4random_uniform(u_int32_t upper_bound)
++{
++	u_int32_t	rand, min;
++
++	if (upper_bound < 2)
++		return (0);
++
++	/*
++	 * 2**32 % x == (2**32 - x) % x
++	 * (Trick comes from OpenBSD, arc4random_uniform.c)
++	 */
++	min = -upper_bound % upper_bound;
++	for (;;) {
++		if (getrandom(&rand, sizeof (rand), GRND_NONBLOCK) !=
++		    sizeof (rand))
++			return (UINT32_MAX);
++		if (rand >= min)
++			break;
++	}
++
++	return (rand % upper_bound);
++}
++#endif	/* _SOLARIS_ */
++
+ void
+ client_error(struct bufferevent *bufev, short what, void *arg)
+ {
+@@ -220,6 +304,12 @@
+ 			return (0);
+ 		}
+ 		s->proxy_port = pick_proxy_port();
++#ifdef _SOLARIS_
++		if (s->proxy_port == UINT16_MAX) {
++			logmsg(LOG_CRIT, "pick_proxy_port() failed");
++			return (0);
++		}
++#endif /* _SOLARIS_ */
+ 		proxy_reply(s->cmd, sstosa(&s->proxy_ss), s->proxy_port);
+ 		logmsg(LOG_DEBUG, "#%d proxy: %s", s->id, linebuf);
+ 	}
+@@ -378,13 +468,30 @@
+ 	struct sockaddr *proxy_to_server_sa;
+ 	struct session *s;
+ 	socklen_t len;
++#ifdef _SOLARIS_
++	socklen_t client_sa_len, server_sa_len;
++	int one = 1; /* parameter for setsockopt */
++#endif	/* _SOLARIS_ */
+ 	int client_fd, fc, on;
+-
++	/*
++	 * We experienced big problems when event_add() was called
++	 * before accepting the incoming connection - for some reason,
++	 * a new event was fired immediately and ftp-proxy was hanged
++	 * trying to accept another client that was not there yet.
++	 * Moving event_add() call a few lines below resolved this
++	 * problem.
++	 */
++#ifndef _SOLARIS_
+ 	event_add(&listen_ev, NULL);
++#endif	/* !_SOLARIS_ */
+ 
+-	if ((event & EV_TIMEOUT))
++	if ((event & EV_TIMEOUT)) {
++#ifdef _SOLARIS_
++		event_add(&listen_ev, NULL);
++#endif	/* _SOLARIS_ */
+ 		/* accept() is no longer paused. */
+ 		return;
++	}
+ 
+ 	/*
+ 	 * We _must_ accept the connection, otherwise libevent will keep
+@@ -393,6 +500,9 @@
+ 	client_sa = sstosa(&tmp_ss);
+ 	len = sizeof(struct sockaddr_storage);
+ 	if ((client_fd = accept(listen_fd, client_sa, &len)) < 0) {
++#ifdef _SOLARIS_
++		event_add(&listen_ev, NULL);
++#endif	/* _SOLARIS_ */
+ 		logmsg(LOG_CRIT, "accept() failed: %s", strerror(errno));
+ 
+ 		/*
+@@ -410,6 +520,16 @@
+ 		return;
+ 	}
+ 
++#ifdef _SOLARIS_
++	event_add(&listen_ev, NULL);
++
++	/*
++	 * Struct sockaddr does not contain sa_len field on Solaris,
++	 * we use client_sa_len instead.
++	 */
++	client_sa_len = len;
++#endif	/* _SOLARIS_ */
++
+ 	/* Refuse connection if the maximum is reached. */
+ 	if (session_count >= max_sessions) {
+ 		logmsg(LOG_ERR, "client limit (%d) reached, refusing "
+@@ -426,8 +546,11 @@
+ 		return;
+ 	}
+ 	s->client_fd = client_fd;
++#ifdef _SOLARIS_
++	memcpy(sstosa(&s->client_ss), client_sa, client_sa_len);
++#else /* !_SOLARIS_ */
+ 	memcpy(sstosa(&s->client_ss), client_sa, client_sa->sa_len);
+-
++#endif	/* _SOLARIS_ */
+ 	/* Cast it once, and be done with it. */
+ 	client_sa = sstosa(&s->client_ss);
+ 	server_sa = sstosa(&s->server_ss);
+@@ -447,6 +570,17 @@
+ 		    strerror(errno));
+ 		goto fail;
+ 	}
++#ifdef _SOLARIS_
++	/*
++	 * Struct sockaddr does not contain sa_len field on Solaris,
++	 * we use server_sa_len instead.
++	 */
++	server_sa_len = len;
++#endif	/* _SOLARIS_ */
++/* SO_RTABLE not defined on Solaris */
++#ifdef _SOLARIS_
++	s->client_rd = 0;
++#else /* !_SOLARIS_ */
+ 	len = sizeof(s->client_rd);
+ 	if (getsockopt(s->client_fd, SOL_SOCKET, SO_RTABLE, &s->client_rd,
+ 	    &len) && errno != ENOPROTOOPT) {
+@@ -454,10 +588,18 @@
+ 		    strerror(errno));
+ 		goto fail;
+ 	}
++#endif	/* _SOLARIS_ */
+ 	if (fixed_server) {
++#ifdef _SOLARIS_
++		memcpy(sstosa(&s->orig_server_ss), server_sa,
++		    server_sa_len);
++		memcpy(server_sa, fixed_server_sa, fixed_server_ss_len);
++		server_sa_len = fixed_server_ss_len; /* update the length */
++#else /* !_SOLARIS_ */
+ 		memcpy(sstosa(&s->orig_server_ss), server_sa,
+ 		    server_sa->sa_len);
+ 		memcpy(server_sa, fixed_server_sa, fixed_server_sa->sa_len);
++#endif	/* _SOLARIS_ */
+ 	}
+ 
+ 	/* XXX: check we are not connecting to ourself. */
+@@ -471,8 +613,14 @@
+ 		    strerror(errno));
+ 		goto fail;
+ 	}
++#ifdef	_SOLARIS_
+ 	if (fixed_proxy && bind(s->server_fd, sstosa(&fixed_proxy_ss),
+-	    fixed_proxy_ss.ss_len) != 0) {
++	    fixed_proxy_ss_len) != 0)
++#else /* !_SOLARIS_ */
++	if (fixed_proxy && bind(s->server_fd, sstosa(&fixed_proxy_ss),
++	    fixed_proxy_ss.ss_len) != 0)
++#endif	/* _SOLARIS_ */
++	{
+ 		logmsg(LOG_CRIT, "#%d cannot bind fixed proxy address: %s",
+ 		    s->id, strerror(errno));
+ 		goto fail;
+@@ -485,8 +633,14 @@
+ 		    s->id, strerror(errno));
+ 		goto fail;
+ 	}
++#ifdef _SOLARIS_
++	if (connect(s->server_fd, server_sa, server_sa_len) < 0 &&
++	    errno != EINPROGRESS)
++#else /* !_SOLARIS_ */
+ 	if (connect(s->server_fd, server_sa, server_sa->sa_len) < 0 &&
+-	    errno != EINPROGRESS) {
++	    errno != EINPROGRESS)
++#endif	/* _SOLARIS_ */
++	{
+ 		logmsg(LOG_CRIT, "#%d proxy cannot connect to server %s: %s",
+ 		    s->id, sock_ntop(server_sa), strerror(errno));
+ 		goto fail;
+@@ -592,6 +746,9 @@
+ 		/* syslog does its own vissing. */
+ 		vsyslog(pri, message, ap);
+ 	else {
++#ifdef _SOLARIS_
++		vsyslog(pri, message, ap);
++#else /* !_SOLARIS_ */
+ 		char buf[MAX_LOGLINE];
+ 		char visbuf[2 * MAX_LOGLINE];
+ 
+@@ -599,6 +756,7 @@
+ 		vsnprintf(buf, sizeof buf, message, ap);
+ 		strnvis(visbuf, buf, sizeof visbuf, VIS_CSTYLE | VIS_NL);
+ 		fprintf(stderr, "%s\n", visbuf);
++#endif	/* _SOLARIS_ */
+ 	}
+ 
+ 	va_end(ap);
+@@ -636,9 +794,11 @@
+ 
+ 	while ((ch = getopt(argc, argv, "6Aa:b:D:dm:P:p:q:R:rT:t:v")) != -1) {
+ 		switch (ch) {
++#ifndef	_SOLARIS_
+ 		case '6':
+ 			ipv6_mode = 1;
+ 			break;
++#endif	/* !_SOLARIS_ */
+ 		case 'A':
+ 			anonymous_only = 1;
+ 			break;
+@@ -668,11 +828,13 @@
+ 		case 'p':
+ 			listen_port = optarg;
+ 			break;
++#ifndef	_SOLARIS_
+ 		case 'q':
+ 			if (strlen(optarg) >= PF_QNAME_SIZE)
+ 				errx(1, "queuename too long");
+ 			qname = optarg;
+ 			break;
++#endif	/* !_SOLARIS_ */
+ 		case 'R':
+ 			fixed_server = optarg;
+ 			break;
+@@ -718,9 +880,16 @@
+ 		hints.ai_socktype = SOCK_STREAM;
+ 		error = getaddrinfo(fixed_proxy, NULL, &hints, &res);
+ 		if (error)
+-			errx(1, "getaddrinfo fixed proxy address failed: %s",
++			errx(1, "getaddrinfo fixed proxy address (%s) failed: %s", fixed_proxy,
+ 			    gai_strerror(error));
+ 		memcpy(&fixed_proxy_ss, res->ai_addr, res->ai_addrlen);
++#ifdef _SOLARIS_
++		/*
++		 * struct sockaddr_storage does not have the member
++		 * ss_len on Solaris, thus we use a global variable.
++		 */
++		fixed_proxy_ss_len = res->ai_addrlen;
++#endif	/* _SOLARIS_ */
+ 		logmsg(LOG_INFO, "using %s to connect to servers",
+ 		    sock_ntop(sstosa(&fixed_proxy_ss)));
+ 		freeaddrinfo(res);
+@@ -736,6 +905,13 @@
+ 			errx(1, "getaddrinfo fixed server address failed: %s",
+ 			    gai_strerror(error));
+ 		memcpy(&fixed_server_ss, res->ai_addr, res->ai_addrlen);
++#ifdef _SOLARIS_
++		/*
++		 * struct sockaddr_storage does not have the member
++		 * ss_len on Solaris, thus we use a global variable.
++		 */
++		fixed_server_ss_len = res->ai_addrlen;
++#endif	/* _SOLARIS_ */
+ 		logmsg(LOG_INFO, "using fixed server %s",
+ 		    sock_ntop(sstosa(&fixed_server_ss)));
+ 		freeaddrinfo(res);
+@@ -752,6 +928,11 @@
+ 		    gai_strerror(error));
+ 	if ((listenfd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP)) == -1)
+ 		errx(1, "socket failed");
++#ifdef _SOLARIS_
++	if (setsockopt(listenfd, SOL_FILTER, FIL_ATTACH, DIVERT_MODULE_NAME,
++	    (strlen(DIVERT_MODULE_NAME)+1)) != 0)
++		err(1, "setsockopt failed - unable to attach filter");
++#endif /* _SOLARIS_ */
+ 	on = 1;
+ 	if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (void *)&on,
+ 	    sizeof on) != 0)
+@@ -782,7 +963,11 @@
+ 	event_init();
+ 
+ 	/* Setup signal handler. */
++#ifdef	_SOLARIS_
++	sigset(SIGPIPE, SIG_IGN);
++#else /* !_SOLARIS_ */
+ 	signal(SIGPIPE, SIG_IGN);
++#endif	/* _SOLARIS_ */
+ 	signal_set(&ev_sighup, SIGHUP, handle_signal, NULL);
+ 	signal_set(&ev_sigint, SIGINT, handle_signal, NULL);
+ 	signal_set(&ev_sigterm, SIGTERM, handle_signal, NULL);
+@@ -857,12 +1042,25 @@
+ 	return (0);
+ }
+ 
++/*
++ * On Solaris, fake_arc4random_uniform() can fail. We return UINT16_MAX
++ * on error.
++ */
+ u_int16_t
+ pick_proxy_port(void)
+ {
++#ifdef _SOLARIS_
++	u_int32_t shift;
++
++	shift = fake_arc4random_uniform(IPPORT_HILASTAUTO - IPPORT_HIFIRSTAUTO);
++	if (shift == UINT32_MAX)
++		return UINT16_MAX;
++	return (IPPORT_HIFIRSTAUTO + shift);
++#else /* !_SOLARIS_ */
+ 	/* Random should be good enough for avoiding port collisions. */
+ 	return (IPPORT_HIFIRSTAUTO +
+ 	    arc4random_uniform(IPPORT_HILASTAUTO - IPPORT_HIFIRSTAUTO));
++#endif /* _SOLARIS_ */
+ }
+ 
+ void
+@@ -985,6 +1183,12 @@
+ 			return (0);
+ 		}
+ 		s->proxy_port = pick_proxy_port();
++#ifdef _SOLARIS_
++		if (s->proxy_port == UINT16_MAX) {
++			logmsg(LOG_CRIT, "pick_proxy_port() failed");
++			return (0);
++		}
++#endif /* _SOLARIS_ */
+ 		logmsg(LOG_INFO, "#%d passive: client to server port %d"
+ 		    " via port %d", s->id, s->port, s->proxy_port);
+ 
+@@ -1126,6 +1330,6 @@
+ 	fprintf(stderr, "usage: %s [-6Adrv] [-a address] [-b address]"
+ 	    " [-D level] [-m maxsessions]\n                 [-P port]"
+ 	    " [-p port] [-q queue] [-R address] [-T tag]\n"
+-            "                 [-t timeout]\n", __progname);
++	    "                 [-t timeout]\n", __progname);
+ 	exit(1);
+ }