aboutsummaryrefslogtreecommitdiffstats
path: root/lang/erlang/files
diff options
context:
space:
mode:
authorolgeni <olgeni@FreeBSD.org>2015-04-12 18:11:35 +0800
committerolgeni <olgeni@FreeBSD.org>2015-04-12 18:11:35 +0800
commitd62df72a236fb60b73405d3afef942d8511d0e62 (patch)
tree0c8287c34a67ee8b0f28219d0587bdf832a74968 /lang/erlang/files
parent34581be98828c271d0af4283c75950a33f04b434 (diff)
downloadfreebsd-ports-graphics-d62df72a236fb60b73405d3afef942d8511d0e62.tar.gz
freebsd-ports-graphics-d62df72a236fb60b73405d3afef942d8511d0e62.tar.zst
freebsd-ports-graphics-d62df72a236fb60b73405d3afef942d8511d0e62.zip
Upgrade to version 17.5.1.
Diffstat (limited to 'lang/erlang/files')
-rw-r--r--lang/erlang/files/patch-otp-17.4.11358
-rw-r--r--lang/erlang/files/patch-otp-17.5.1826
2 files changed, 826 insertions, 1358 deletions
diff --git a/lang/erlang/files/patch-otp-17.4.1 b/lang/erlang/files/patch-otp-17.4.1
deleted file mode 100644
index a6e71eefcdb..00000000000
--- a/lang/erlang/files/patch-otp-17.4.1
+++ /dev/null
@@ -1,1358 +0,0 @@
-diff --git OTP_VERSION OTP_VERSION
-index 5f9cbaa..ae704d6 100644
---- OTP_VERSION
-+++ OTP_VERSION
-@@ -1 +1 @@
--17.4
-+17.4.1
-diff --git erts/doc/src/notes.xml erts/doc/src/notes.xml
-index c896ee0..af0d4d7 100644
---- erts/doc/src/notes.xml
-+++ erts/doc/src/notes.xml
-@@ -30,6 +30,65 @@
- </header>
- <p>This document describes the changes made to the ERTS application.</p>
-
-+<section><title>Erts 6.3.1</title>
-+
-+ <section><title>Fixed Bugs and Malfunctions</title>
-+ <list>
-+ <item>
-+ <p>
-+ Fix getifaddrs realloc pointer error</p>
-+ <p>
-+ When a buffer was exhausted and subsequently reallocated,
-+ we could get an unsafe pointer pointing to faulty memory.</p>
-+ <p>
-+ For this to occur we would need to have a large number of
-+ interfaces and a reallocation of memory to a lower
-+ addresses.</p>
-+ <p>
-+ The symptom would be garbage returned from
-+ erlang:port_control(Port, 25, [])
-+ (prim_inet:getifaddrs(Port) resulting in a badarg) or a
-+ segmentation fault.</p>
-+ <p>
-+ Own Id: OTP-12445</p>
-+ </item>
-+ <item>
-+ <p>
-+ Don't close all file descriptors twice in child_setup</p>
-+ <p>
-+ The commit c2b4eab25c907f453a394d382c04cd04e6c06b49
-+ introduced an error in which child_setup erroneously
-+ tried to close all file descriptors twice.</p>
-+ <p>
-+ Use closefrom() if available when closing all file
-+ descriptors.</p>
-+ <p>
-+ The function closefrom() was only used in the vfork()
-+ case before but is now also used in the fork() case if
-+ available.</p>
-+ <p>
-+ Own Id: OTP-12446</p>
-+ </item>
-+ <item>
-+ <p>
-+ During a crashdump all file descriptors are closed to
-+ ensure the closing of the epmd port and to reserve a file
-+ descriptor for the crashdump file.</p>
-+ <p>
-+ If a driver (third party library) cannot handle closing
-+ of sockets this could result in a segmentation fault in
-+ which case a crashdump would not be produced. This is now
-+ fixed by only closing inets sockets via an emergency
-+ close callback to the driver and thus closing the epmd
-+ socket.</p>
-+ <p>
-+ Own Id: OTP-12447</p>
-+ </item>
-+ </list>
-+ </section>
-+
-+</section>
-+
- <section><title>Erts 6.3</title>
-
- <section><title>Fixed Bugs and Malfunctions</title>
-diff --git erts/emulator/beam/erl_driver.h erts/emulator/beam/erl_driver.h
-index f9938fc..e498ac7 100644
---- erts/emulator/beam/erl_driver.h
-+++ erts/emulator/beam/erl_driver.h
-@@ -133,7 +133,7 @@ typedef struct {
-
- #define ERL_DRV_EXTENDED_MARKER (0xfeeeeeed)
- #define ERL_DRV_EXTENDED_MAJOR_VERSION 3
--#define ERL_DRV_EXTENDED_MINOR_VERSION 1
-+#define ERL_DRV_EXTENDED_MINOR_VERSION 2
-
- /*
- * The emulator will refuse to load a driver with a major version
-@@ -361,6 +361,9 @@ typedef struct erl_drv_entry {
- /* Called on behalf of driver_select when
- it is safe to release 'event'. A typical
- unix driver would call close(event) */
-+ void (*emergency_close)(ErlDrvData drv_data);
-+ /* called when the port is closed abruptly.
-+ specifically when erl_crash_dump is called. */
- /* When adding entries here, dont forget to pad in obsolete/driver.h */
- } ErlDrvEntry;
-
-diff --git erts/emulator/beam/global.h erts/emulator/beam/global.h
-index 891046a..32a2dc4 100644
---- erts/emulator/beam/global.h
-+++ erts/emulator/beam/global.h
-@@ -160,6 +160,7 @@ struct erts_driver_t_ {
- void (*ready_async)(ErlDrvData drv_data, ErlDrvThreadData thread_data); /* Might be NULL */
- void (*process_exit)(ErlDrvData drv_data, ErlDrvMonitor *monitor);
- void (*stop_select)(ErlDrvEvent event, void*); /* Might be NULL */
-+ void (*emergency_close)(ErlDrvData drv_data); /* Might be NULL */
- };
-
- extern erts_driver_t *driver_list;
-@@ -852,6 +853,7 @@ Uint erts_port_ioq_size(Port *pp);
- void erts_stale_drv_select(Eterm, ErlDrvPort, ErlDrvEvent, int, int);
-
- Port *erts_get_heart_port(void);
-+void erts_emergency_close_ports(void);
-
- #if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_COUNT)
- void erts_lcnt_enable_io_lock_count(int enable);
-diff --git erts/emulator/beam/io.c erts/emulator/beam/io.c
-index 9ae973e..012a7d1 100644
---- erts/emulator/beam/io.c
-+++ erts/emulator/beam/io.c
-@@ -7349,6 +7349,8 @@ no_stop_select_callback(ErlDrvEvent event, void* private)
- erts_send_error_to_logger_nogl(dsbufp);
- }
-
-+#define IS_DRIVER_VERSION_GE(DE,MAJOR,MINOR) \
-+ ((DE)->major_version >= (MAJOR) && (DE)->minor_version >= (MINOR))
-
- static int
- init_driver(erts_driver_t *drv, ErlDrvEntry *de, DE_Handle *handle)
-@@ -7396,6 +7398,7 @@ init_driver(erts_driver_t *drv, ErlDrvEntry *de, DE_Handle *handle)
- drv->timeout = de->timeout ? de->timeout : no_timeout_callback;
- drv->ready_async = de->ready_async;
- drv->process_exit = de->process_exit;
-+ drv->emergency_close = IS_DRIVER_VERSION_GE(de,3,2) ? de->emergency_close : NULL;
- if (de->stop_select)
- drv->stop_select = de->stop_select;
- else
-@@ -7414,6 +7417,8 @@ init_driver(erts_driver_t *drv, ErlDrvEntry *de, DE_Handle *handle)
- }
- }
-
-+#undef IS_DRIVER_VERSION_GE
-+
- void
- erts_destroy_driver(erts_driver_t *drv)
- {
-@@ -7557,7 +7562,7 @@ Port *erts_get_heart_port(void)
- if (!port)
- continue;
- /* only examine undead or alive ports */
-- if (erts_atomic32_read_nob(&port->state) & ERTS_PORT_SFLGS_DEAD)
-+ if (erts_atomic32_read_nob(&port->state) & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP)
- continue;
- /* immediate atom compare */
- reg = port->common.u.alive.reg;
-@@ -7568,3 +7573,23 @@ Port *erts_get_heart_port(void)
-
- return NULL;
- }
-+
-+void erts_emergency_close_ports(void)
-+{
-+ int ix, max = erts_ptab_max(&erts_port);
-+
-+ for (ix = 0; ix < max; ix++) {
-+ Port *port = erts_pix2port(ix);
-+
-+ if (!port)
-+ continue;
-+ /* only examine undead or alive ports */
-+ if (erts_atomic32_read_nob(&port->state) & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP)
-+ continue;
-+
-+ /* emergency close socket */
-+ if (port->drv_ptr->emergency_close) {
-+ port->drv_ptr->emergency_close((ErlDrvData) port->drv_data);
-+ }
-+ }
-+}
-diff --git erts/emulator/drivers/common/inet_drv.c erts/emulator/drivers/common/inet_drv.c
-index db8a251..04721d2 100644
---- erts/emulator/drivers/common/inet_drv.c
-+++ erts/emulator/drivers/common/inet_drv.c
-@@ -268,14 +268,13 @@ static BOOL (WINAPI *fpSetHandleInformation)(HANDLE,DWORD,DWORD);
- #define sock_htonl(x) htonl((x))
- #define sock_send(s,buf,len,flag) send((s),(buf),(len),(flag))
- #define sock_sendv(s, vec, size, np, flag) \
-- WSASend((s),(WSABUF*)(vec),\
-- (size),(np),(flag),NULL,NULL)
-+ WSASend((s),(WSABUF*)(vec),(size),(np),(flag),NULL,NULL)
- #define sock_recv(s,buf,len,flag) recv((s),(buf),(len),(flag))
-
- #define sock_recvfrom(s,buf,blen,flag,addr,alen) \
-- recvfrom((s),(buf),(blen),(flag),(addr),(alen))
-+ recvfrom((s),(buf),(blen),(flag),(addr),(alen))
- #define sock_sendto(s,buf,blen,flag,addr,alen) \
-- sendto((s),(buf),(blen),(flag),(addr),(alen))
-+ sendto((s),(buf),(blen),(flag),(addr),(alen))
- #define sock_hostname(buf, len) gethostname((buf), (len))
-
- #define sock_getservbyname(name,proto) getservbyname((name),(proto))
-@@ -360,9 +359,9 @@ static ssize_t writev_fallback(int fd, const struct iovec *iov, int iovcnt, int
- #define sock_accept(s, addr, len) accept((s), (addr), (len))
- #define sock_send(s,buf,len,flag) inet_send((s),(buf),(len),(flag))
- #define sock_sendto(s,buf,blen,flag,addr,alen) \
-- sendto((s),(buf),(blen),(flag),(addr),(alen))
-+ sendto((s),(buf),(blen),(flag),(addr),(alen))
- #define sock_sendv(s, vec, size, np, flag) \
-- (*(np) = writev_fallback((s), (struct iovec*)(vec), (size), (*(np))))
-+ (*(np) = writev_fallback((s), (struct iovec*)(vec), (size), (*(np))))
- #define sock_sendmsg(s,msghdr,flag) sendmsg((s),(msghdr),(flag))
-
- #define sock_open(af, type, proto) socket((af), (type), (proto))
-@@ -1178,6 +1177,7 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData, unsigned int,
- static void tcp_inet_timeout(ErlDrvData);
- static void tcp_inet_process_exit(ErlDrvData, ErlDrvMonitor *);
- static void inet_stop_select(ErlDrvEvent, void*);
-+static void inet_emergency_close(ErlDrvData);
- #ifdef __WIN32__
- static void tcp_inet_event(ErlDrvData, ErlDrvEvent);
- static void find_dynamic_functions(void);
-@@ -1288,7 +1288,8 @@ static struct erl_drv_entry tcp_inet_driver_entry =
- ERL_DRV_FLAG_USE_PORT_LOCKING|ERL_DRV_FLAG_SOFT_BUSY,
- NULL,
- tcp_inet_process_exit,
-- inet_stop_select
-+ inet_stop_select,
-+ inet_emergency_close
- };
-
-
-@@ -1341,7 +1342,8 @@ static struct erl_drv_entry udp_inet_driver_entry =
- ERL_DRV_FLAG_USE_PORT_LOCKING,
- NULL,
- NULL,
-- inet_stop_select
-+ inet_stop_select,
-+ inet_emergency_close
- };
- #endif
-
-@@ -1375,7 +1377,8 @@ static struct erl_drv_entry sctp_inet_driver_entry =
- ERL_DRV_FLAG_USE_PORT_LOCKING,
- NULL,
- NULL, /* process_exit */
-- inet_stop_select
-+ inet_stop_select,
-+ inet_emergency_close
- };
- #endif
-
-@@ -1421,7 +1424,7 @@ static int packet_inet_input(udp_descriptor* udesc, HANDLE event);
- static int packet_inet_output(udp_descriptor* udesc, HANDLE event);
- #endif
-
--/* convert descriptor poiner to inet_descriptor pointer */
-+/* convert descriptor pointer to inet_descriptor pointer */
- #define INETP(d) (&(d)->inet)
-
- #ifdef __OSE__
-@@ -4721,6 +4724,36 @@ static char* sockaddr_to_buf(struct sockaddr* addr, char* ptr, char* end)
- return NULL;
- }
-
-+/* sockaddr_bufsz_need
-+ * Returns the number of bytes needed to store the information
-+ * through sockaddr_to_buf
-+ */
-+
-+static size_t sockaddr_bufsz_need(struct sockaddr* addr)
-+{
-+ if (addr->sa_family == AF_INET || addr->sa_family == 0) {
-+ return 1 + sizeof(struct in_addr);
-+ }
-+#if defined(HAVE_IN6) && defined(AF_INET6)
-+ else if (addr->sa_family == AF_INET6) {
-+ return 1 + sizeof(struct in6_addr);
-+ }
-+#endif
-+#if defined(AF_LINK)
-+ if (addr->sa_family == AF_LINK) {
-+ struct sockaddr_dl *sdl_p = (struct sockaddr_dl*) addr;
-+ return 2 + sdl_p->sdl_alen;
-+ }
-+#endif
-+#if defined(AF_PACKET) && defined(HAVE_NETPACKET_PACKET_H)
-+ else if(addr->sa_family == AF_PACKET) {
-+ struct sockaddr_ll *sll_p = (struct sockaddr_ll*) addr;
-+ return 2 + sll_p->sll_halen;
-+ }
-+#endif
-+ return 0;
-+}
-+
- static char* buf_to_sockaddr(char* ptr, char* end, struct sockaddr* addr)
- {
- buf_check(ptr,end,1);
-@@ -5799,6 +5832,11 @@ done:
- }
-
- #elif defined(HAVE_GETIFADDRS)
-+#ifdef DEBUG
-+#define GETIFADDRS_BUFSZ (1)
-+#else
-+#define GETIFADDRS_BUFSZ (512)
-+#endif
-
- static ErlDrvSSizeT inet_ctl_getifaddrs(inet_descriptor* desc_p,
- char **rbuf_pp, ErlDrvSizeT rsize)
-@@ -5809,15 +5847,15 @@ static ErlDrvSSizeT inet_ctl_getifaddrs(inet_descriptor* desc_p,
- char *buf_p;
- char *buf_alloc_p;
-
-- buf_size = 512;
-- buf_alloc_p = ALLOC(buf_size);
-+ buf_size = GETIFADDRS_BUFSZ;
-+ buf_alloc_p = ALLOC(GETIFADDRS_BUFSZ);
- buf_p = buf_alloc_p;
- # define BUF_ENSURE(Size) \
- do { \
- int NEED_, GOT_ = buf_p - buf_alloc_p; \
- NEED_ = GOT_ + (Size); \
- if (NEED_ > buf_size) { \
-- buf_size = NEED_ + 512; \
-+ buf_size = NEED_ + GETIFADDRS_BUFSZ; \
- buf_alloc_p = REALLOC(buf_alloc_p, buf_size); \
- buf_p = buf_alloc_p + GOT_; \
- } \
-@@ -5830,7 +5868,7 @@ static ErlDrvSSizeT inet_ctl_getifaddrs(inet_descriptor* desc_p,
- while (! (P_ = sockaddr_to_buf((sa), buf_p, \
- buf_alloc_p+buf_size))) { \
- int GOT_ = buf_p - buf_alloc_p; \
-- buf_size += 512; \
-+ buf_size += GETIFADDRS_BUFSZ; \
- buf_alloc_p = REALLOC(buf_alloc_p, buf_size); \
- buf_p = buf_alloc_p + GOT_; \
- } \
-@@ -5887,10 +5925,11 @@ static ErlDrvSSizeT inet_ctl_getifaddrs(inet_descriptor* desc_p,
- || ifa_p->ifa_addr->sa_family == AF_PACKET
- #endif
- ) {
-- char *bp = buf_p;
-- BUF_ENSURE(1);
-- SOCKADDR_TO_BUF(INET_IFOPT_HWADDR, ifa_p->ifa_addr);
-- if (buf_p - bp < 4) buf_p = bp; /* Empty hwaddr */
-+ size_t need = sockaddr_bufsz_need(ifa_p->ifa_addr);
-+ if (need > 3) {
-+ BUF_ENSURE(1 + need);
-+ SOCKADDR_TO_BUF(INET_IFOPT_HWADDR, ifa_p->ifa_addr);
-+ }
- }
- #endif
- }
-@@ -5905,6 +5944,7 @@ static ErlDrvSSizeT inet_ctl_getifaddrs(inet_descriptor* desc_p,
- return buf_size;
- # undef BUF_ENSURE
- }
-+#undef GETIFADDRS_BUFSZ
-
- #else
-
-@@ -8204,6 +8244,19 @@ static void inet_stop(inet_descriptor* desc)
- FREE(desc);
- }
-
-+static void inet_emergency_close(ErlDrvData data)
-+{
-+ /* valid for any (UDP, TCP or SCTP) descriptor */
-+ tcp_descriptor* tcp_desc = (tcp_descriptor*)data;
-+ inet_descriptor* desc = INETP(tcp_desc);
-+ DEBUGF(("inet_emergency_close(%ld) {s=%d\r\n",
-+ (long)desc->port, desc->s));
-+ if (desc->s != INVALID_SOCKET) {
-+ sock_close(desc->s);
-+ }
-+}
-+
-+
- static void set_default_msgq_limits(ErlDrvPort port)
- {
- ErlDrvSizeT q_high = INET_HIGH_MSGQ_WATERMARK;
-diff --git erts/emulator/sys/unix/erl_child_setup.c erts/emulator/sys/unix/erl_child_setup.c
-index 94eb6b1..5ad92da 100644
---- erts/emulator/sys/unix/erl_child_setup.c
-+++ erts/emulator/sys/unix/erl_child_setup.c
-@@ -101,7 +101,9 @@ main(int argc, char *argv[])
- if (sscanf(argv[CS_ARGV_FD_CR_IX], "%d:%d", &from, &to) != 2)
- return 1;
-
--#if defined(__ANDROID__)
-+#if defined(HAVE_CLOSEFROM)
-+ closefrom(from);
-+#elif defined(__ANDROID__)
- for (i = from; i <= to; i++) {
- if (i!=__system_properties_fd)
- (void) close(i);
-@@ -109,13 +111,6 @@ main(int argc, char *argv[])
- #else
- for (i = from; i <= to; i++)
- (void) close(i);
--#endif /* __ANDROID__ */
--
--#if defined(HAVE_CLOSEFROM)
-- closefrom(from);
--#else
-- for (i = from; i <= to; i++)
-- (void) close(i);
- #endif
-
- if (!(argv[CS_ARGV_WD_IX][0] == '.' && argv[CS_ARGV_WD_IX][1] == '\0')
-@@ -147,8 +142,6 @@ main(int argc, char *argv[])
- return 1;
- }
-
--
--
- #if defined(__ANDROID__)
- int __system_properties_fd(void)
- {
-diff --git erts/emulator/sys/unix/sys.c erts/emulator/sys/unix/sys.c
-index 0d677d5..cd87b32 100644
---- erts/emulator/sys/unix/sys.c
-+++ erts/emulator/sys/unix/sys.c
-@@ -202,8 +202,6 @@ static erts_smp_atomic_t sys_misc_mem_sz;
- #if defined(ERTS_SMP)
- static void smp_sig_notify(char c);
- static int sig_notify_fds[2] = {-1, -1};
--#elif defined(USE_THREADS)
--static int async_fd[2];
- #endif
-
- #if CHLDWTHR || defined(ERTS_SMP)
-@@ -246,6 +244,8 @@ static void note_child_death(int, int);
- static void* child_waiter(void *);
- #endif
-
-+static int crashdump_companion_cube_fd = -1;
-+
- /********************* General functions ****************************/
-
- /* This is used by both the drivers and general I/O, must be set early */
-@@ -575,6 +575,14 @@ erts_sys_pre_init(void)
- close(fd);
- }
-
-+ /* We need a file descriptor to close in the crashdump creation.
-+ * We close this one to be sure we can get a fd for our real file ...
-+ * so, we create one here ... a stone to carry all the way home.
-+ */
-+
-+ crashdump_companion_cube_fd = open("/dev/null", O_RDONLY);
-+
-+ /* don't lose it, there will be cake */
- }
-
- void
-@@ -719,14 +727,13 @@ static ERTS_INLINE int
- prepare_crash_dump(int secs)
- {
- #define NUFBUF (3)
-- int i, max;
-+ int i;
- char env[21]; /* enough to hold any 64-bit integer */
- size_t envsz;
- DeclareTmpHeapNoproc(heap,NUFBUF);
- Port *heart_port;
- Eterm *hp = heap;
- Eterm list = NIL;
-- int heart_fd[2] = {-1,-1};
- int has_heart = 0;
-
- UseTmpHeapNoproc(NUFBUF);
-@@ -749,43 +756,22 @@ prepare_crash_dump(int secs)
- alarm((unsigned int)secs);
- }
-
-+ /* close all viable sockets via emergency close callbacks.
-+ * Specifically we want to close epmd sockets.
-+ */
-+
-+ erts_emergency_close_ports();
-+
- if (heart_port) {
-- /* hearts input fd
-- * We "know" drv_data is the in_fd since the port is started with read|write
-- */
-- heart_fd[0] = (int)heart_port->drv_data;
-- heart_fd[1] = (int)driver_data[heart_fd[0]].ofd;
-- has_heart = 1;
--
-+ has_heart = 1;
- list = CONS(hp, make_small(8), list); hp += 2;
--
- /* send to heart port, CMD = 8, i.e. prepare crash dump =o */
- erts_port_output(NULL, ERTS_PORT_SIG_FLG_FORCE_IMM_CALL, heart_port,
- heart_port->common.id, list, NULL);
- }
-
-- /* Make sure we unregister at epmd (unknown fd) and get at least
-- one free filedescriptor (for erl_crash.dump) */
--
-- max = max_files;
-- if (max < 1024)
-- max = 1024;
-- for (i = 3; i < max; i++) {
--#if defined(ERTS_SMP)
-- /* We don't want to close the signal notification pipe... */
-- if (i == sig_notify_fds[0] || i == sig_notify_fds[1])
-- continue;
--#elif defined(USE_THREADS)
-- /* We don't want to close the async notification pipe... */
-- if (i == async_fd[0] || i == async_fd[1])
-- continue;
--#endif
-- /* We don't want to close our heart yet ... */
-- if (i == heart_fd[0] || i == heart_fd[1])
-- continue;
--
-- close(i);
-- }
-+ /* Make sure we have a fd for our crashdump file. */
-+ close(crashdump_companion_cube_fd);
-
- envsz = sizeof(env);
- i = erts_sys_getenv__("ERL_CRASH_DUMP_NICE", env, &envsz);
-@@ -1574,9 +1560,13 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* op
- goto child_error;
- }
-
-+#if defined(HAVE_CLOSEFROM)
-+ closefrom(opts->use_stdio ? 3 : 5);
-+#else
- for (i = opts->use_stdio ? 3 : 5; i < max_files; i++)
- (void) close(i);
--
-+#endif
-+
- if (opts->wd && chdir(opts->wd) < 0)
- goto child_error;
-
-diff --git erts/vsn.mk erts/vsn.mk
-index d0dc8f7..e4b071b 100644
---- erts/vsn.mk
-+++ erts/vsn.mk
-@@ -17,7 +17,7 @@
- # %CopyrightEnd%
- #
-
--VSN = 6.3
-+VSN = 6.3.1
-
- # Port number 4365 in 4.2
- # Port number 4366 in 4.3
-diff --git lib/inets/doc/src/httpd.xml lib/inets/doc/src/httpd.xml
-index 4ca038c..20c8a6b 100644
---- lib/inets/doc/src/httpd.xml
-+++ lib/inets/doc/src/httpd.xml
-@@ -4,7 +4,7 @@
- <erlref>
- <header>
- <copyright>
-- <year>1997</year><year>2013</year>
-+ <year>1997</year><year>2015</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
-@@ -249,7 +249,16 @@
- <p>Limits the size of the message header of HTTP request.
- Defaults to 10240. </p>
- </item>
--
-+
-+ <marker id="prop_max_content_length"></marker>
-+ <tag>{max_content_length, integer()}</tag>
-+ <item>
-+ <p>Maximum Content-Length in an incoming request, in bytes. Requests
-+ with content larger than this are answered with Status 413.
-+ Defaults to 100000000 (100 MB).
-+ </p>
-+ </item>
-+
- <marker id="prop_max_uri"></marker>
- <tag>{max_uri_size, integer()}</tag>
- <item>
-diff --git lib/inets/doc/src/notes.xml lib/inets/doc/src/notes.xml
-index fb70344..7f73aa5 100644
---- lib/inets/doc/src/notes.xml
-+++ lib/inets/doc/src/notes.xml
-@@ -32,7 +32,40 @@
- <file>notes.xml</file>
- </header>
-
-- <section><title>Inets 5.10.4</title>
-+ <section><title>Inets 5.10.5</title>
-+
-+ <section><title>Fixed Bugs and Malfunctions</title>
-+ <list>
-+ <item>
-+ <p>
-+ mod_alias now handles https-URIs properly</p>
-+ <p>
-+ Consistent view of configuration parameter
-+ keep_alive_timeout, should be presented in the
-+ httpd:info/[1,2] function in the same unit as it is
-+ inputted.</p>
-+ <p>
-+ Own Id: OTP-12436 Aux Id: seq12786 </p>
-+ </item>
-+ </list>
-+ </section>
-+
-+
-+ <section><title>Improvements and New Features</title>
-+ <list>
-+ <item>
-+ <p>
-+ Gracefully handle invalid content-lenght headers instead
-+ of crashing in list_to_integer.</p>
-+ <p>
-+ Own Id: OTP-12429</p>
-+ </item>
-+ </list>
-+ </section>
-+
-+</section>
-+
-+<section><title>Inets 5.10.4</title>
-
- <section><title>Fixed Bugs and Malfunctions</title>
- <list>
-diff --git lib/inets/src/http_lib/http_internal.hrl lib/inets/src/http_lib/http_internal.hrl
-index 53b776c..5442574 100644
---- lib/inets/src/http_lib/http_internal.hrl
-+++ lib/inets/src/http_lib/http_internal.hrl
-@@ -1,7 +1,7 @@
- %%
- %% %CopyrightBegin%
- %%
--%% Copyright Ericsson AB 2002-2014. All Rights Reserved.
-+%% Copyright Ericsson AB 2002-2015. All Rights Reserved.
- %%
- %% The contents of this file are subject to the Erlang Public License,
- %% Version 1.1, (the "License"); you may not use this file except in
-@@ -28,6 +28,7 @@
- -define(HTTP_MAX_URI_SIZE, nolimit).
- -define(HTTP_MAX_VERSION_STRING, 8).
- -define(HTTP_MAX_METHOD_STRING, 20).
-+-define(HTTP_MAX_CONTENT_LENGTH, 100000000).
-
- -ifndef(HTTP_DEFAULT_SSL_KIND).
- -define(HTTP_DEFAULT_SSL_KIND, essl).
-diff --git lib/inets/src/http_lib/http_request.erl lib/inets/src/http_lib/http_request.erl
-index f295453..a0833dd 100644
---- lib/inets/src/http_lib/http_request.erl
-+++ lib/inets/src/http_lib/http_request.erl
-@@ -1,7 +1,7 @@
- %%
- %% %CopyrightBegin%
- %%
--%% Copyright Ericsson AB 2005-2014. All Rights Reserved.
-+%% Copyright Ericsson AB 2005-2015. All Rights Reserved.
- %%
- %% The contents of this file are subject to the Erlang Public License,
- %% Version 1.1, (the "License"); you may not use this file except in
-@@ -21,8 +21,16 @@
-
- -include("http_internal.hrl").
-
---export([headers/2, http_headers/1, is_absolut_uri/1]).
-+-export([headers/2, http_headers/1, is_absolut_uri/1, key_value/1]).
-
-+
-+key_value(KeyValueStr) ->
-+ case lists:splitwith(fun($:) -> false; (_) -> true end, KeyValueStr) of
-+ {Key, [$: | Value]} ->
-+ {http_util:to_lower(string:strip(Key)), string:strip(Value)};
-+ {_, []} ->
-+ undefined
-+ end.
- %%-------------------------------------------------------------------------
- %% headers(HeaderList, #http_request_h{}) -> #http_request_h{}
- %% HeaderList - ["HeaderField:Value"]
-@@ -34,14 +42,12 @@
- %%-------------------------------------------------------------------------
- headers([], Headers) ->
- Headers;
--headers([Header | Tail], Headers) ->
-- case lists:splitwith(fun($:) -> false; (_) -> true end, Header) of
-- {Key, [$: | Value]} ->
-- headers(Tail, headers(http_util:to_lower(string:strip(Key)),
-- string:strip(Value), Headers));
-- {_, []} ->
-- headers(Tail, Headers)
-- end.
-+headers([{Key, Value} | Tail], Headers) ->
-+ headers(Tail, headers(Key, Value, Headers));
-+headers([undefined], Headers) ->
-+ Headers;
-+headers(KeyValues, Headers) ->
-+ headers([key_value(KeyValue) || KeyValue <- KeyValues], Headers).
-
- %%-------------------------------------------------------------------------
- %% headers(#http_request_h{}) -> HeaderList
-diff --git lib/inets/src/http_server/httpd_conf.erl lib/inets/src/http_server/httpd_conf.erl
-index 27446ca..55698d5 100644
---- lib/inets/src/http_server/httpd_conf.erl
-+++ lib/inets/src/http_server/httpd_conf.erl
-@@ -1,7 +1,7 @@
- %%
- %% %CopyrightBegin%
- %%
--%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
-+%% Copyright Ericsson AB 1997-2015. All Rights Reserved.
- %%
- %% The contents of this file are subject to the Erlang Public License,
- %% Version 1.1, (the "License"); you may not use this file except in
-@@ -205,13 +205,13 @@ load("MaxURISize " ++ MaxHeaderSize, []) ->
- " is an invalid number of MaxHeaderSize")}
- end;
-
--load("MaxBodySize " ++ MaxBodySize, []) ->
-- case make_integer(MaxBodySize) of
-+load("MaxContentLength " ++ Max, []) ->
-+ case make_integer(Max) of
- {ok, Integer} ->
-- {ok, [], {max_body_size,Integer}};
-+ {ok, [], {max_content_length, Integer}};
- {error, _} ->
-- {error, ?NICE(clean(MaxBodySize) ++
-- " is an invalid number of MaxBodySize")}
-+ {error, ?NICE(clean(Max) ++
-+ " is an invalid number of MaxContentLength")}
- end;
-
- load("ServerName " ++ ServerName, []) ->
-@@ -337,7 +337,7 @@ load("MaxKeepAliveRequest " ++ MaxRequests, []) ->
- load("KeepAliveTimeout " ++ Timeout, []) ->
- case make_integer(Timeout) of
- {ok, Integer} ->
-- {ok, [], {keep_alive_timeout, Integer*1000}};
-+ {ok, [], {keep_alive_timeout, Integer}};
- {error, _} ->
- {error, ?NICE(clean(Timeout)++" is an invalid KeepAliveTimeout")}
- end;
-@@ -569,6 +569,12 @@ validate_config_params([{max_body_size, Value} | Rest])
- validate_config_params([{max_body_size, Value} | _]) ->
- throw({max_body_size, Value});
-
-+validate_config_params([{max_content_length, Value} | Rest])
-+ when is_integer(Value) andalso (Value > 0) ->
-+ validate_config_params(Rest);
-+validate_config_params([{max_content_length, Value} | _]) ->
-+ throw({max_content_length, Value});
-+
- validate_config_params([{server_name, Value} | Rest])
- when is_list(Value) ->
- validate_config_params(Rest);
-@@ -635,7 +641,7 @@ validate_config_params([{max_keep_alive_request, Value} | Rest])
- when is_integer(Value) andalso (Value > 0) ->
- validate_config_params(Rest);
- validate_config_params([{max_keep_alive_request, Value} | _]) ->
-- throw({max_header_size, Value});
-+ throw({max_keep_alive_request, Value});
-
- validate_config_params([{keep_alive_timeout, Value} | Rest])
- when is_integer(Value) andalso (Value >= 0) ->
-@@ -799,7 +805,7 @@ store({server_tokens, ServerTokens} = Entry, _ConfigList) ->
- Server = server(ServerTokens),
- {ok, [Entry, {server, Server}]};
- store({keep_alive_timeout, KeepAliveTimeout}, _ConfigList) ->
-- {ok, {keep_alive_timeout, KeepAliveTimeout * 1000}};
-+ {ok, {keep_alive_timeout, KeepAliveTimeout}};
- store(ConfigListEntry, _ConfigList) ->
- {ok, ConfigListEntry}.
-
-diff --git lib/inets/src/http_server/httpd_request.erl lib/inets/src/http_server/httpd_request.erl
-index 712c735..6985065 100644
---- lib/inets/src/http_server/httpd_request.erl
-+++ lib/inets/src/http_server/httpd_request.erl
-@@ -1,7 +1,7 @@
- %%
- %% %CopyrightBegin%
- %%
--%% Copyright Ericsson AB 2005-2014. All Rights Reserved.
-+%% Copyright Ericsson AB 2005-2015. All Rights Reserved.
- %%
- %% The contents of this file are subject to the Erlang Public License,
- %% Version 1.1, (the "License"); you may not use this file except in
-@@ -118,18 +118,17 @@ validate(Method, Uri, Version) ->
- %% create it.
- %% ----------------------------------------------------------------------
- update_mod_data(ModData, Method, RequestURI, HTTPVersion, Headers)->
-- ParsedHeaders = tagup_header(Headers),
-- PersistentConn = get_persistens(HTTPVersion, ParsedHeaders,
-+ PersistentConn = get_persistens(HTTPVersion, Headers,
- ModData#mod.config_db),
- {ok, ModData#mod{data = [],
- method = Method,
- absolute_uri = format_absolute_uri(RequestURI,
-- ParsedHeaders),
-+ Headers),
- request_uri = format_request_uri(RequestURI),
- http_version = HTTPVersion,
- request_line = Method ++ " " ++ RequestURI ++
- " " ++ HTTPVersion,
-- parsed_header = ParsedHeaders,
-+ parsed_header = Headers,
- connection = PersistentConn}}.
-
- %%%========================================================================
-@@ -146,14 +145,14 @@ parse_method(_, _, _, Max, _, _) ->
- %% We do not know the version of the client as it comes after the
- %% method send the lowest version in the response so that the client
- %% will be able to handle it.
-- {error, {too_long, Max, 413, "Method unreasonably long"}, lowest_version()}.
-+ {error, {size_error, Max, 413, "Method unreasonably long"}, lowest_version()}.
-
- parse_uri(_, _, Current, MaxURI, _, _)
- when (Current > MaxURI) andalso (MaxURI =/= nolimit) ->
- %% We do not know the version of the client as it comes after the
- %% uri send the lowest version in the response so that the client
- %% will be able to handle it.
-- {error, {too_long, MaxURI, 414, "URI unreasonably long"},lowest_version()};
-+ {error, {size_error, MaxURI, 414, "URI unreasonably long"},lowest_version()};
- parse_uri(<<>>, URI, Current, Max, MaxSizes, Result) ->
- {?MODULE, parse_uri, [URI, Current, Max, MaxSizes, Result]};
- parse_uri(<<?SP, Rest/binary>>, URI, _, _, MaxSizes, Result) ->
-@@ -179,12 +178,12 @@ parse_version(<<?CR>> = Data, Version, Current, Max, MaxSizes, Result) ->
- parse_version(<<Octet, Rest/binary>>, Version, Current, Max, MaxSizes, Result) when Current =< Max ->
- parse_version(Rest, [Octet | Version], Current + 1, Max, MaxSizes, Result);
- parse_version(_, _, _, Max,_,_) ->
-- {error, {too_long, Max, 413, "Version string unreasonably long"}, lowest_version()}.
-+ {error, {size_error, Max, 413, "Version string unreasonably long"}, lowest_version()}.
-
- parse_headers(_, _, _, Current, Max, _, Result)
- when Max =/= nolimit andalso Current > Max ->
- HttpVersion = lists:nth(3, lists:reverse(Result)),
-- {error, {too_long, Max, 413, "Headers unreasonably long"}, HttpVersion};
-+ {error, {size_error, Max, 413, "Headers unreasonably long"}, HttpVersion};
-
- parse_headers(<<>>, Header, Headers, Current, Max, MaxSizes, Result) ->
- {?MODULE, parse_headers, [<<>>, Header, Headers, Current, Max,
-@@ -204,14 +203,22 @@ parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], _, _, _, Result) ->
- Result])),
- {ok, NewResult};
- parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, Header, Headers, _, _,
-- _, Result) ->
-- HTTPHeaders = [lists:reverse(Header) | Headers],
-- RequestHeaderRcord =
-- http_request:headers(HTTPHeaders, #http_request_h{}),
-- NewResult =
-- list_to_tuple(lists:reverse([Body, {RequestHeaderRcord,
-- HTTPHeaders} | Result])),
-- {ok, NewResult};
-+ MaxSizes, Result) ->
-+ case http_request:key_value(lists:reverse(Header)) of
-+ undefined -> %% Skip headers with missing :
-+ {ok, list_to_tuple(lists:reverse([Body, {http_request:headers(Headers, #http_request_h{}), Headers} | Result]))};
-+ NewHeader ->
-+ case check_header(NewHeader, MaxSizes) of
-+ ok ->
-+ {ok, list_to_tuple(lists:reverse([Body, {http_request:headers([NewHeader | Headers],
-+ #http_request_h{}),
-+ [NewHeader | Headers]} | Result]))};
-+
-+ {error, Reason} ->
-+ HttpVersion = lists:nth(3, lists:reverse(Result)),
-+ {error, Reason, HttpVersion}
-+ end
-+ end;
-
- parse_headers(<<?CR,?LF,?CR>> = Data, Header, Headers, Current, Max,
- MaxSizes, Result) ->
-@@ -243,8 +250,21 @@ parse_headers(<<?LF, Octet, Rest/binary>>, Header, Headers, Current, Max,
- MaxSizes, Result);
- parse_headers(<<?CR,?LF, Octet, Rest/binary>>, Header, Headers, _, Max,
- MaxSizes, Result) ->
-- parse_headers(Rest, [Octet], [lists:reverse(Header) | Headers],
-- 0, Max, MaxSizes, Result);
-+ case http_request:key_value(lists:reverse(Header)) of
-+ undefined -> %% Skip headers with missing :
-+ parse_headers(Rest, [Octet], Headers,
-+ 0, Max, MaxSizes, Result);
-+ NewHeader ->
-+ case check_header(NewHeader, MaxSizes) of
-+ ok ->
-+ parse_headers(Rest, [Octet], [NewHeader | Headers],
-+ 0, Max, MaxSizes, Result);
-+ {error, Reason} ->
-+ HttpVersion = lists:nth(3, lists:reverse(Result)),
-+ {error, Reason, HttpVersion}
-+ end
-+ end;
-+
- parse_headers(<<?CR>> = Data, Header, Headers, Current, Max,
- MaxSizes, Result) ->
- {?MODULE, parse_headers, [Data, Header, Headers, Current, Max,
-@@ -388,29 +408,25 @@ get_persistens(HTTPVersion,ParsedHeader,ConfigDB)->
- false
- end.
-
--
--%%----------------------------------------------------------------------
--%% tagup_header
--%%
--%% Parses the header of a HTTP request and returns a key,value tuple
--%% list containing Name and Value of each header directive as of:
--%%
--%% Content-Type: multipart/mixed -> {"Content-Type", "multipart/mixed"}
--%%
--%% But in http/1.1 the field-names are case insencitive so now it must be
--%% Content-Type: multipart/mixed -> {"content-type", "multipart/mixed"}
--%% The standard furthermore says that leading and traling white space
--%% is not a part of the fieldvalue and shall therefore be removed.
--%%----------------------------------------------------------------------
--tagup_header([]) -> [];
--tagup_header([Line|Rest]) -> [tag(Line, [])|tagup_header(Rest)].
--
--tag([], Tag) ->
-- {http_util:to_lower(lists:reverse(Tag)), ""};
--tag([$:|Rest], Tag) ->
-- {http_util:to_lower(lists:reverse(Tag)), string:strip(Rest)};
--tag([Chr|Rest], Tag) ->
-- tag(Rest, [Chr|Tag]).
--
- lowest_version()->
- "HTTP/0.9".
-+
-+check_header({"content-length", Value}, Maxsizes) ->
-+ Max = proplists:get_value(max_content_length, Maxsizes),
-+ MaxLen = length(integer_to_list(Max)),
-+ case length(Value) =< MaxLen of
-+ true ->
-+ try
-+ _ = list_to_integer(Value),
-+ ok
-+ catch _:_ ->
-+ {error, {size_error, Max, 411, "content-length not an integer"}}
-+ end;
-+ false ->
-+ {error, {size_error, Max, 413, "content-length unreasonably long"}}
-+ end;
-+check_header(_, _) ->
-+ ok.
-+
-+
-+
-diff --git lib/inets/src/http_server/httpd_request_handler.erl lib/inets/src/http_server/httpd_request_handler.erl
-index 9bea58c..f7a9fe5 100644
---- lib/inets/src/http_server/httpd_request_handler.erl
-+++ lib/inets/src/http_server/httpd_request_handler.erl
-@@ -1,7 +1,7 @@
- %%
- %% %CopyrightBegin%
- %%
--%% Copyright Ericsson AB 1997-2014. All Rights Reserved.
-+%% Copyright Ericsson AB 1997-2015. All Rights Reserved.
- %%
- %% The contents of this file are subject to the Erlang Public License,
- %% Version 1.1, (the "License"); you may not use this file except in
-@@ -96,8 +96,9 @@ init([Manager, ConfigDB, AcceptTimeout]) ->
- proc_lib:init_ack({ok, self()}),
-
- {SocketType, Socket} = await_socket_ownership_transfer(AcceptTimeout),
--
-- KeepAliveTimeOut = httpd_util:lookup(ConfigDB, keep_alive_timeout, 150000),
-+
-+ %%Timeout value is in seconds we want it in milliseconds
-+ KeepAliveTimeOut = 1000 * httpd_util:lookup(ConfigDB, keep_alive_timeout, 150),
-
- case http_transport:negotiate(SocketType, Socket, ?HANDSHAKE_TIMEOUT) of
- {error, _Error} ->
-@@ -119,11 +120,15 @@ continue_init(Manager, ConfigDB, SocketType, Socket, TimeOut) ->
- MaxHeaderSize = max_header_size(ConfigDB),
- MaxURISize = max_uri_size(ConfigDB),
- NrOfRequest = max_keep_alive_request(ConfigDB),
--
-+ MaxContentLen = max_content_length(ConfigDB),
-+
- {_, Status} = httpd_manager:new_connection(Manager),
-
- MFA = {httpd_request, parse, [[{max_uri, MaxURISize}, {max_header, MaxHeaderSize},
-- {max_version, ?HTTP_MAX_VERSION_STRING}, {max_method, ?HTTP_MAX_METHOD_STRING}]]},
-+ {max_version, ?HTTP_MAX_VERSION_STRING},
-+ {max_method, ?HTTP_MAX_METHOD_STRING},
-+ {max_content_length, MaxContentLen}
-+ ]]},
-
- State = #state{mod = Mod,
- manager = Manager,
-@@ -207,7 +212,7 @@ handle_info({Proto, Socket, Data},
- set_new_data_size(cancel_request_timeout(State), NewDataSize)
- end,
- handle_http_msg(Result, NewState);
-- {error, {too_long, MaxSize, ErrCode, ErrStr}, Version} ->
-+ {error, {size_error, MaxSize, ErrCode, ErrStr}, Version} ->
- NewModData = ModData#mod{http_version = Version},
- httpd_response:send_status(NewModData, ErrCode, ErrStr),
- Reason = io_lib:format("~p: ~p max size is ~p~n",
-@@ -444,8 +449,7 @@ handle_body(#state{headers = Headers, body = Body, mod = ModData} = State,
- error_log(Reason, ModData),
- {stop, normal, State#state{response_sent = true}};
- _ ->
-- Length =
-- list_to_integer(Headers#http_request_h.'content-length'),
-+ Length = list_to_integer(Headers#http_request_h.'content-length'),
- case ((Length =< MaxBodySize) or (MaxBodySize == nolimit)) of
- true ->
- case httpd_request:whole_body(Body, Length) of
-@@ -454,7 +458,7 @@ handle_body(#state{headers = Headers, body = Body, mod = ModData} = State,
- ModData#mod.socket,
- [{active, once}]),
- {noreply, State#state{mfa =
-- {Module, Function, Args}}};
-+ {Module, Function, Args}}};
-
- {ok, NewBody} ->
- handle_response(
-@@ -471,7 +475,7 @@ handle_body(#state{headers = Headers, body = Body, mod = ModData} = State,
- handle_expect(#state{headers = Headers, mod =
- #mod{config_db = ConfigDB} = ModData} = State,
- MaxBodySize) ->
-- Length = Headers#http_request_h.'content-length',
-+ Length = list_to_integer(Headers#http_request_h.'content-length'),
- case expect(Headers, ModData#mod.http_version, ConfigDB) of
- continue when (MaxBodySize > Length) orelse (MaxBodySize =:= nolimit) ->
- httpd_response:send_status(ModData, 100, ""),
-@@ -545,9 +549,13 @@ handle_next_request(#state{mod = #mod{connection = true} = ModData,
- init_data = ModData#mod.init_data},
- MaxHeaderSize = max_header_size(ModData#mod.config_db),
- MaxURISize = max_uri_size(ModData#mod.config_db),
-+ MaxContentLen = max_content_length(ModData#mod.config_db),
-
- MFA = {httpd_request, parse, [[{max_uri, MaxURISize}, {max_header, MaxHeaderSize},
-- {max_version, ?HTTP_MAX_VERSION_STRING}, {max_method, ?HTTP_MAX_METHOD_STRING}]]},
-+ {max_version, ?HTTP_MAX_VERSION_STRING},
-+ {max_method, ?HTTP_MAX_METHOD_STRING},
-+ {max_content_length, MaxContentLen}
-+ ]]},
- TmpState = State#state{mod = NewModData,
- mfa = MFA,
- max_keep_alive_request = decrease(Max),
-@@ -630,3 +638,5 @@ max_body_size(ConfigDB) ->
- max_keep_alive_request(ConfigDB) ->
- httpd_util:lookup(ConfigDB, max_keep_alive_request, infinity).
-
-+max_content_length(ConfigDB) ->
-+ httpd_util:lookup(ConfigDB, max_content_length, ?HTTP_MAX_CONTENT_LENGTH).
-diff --git lib/inets/src/http_server/mod_alias.erl lib/inets/src/http_server/mod_alias.erl
-index 0b9fe4c..5039cd5 100644
---- lib/inets/src/http_server/mod_alias.erl
-+++ lib/inets/src/http_server/mod_alias.erl
-@@ -1,7 +1,7 @@
- %%
- %% %CopyrightBegin%
- %%
--%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
-+%% Copyright Ericsson AB 1997-2015. All Rights Reserved.
- %%
- %% The contents of this file are subject to the Erlang Public License,
- %% Version 1.1, (the "License"); you may not use this file except in
-@@ -55,6 +55,7 @@ do(#mod{data = Data} = Info) ->
-
- do_alias(#mod{config_db = ConfigDB,
- request_uri = ReqURI,
-+ socket_type = SocketType,
- data = Data}) ->
- {ShortPath, Path, AfterPath} =
- real_name(ConfigDB, ReqURI, which_alias(ConfigDB)),
-@@ -70,8 +71,9 @@ do_alias(#mod{config_db = ConfigDB,
- (LastChar =/= $/)) ->
- ?hdrt("directory and last-char is a /", []),
- ServerName = which_server_name(ConfigDB),
-- Port = port_string( which_port(ConfigDB) ),
-- URL = "http://" ++ ServerName ++ Port ++ ReqURI ++ "/",
-+ Port = port_string(which_port(ConfigDB)),
-+ Protocol = get_protocol(SocketType),
-+ URL = Protocol ++ ServerName ++ Port ++ ReqURI ++ "/",
- ReasonPhrase = httpd_util:reason_phrase(301),
- Message = httpd_util:message(301, URL, ConfigDB),
- {proceed,
-@@ -94,6 +96,12 @@ port_string(80) ->
- port_string(Port) ->
- ":" ++ integer_to_list(Port).
-
-+get_protocol(ip_comm) ->
-+ "http://";
-+get_protocol(_) ->
-+ %% Should clean up to have only one ssl type essl vs ssl is not relevant any more
-+ "https://".
-+
- %% real_name
-
- real_name(ConfigDB, RequestURI, []) ->
-diff --git lib/inets/test/http_format_SUITE.erl lib/inets/test/http_format_SUITE.erl
-index d4a3f28..5952e9f 100644
---- lib/inets/test/http_format_SUITE.erl
-+++ lib/inets/test/http_format_SUITE.erl
-@@ -1,7 +1,7 @@
- %%
- %% %CopyrightBegin%
- %%
--%% Copyright Ericsson AB 2004-2014. All Rights Reserved.
-+%% Copyright Ericsson AB 2004-2015. All Rights Reserved.
- %%
- %% The contents of this file are subject to the Erlang Public License,
- %% Version 1.1, (the "License"); you may not use this file except in
-@@ -355,10 +355,12 @@ http_request(Config) when is_list(Config) ->
- "http://www.erlang.org",
- "HTTP/1.1",
- {#http_request_h{host = "www.erlang.org", te = []},
-- ["te: ","host:www.erlang.org"]}, <<>>} =
-+ [{"te", []}, {"host", "www.erlang.org"}]}, <<>>} =
- parse(httpd_request, parse, [[{max_header, ?HTTP_MAX_HEADER_SIZE},
- {max_version, ?HTTP_MAX_VERSION_STRING},
-- {max_method, ?HTTP_MAX_METHOD_STRING}]],
-+ {max_method, ?HTTP_MAX_METHOD_STRING},
-+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH}
-+ ]],
- HttpHead),
-
- HttpHead1 = ["GET http://www.erlang.org HTTP/1.1" ++
-@@ -369,7 +371,9 @@ http_request(Config) when is_list(Config) ->
- {#http_request_h{}, []}, <<>>} =
- parse(httpd_request, parse, [[{max_header, ?HTTP_MAX_HEADER_SIZE},
- {max_version, ?HTTP_MAX_VERSION_STRING},
-- {max_method, ?HTTP_MAX_METHOD_STRING}]], HttpHead1),
-+ {max_method, ?HTTP_MAX_METHOD_STRING},
-+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH}
-+ ]], HttpHead1),
-
-
- HttpHead2 = ["GET http://www.erlang.org HTTP/1.1" ++
-@@ -380,7 +384,9 @@ http_request(Config) when is_list(Config) ->
- {#http_request_h{}, []}, <<>>} =
- parse(httpd_request, parse, [[{max_header, ?HTTP_MAX_HEADER_SIZE},
- {max_version, ?HTTP_MAX_VERSION_STRING},
-- {max_method, ?HTTP_MAX_METHOD_STRING}]], HttpHead2),
-+ {max_method, ?HTTP_MAX_METHOD_STRING},
-+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH}
-+ ]], HttpHead2),
-
- %% Note the following body is not related to the headers above
- HttpBody = ["<HTML>\n<HEAD>\n<TITLE> dummy </TITLE>\n</HEAD>\n<BODY>\n",
-diff --git lib/inets/test/httpc_SUITE.erl lib/inets/test/httpc_SUITE.erl
-index c535d59..390f2bb 100644
---- lib/inets/test/httpc_SUITE.erl
-+++ lib/inets/test/httpc_SUITE.erl
-@@ -1,7 +1,7 @@
- %%
- %% %CopyrightBegin%
- %%
--%% Copyright Ericsson AB 2004-2014. All Rights Reserved.
-+%% Copyright Ericsson AB 2004-2015. All Rights Reserved.
- %%
- %% The contents of this file are subject to the Erlang Public License,
- %% Version 1.1, (the "License"); you may not use this file except in
-@@ -1246,8 +1246,9 @@ dummy_server_init(Caller, ip_comm, Inet, _) ->
- dummy_ipcomm_server_loop({httpd_request, parse, [[{max_uri, ?HTTP_MAX_URI_SIZE},
- {max_header, ?HTTP_MAX_HEADER_SIZE},
- {max_version,?HTTP_MAX_VERSION_STRING},
-- {max_method, ?HTTP_MAX_METHOD_STRING}]]},
-- [], ListenSocket);
-+ {max_method, ?HTTP_MAX_METHOD_STRING},
-+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH}]]},
-+ [], ListenSocket);
-
- dummy_server_init(Caller, ssl, Inet, SSLOptions) ->
- BaseOpts = [binary, {reuseaddr,true}, {active, false} |
-@@ -1261,7 +1262,9 @@ dummy_ssl_server_init(Caller, BaseOpts, Inet) ->
- dummy_ssl_server_loop({httpd_request, parse, [[{max_uri, ?HTTP_MAX_URI_SIZE},
- {max_method, ?HTTP_MAX_METHOD_STRING},
- {max_version,?HTTP_MAX_VERSION_STRING},
-- {max_method, ?HTTP_MAX_METHOD_STRING}]]},
-+ {max_method, ?HTTP_MAX_METHOD_STRING},
-+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH}
-+ ]]},
- [], ListenSocket).
-
- dummy_ipcomm_server_loop(MFA, Handlers, ListenSocket) ->
-@@ -1338,16 +1341,20 @@ handle_request(Module, Function, Args, Socket) ->
- stop ->
- stop;
- <<>> ->
-- {httpd_request, parse, [[<<>>, [{max_uri, ?HTTP_MAX_URI_SIZE},
-+ {httpd_request, parse, [[{max_uri,?HTTP_MAX_URI_SIZE},
- {max_header, ?HTTP_MAX_HEADER_SIZE},
- {max_version,?HTTP_MAX_VERSION_STRING},
-- {max_method, ?HTTP_MAX_METHOD_STRING}]]]};
-+ {max_method, ?HTTP_MAX_METHOD_STRING},
-+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH}
-+ ]]};
- Data ->
- handle_request(httpd_request, parse,
- [Data, [{max_uri, ?HTTP_MAX_URI_SIZE},
-- {max_header, ?HTTP_MAX_HEADER_SIZE},
-- {max_version,?HTTP_MAX_VERSION_STRING},
-- {max_method, ?HTTP_MAX_METHOD_STRING}]], Socket)
-+ {max_header, ?HTTP_MAX_HEADER_SIZE},
-+ {max_version,?HTTP_MAX_VERSION_STRING},
-+ {max_method, ?HTTP_MAX_METHOD_STRING},
-+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH}
-+ ]], Socket)
- end;
- NewMFA ->
- NewMFA
-@@ -1437,7 +1444,7 @@ dummy_ssl_server_hang_loop(_) ->
-
- ensure_host_header_with_port([]) ->
- false;
--ensure_host_header_with_port(["host: " ++ Host| _]) ->
-+ensure_host_header_with_port([{"host", Host}| _]) ->
- case string:tokens(Host, [$:]) of
- [_ActualHost, _Port] ->
- true;
-@@ -1449,7 +1456,7 @@ ensure_host_header_with_port([_|T]) ->
-
- auth_header([]) ->
- auth_header_not_found;
--auth_header(["authorization:" ++ Value | _]) ->
-+auth_header([{"authorization", Value} | _]) ->
- {ok, string:strip(Value)};
- auth_header([_ | Tail]) ->
- auth_header(Tail).
-@@ -1466,7 +1473,7 @@ handle_auth("Basic " ++ UserInfo, Challange, DefaultResponse) ->
-
- check_cookie([]) ->
- ct:fail(no_cookie_header);
--check_cookie(["cookie:" ++ _Value | _]) ->
-+check_cookie([{"cookie", _} | _]) ->
- ok;
- check_cookie([_Head | Tail]) ->
- check_cookie(Tail).
-diff --git lib/inets/test/httpd_SUITE.erl lib/inets/test/httpd_SUITE.erl
-index 4010597..342004f 100644
---- lib/inets/test/httpd_SUITE.erl
-+++ lib/inets/test/httpd_SUITE.erl
-@@ -1,7 +1,7 @@
- %%
- %% %CopyrightBegin%
- %%
--%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
-+%% Copyright Ericsson AB 2013-2015. All Rights Reserved.
- %%
- %% The contents of this file are subject to the Erlang Public License,
- %% Version 1.1, (the "License"); you may not use this file except in
-@@ -132,6 +132,7 @@ http_get() ->
- bad_hex,
- missing_CR,
- max_header,
-+ max_content_length,
- ipv6
- ].
-
-@@ -979,13 +980,22 @@ max_header(Config) when is_list(Config) ->
- Host = ?config(host, Config),
- case Version of
- "HTTP/0.9" ->
-- {skip, no_implemented};
-+ {skip, not_implemented};
- _ ->
- dos_hostname(?config(type, Config), ?config(port, Config), Host,
- ?config(node, Config), Version, ?MAX_HEADER_SIZE)
- end.
-
- %%-------------------------------------------------------------------------
-+max_content_length() ->
-+ ["Denial Of Service (DOS) attack, prevented by max_content_length"].
-+max_content_length(Config) when is_list(Config) ->
-+ Version = ?config(http_version, Config),
-+ Host = ?config(host, Config),
-+ garbage_content_length(?config(type, Config), ?config(port, Config), Host,
-+ ?config(node, Config), Version).
-+
-+%%-------------------------------------------------------------------------
- security_1_1(Config) when is_list(Config) ->
- security([{http_version, "HTTP/1.1"} | Config]).
-
-@@ -1368,7 +1378,9 @@ server_config(http_reload, Config) ->
- server_config(https_reload, Config) ->
- [{keep_alive_timeout, 2}] ++ server_config(https, Config);
- server_config(http_limit, Config) ->
-- [{max_clients, 1}] ++ server_config(http, Config);
-+ [{max_clients, 1},
-+ %% Make sure option checking code is run
-+ {max_content_length, 100000002}] ++ server_config(http, Config);
- server_config(https_limit, Config) ->
- [{max_clients, 1}] ++ server_config(https, Config);
- server_config(http_basic_auth, Config) ->
-@@ -1814,7 +1826,7 @@ dos_hostname(Type, Port, Host, Node, Version, Max) ->
-
- ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
- dos_hostname_request(TooLongHeader, Version),
-- [{statuscode, dos_code(Version)},
-+ [{statuscode, request_entity_too_large_code(Version)},
- {version, Version}]).
- dos_hostname_request(Host, Version) ->
- dos_http_request("GET / ", Version, Host).
-@@ -1824,11 +1836,32 @@ dos_http_request(Request, "HTTP/1.1" = Version, Host) ->
- dos_http_request(Request, Version, Host) ->
- Request ++ Version ++ "\r\nhost:" ++ Host ++ "\r\n\r\n".
-
--dos_code("HTTP/1.0") ->
-+request_entity_too_large_code("HTTP/1.0") ->
- 403; %% 413 not defined in HTTP/1.0
--dos_code(_) ->
-+request_entity_too_large_code(_) ->
- 413.
-
-+length_required_code("HTTP/1.0") ->
-+ 403; %% 411 not defined in HTTP/1.0
-+length_required_code(_) ->
-+ 411.
-+
-+garbage_content_length(Type, Port, Host, Node, Version) ->
-+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
-+ garbage_content_length_request("GET / ", Version, Host, "aaaa"),
-+ [{statuscode, length_required_code(Version)},
-+ {version, Version}]),
-+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
-+ garbage_content_length_request("GET / ", Version, Host,
-+ lists:duplicate($a, 100)),
-+ [{statuscode, request_entity_too_large_code(Version)},
-+ {version, Version}]).
-+
-+garbage_content_length_request(Request, Version, Host, Garbage) ->
-+ http_request(Request, Version, Host,
-+ {"content-length:" ++ Garbage, "Body with garbage content length indicator"}).
-+
-+
- update_password(Node, ServerRoot, _Address, Port, AuthPrefix, Dir, Old, New)->
- Directory = filename:join([ServerRoot, "htdocs", AuthPrefix ++ Dir]),
- rpc:call(Node, mod_auth, update_password,
-diff --git lib/inets/vsn.mk lib/inets/vsn.mk
-index dbae5e4..7d11916 100644
---- lib/inets/vsn.mk
-+++ lib/inets/vsn.mk
-@@ -18,6 +18,6 @@
- # %CopyrightEnd%
-
- APPLICATION = inets
--INETS_VSN = 5.10.4
-+INETS_VSN = 5.10.5
- PRE_VSN =
- APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
-diff --git otp_versions.table otp_versions.table
-index 5753385..41c05e7 100644
---- otp_versions.table
-+++ otp_versions.table
-@@ -1,3 +1,4 @@
-+OTP-17.4.1 : erts-6.3.1 inets-5.10.5 # asn1-3.0.3 common_test-1.9 compiler-5.0.3 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.4.2 debugger-4.0.2 dialyzer-2.7.3 diameter-1.8 edoc-0.7.16 eldap-1.1 erl_docgen-0.3.7 erl_interface-3.7.20 et-1.5 eunit-2.2.9 gs-1.5.16 hipe-3.11.2 ic-4.3.6 jinterface-1.5.12 kernel-3.1 megaco-3.17.3 mnesia-4.12.4 observer-2.0.3 odbc-2.10.22 orber-3.7.1 os_mon-2.3 ose-1.0.2 otp_mibs-1.0.10 parsetools-2.0.12 percept-0.8.10 public_key-0.22.1 reltool-0.6.6 runtime_tools-1.8.15 sasl-2.4.1 snmp-5.1.1 ssh-3.1 ssl-5.3.8 stdlib-2.3 syntax_tools-1.6.17 test_server-3.7.2 tools-2.7.1 typer-0.9.8 webtool-0.8.10 wx-1.3.2 xmerl-1.3.7 :
- OTP-17.4 : asn1-3.0.3 common_test-1.9 compiler-5.0.3 crypto-3.4.2 debugger-4.0.2 dialyzer-2.7.3 diameter-1.8 edoc-0.7.16 eldap-1.1 erl_docgen-0.3.7 erl_interface-3.7.20 erts-6.3 eunit-2.2.9 hipe-3.11.2 inets-5.10.4 jinterface-1.5.12 kernel-3.1 megaco-3.17.3 mnesia-4.12.4 observer-2.0.3 odbc-2.10.22 otp_mibs-1.0.10 parsetools-2.0.12 percept-0.8.10 runtime_tools-1.8.15 snmp-5.1.1 ssh-3.1 ssl-5.3.8 stdlib-2.3 syntax_tools-1.6.17 test_server-3.7.2 tools-2.7.1 wx-1.3.2 # cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 et-1.5 gs-1.5.16 ic-4.3.6 orber-3.7.1 os_mon-2.3 ose-1.0.2 public_key-0.22.1 reltool-0.6.6 sasl-2.4.1 typer-0.9.8 webtool-0.8.10 xmerl-1.3.7 :
- OTP-17.3.4 : erts-6.2.1 # asn1-3.0.2 common_test-1.8.2 compiler-5.0.2 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.4.1 debugger-4.0.1 dialyzer-2.7.2 diameter-1.7.1 edoc-0.7.15 eldap-1.0.4 erl_docgen-0.3.6 erl_interface-3.7.19 et-1.5 eunit-2.2.8 gs-1.5.16 hipe-3.11.1 ic-4.3.6 inets-5.10.3 jinterface-1.5.11 kernel-3.0.3 megaco-3.17.2 mnesia-4.12.3 observer-2.0.2 odbc-2.10.21 orber-3.7.1 os_mon-2.3 ose-1.0.2 otp_mibs-1.0.9 parsetools-2.0.11 percept-0.8.9 public_key-0.22.1 reltool-0.6.6 runtime_tools-1.8.14 sasl-2.4.1 snmp-5.1 ssh-3.0.8 ssl-5.3.7 stdlib-2.2 syntax_tools-1.6.16 test_server-3.7.1 tools-2.7 typer-0.9.8 webtool-0.8.10 wx-1.3.1 xmerl-1.3.7 :
- OTP-17.3.3 : ssh-3.0.8 # asn1-3.0.2 common_test-1.8.2 compiler-5.0.2 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.4.1 debugger-4.0.1 dialyzer-2.7.2 diameter-1.7.1 edoc-0.7.15 eldap-1.0.4 erl_docgen-0.3.6 erl_interface-3.7.19 erts-6.2 et-1.5 eunit-2.2.8 gs-1.5.16 hipe-3.11.1 ic-4.3.6 inets-5.10.3 jinterface-1.5.11 kernel-3.0.3 megaco-3.17.2 mnesia-4.12.3 observer-2.0.2 odbc-2.10.21 orber-3.7.1 os_mon-2.3 ose-1.0.2 otp_mibs-1.0.9 parsetools-2.0.11 percept-0.8.9 public_key-0.22.1 reltool-0.6.6 runtime_tools-1.8.14 sasl-2.4.1 snmp-5.1 ssl-5.3.7 stdlib-2.2 syntax_tools-1.6.16 test_server-3.7.1 tools-2.7 typer-0.9.8 webtool-0.8.10 wx-1.3.1 xmerl-1.3.7 :
diff --git a/lang/erlang/files/patch-otp-17.5.1 b/lang/erlang/files/patch-otp-17.5.1
new file mode 100644
index 00000000000..296b344a47c
--- /dev/null
+++ b/lang/erlang/files/patch-otp-17.5.1
@@ -0,0 +1,826 @@
+diff --git OTP_VERSION OTP_VERSION
+index 6060b96..9cbaf23 100644
+--- OTP_VERSION
++++ OTP_VERSION
+@@ -1 +1 @@
+-17.5
++17.5.1
+diff --git lib/ssh/doc/src/notes.xml lib/ssh/doc/src/notes.xml
+index f22bca3..acbf312 100644
+--- lib/ssh/doc/src/notes.xml
++++ lib/ssh/doc/src/notes.xml
+@@ -29,6 +29,28 @@
+ <file>notes.xml</file>
+ </header>
+
++<section><title>Ssh 3.2.1</title>
++
++ <section><title>Fixed Bugs and Malfunctions</title>
++ <list>
++ <item>
++ <p>
++ Ssh crashed if a message was sent on a channel with
++ packet_size = 0.</p>
++ <p>
++ A new option for ssh:daemon is also introduced:
++ <c>minimal_remote_max_packet_size</c>. This option sets
++ the least max packet size declaration that the daemon
++ will accept from a client. The default value is 0 to
++ maintain compatibility with OpenSSH and the rfc:s.</p>
++ <p>
++ Own Id: OTP-12645 Aux Id: seq12816 </p>
++ </item>
++ </list>
++ </section>
++
++</section>
++
+ <section><title>Ssh 3.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+diff --git lib/ssh/doc/src/ssh.xml lib/ssh/doc/src/ssh.xml
+index d481a75..0e7e384 100644
+--- lib/ssh/doc/src/ssh.xml
++++ lib/ssh/doc/src/ssh.xml
+@@ -338,6 +338,12 @@
+ </warning>
+ </item>
+
++ <tag><c><![CDATA[{minimal_remote_max_packet_size, non_negative_integer()}]]></c></tag>
++ <item>
++ <p>The least maximum packet size that the daemon will accept in channel open requests from the client. The default value is 0.
++ </p>
++ </item>
++
+ <tag><c><![CDATA[{key_cb, atom()}]]></c></tag>
+ <item>
+ <p>Module implementing the behaviour <seealso marker="ssh_server_key_api">ssh_server_key_api</seealso>.
+diff --git lib/ssh/src/ssh.appup.src lib/ssh/src/ssh.appup.src
+index b2b2994..e76c110 100644
+--- lib/ssh/src/ssh.appup.src
++++ lib/ssh/src/ssh.appup.src
+@@ -1,7 +1,7 @@
+ %% -*- erlang -*-
+ %% %CopyrightBegin%
+ %%
+-%% Copyright Ericsson AB 2004-2014. All Rights Reserved.
++%% Copyright Ericsson AB 2004-2015. All Rights Reserved.
+ %%
+ %% The contents of this file are subject to the Erlang Public License,
+ %% Version 1.1, (the "License"); you may not use this file except in
+@@ -19,61 +19,9 @@
+
+ {"%VSN%",
+ [
+- {"3.0.8", [{load_module, ssh_connection, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_sftp, soft_purge, soft_purge, [ssh_xfer]},
+- {load_module, ssh_connection_handler, soft_purge, soft_purge, []},
+- {load_module, ssh, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_xfer, soft_purge, soft_purge, []}
+- ]},
+- {"3.0.7", [{load_module, ssh_auth, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_acceptor, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_channel, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_connection, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_connection_handler, soft_purge, soft_purge, []},
+- {load_module, ssh_info, soft_purge, soft_purge, []},
+- {load_module, ssh_message, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_io, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_sftp, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_xfer, soft_purge, soft_purge, [ssh_connection_handler]}]},
+- {"3.0.6", [{load_module, ssh_auth, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_acceptor, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_channel, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_connection, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_connection_handler, soft_purge, soft_purge, []},
+- {load_module, ssh_info, soft_purge, soft_purge, []},
+- {load_module, ssh_message, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_io, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_sftp, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_xfer, soft_purge, soft_purge, [ssh_connection_handler]}]},
+ {<<".*">>, [{restart_application, ssh}]}
+ ],
+ [
+- {"3.0.8", [{load_module, ssh_connection, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_sftp, soft_purge, soft_purge, []},
+- {load_module, ssh_connection_handler, soft_purge, soft_purge, []},
+- {load_module, ssh, soft_purge, soft_purge, []},
+- {load_module, ssh_xfer, soft_purge, soft_purge, []}
+- ]},
+- {"3.0.7", [{load_module, ssh_auth, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_acceptor, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_channel, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_connection, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_connection_handler, soft_purge, soft_purge, []},
+- {load_module, ssh_info, soft_purge, soft_purge, []},
+- {load_module, ssh_message, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_io, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_sftp, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_xfer, soft_purge, soft_purge, [ssh_connection_handler]}]},
+- {"3.0.6", [{load_module, ssh_auth, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_acceptor, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_channel, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_connection, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_connection_handler, soft_purge, soft_purge, []},
+- {load_module, ssh_info, soft_purge, soft_purge, []},
+- {load_module, ssh_message, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_io, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_sftp, soft_purge, soft_purge, [ssh_connection_handler]},
+- {load_module, ssh_xfer, soft_purge, soft_purge, [ssh_connection_handler]}]},
+ {<<".*">>, [{restart_application, ssh}]}
+ ]
+ }.
+diff --git lib/ssh/src/ssh.erl lib/ssh/src/ssh.erl
+index eae33e3..51ad691 100644
+--- lib/ssh/src/ssh.erl
++++ lib/ssh/src/ssh.erl
+@@ -345,9 +345,14 @@ handle_option([{parallel_login, _} = Opt|Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+ handle_option([parallel_login|Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option({parallel_login,true}) | SshOptions]);
++handle_option([{minimal_remote_max_packet_size, _} = Opt|Rest], SocketOptions, SshOptions) ->
++ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+ handle_option([Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, [handle_inet_option(Opt) | SocketOptions], SshOptions).
+
++
++handle_ssh_option({minimal_remote_max_packet_size, Value} = Opt) when is_integer(Value), Value >=0 ->
++ Opt;
+ handle_ssh_option({system_dir, Value} = Opt) when is_list(Value) ->
+ Opt;
+ handle_ssh_option({user_dir, Value} = Opt) when is_list(Value) ->
+diff --git lib/ssh/src/ssh_acceptor.erl lib/ssh/src/ssh_acceptor.erl
+index 6c443ee..34988f1 100644
+--- lib/ssh/src/ssh_acceptor.erl
++++ lib/ssh/src/ssh_acceptor.erl
+@@ -1,7 +1,7 @@
+ %%
+ %% %CopyrightBegin%
+ %%
+-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
++%% Copyright Ericsson AB 2008-2015. All Rights Reserved.
+ %%
+ %% The contents of this file are subject to the Erlang Public License,
+ %% Version 1.1, (the "License"); you may not use this file except in
+@@ -43,7 +43,7 @@ start_link(Port, Address, SockOpts, Opts, AcceptTimeout) ->
+ acceptor_init(Parent, Port, Address, SockOpts, Opts, AcceptTimeout) ->
+ {_, Callback, _} =
+ proplists:get_value(transport, Opts, {tcp, gen_tcp, tcp_closed}),
+- case (catch do_socket_listen(Callback, Port, SockOpts)) of
++ case (catch do_socket_listen(Callback, Port, [{active, false} | SockOpts])) of
+ {ok, ListenSocket} ->
+ proc_lib:init_ack(Parent, {ok, self()}),
+ acceptor_loop(Callback,
+diff --git lib/ssh/src/ssh_connection.erl lib/ssh/src/ssh_connection.erl
+index c66f810..654b9d4 100644
+--- lib/ssh/src/ssh_connection.erl
++++ lib/ssh/src/ssh_connection.erl
+@@ -1,7 +1,7 @@
+ %%
+ %% %CopyrightBegin%
+ %%
+-%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
++%% Copyright Ericsson AB 2008-2015. All Rights Reserved.
+ %%
+ %% The contents of this file are subject to the Erlang Public License,
+ %% Version 1.1, (the "License"); you may not use this file except in
+@@ -326,9 +326,7 @@ channel_data(ChannelId, DataType, Data,
+ SendDataType,
+ SendData)}
+ end, SendList),
+- FlowCtrlMsgs = flow_control(Replies,
+- Channel,
+- Cache),
++ FlowCtrlMsgs = flow_control(Replies, Channel, Cache),
+ {{replies, Replies ++ FlowCtrlMsgs}, Connection};
+ _ ->
+ gen_fsm:reply(From, {error, closed}),
+@@ -470,18 +468,31 @@ handle_msg(#ssh_msg_channel_window_adjust{recipient_channel = ChannelId,
+ handle_msg(#ssh_msg_channel_open{channel_type = "session" = Type,
+ sender_channel = RemoteId,
+ initial_window_size = WindowSz,
+- maximum_packet_size = PacketSz}, Connection0, server) ->
+-
+- try setup_session(Connection0, RemoteId,
+- Type, WindowSz, PacketSz) of
+- Result ->
+- Result
+- catch _:_ ->
++ maximum_packet_size = PacketSz},
++ #connection{options = SSHopts} = Connection0,
++ server) ->
++ MinAcceptedPackSz = proplists:get_value(minimal_remote_max_packet_size, SSHopts, 0),
++
++ if
++ MinAcceptedPackSz =< PacketSz ->
++ try setup_session(Connection0, RemoteId,
++ Type, WindowSz, PacketSz) of
++ Result ->
++ Result
++ catch _:_ ->
++ FailMsg = channel_open_failure_msg(RemoteId,
++ ?SSH_OPEN_CONNECT_FAILED,
++ "Connection refused", "en"),
++ {{replies, [{connection_reply, FailMsg}]},
++ Connection0}
++ end;
++
++ MinAcceptedPackSz > PacketSz ->
+ FailMsg = channel_open_failure_msg(RemoteId,
+- ?SSH_OPEN_CONNECT_FAILED,
+- "Connection refused", "en"),
+- {{replies, [{connection_reply, FailMsg}]},
+- Connection0}
++ ?SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
++ lists:concat(["Maximum packet size below ",MinAcceptedPackSz,
++ " not supported"]), "en"),
++ {{replies, [{connection_reply, FailMsg}]}, Connection0}
+ end;
+
+ handle_msg(#ssh_msg_channel_open{channel_type = "session",
+@@ -501,41 +512,57 @@ handle_msg(#ssh_msg_channel_open{channel_type = "forwarded-tcpip" = Type,
+ initial_window_size = RWindowSz,
+ maximum_packet_size = RPacketSz,
+ data = Data},
+- #connection{channel_cache = Cache} = Connection0, server) ->
++ #connection{channel_cache = Cache,
++ options = SSHopts} = Connection0, server) ->
+ <<?UINT32(ALen), Address:ALen/binary, ?UINT32(Port),
+ ?UINT32(OLen), Orig:OLen/binary, ?UINT32(OrigPort)>> = Data,
+
+- case bound_channel(Address, Port, Connection0) of
+- undefined ->
++ MinAcceptedPackSz = proplists:get_value(minimal_remote_max_packet_size, SSHopts, 0),
++
++ if
++ MinAcceptedPackSz =< RPacketSz ->
++ case bound_channel(Address, Port, Connection0) of
++ undefined ->
++ FailMsg = channel_open_failure_msg(RemoteId,
++ ?SSH_OPEN_CONNECT_FAILED,
++ "Connection refused", "en"),
++ {{replies,
++ [{connection_reply, FailMsg}]}, Connection0};
++ ChannelPid ->
++ {ChannelId, Connection1} = new_channel_id(Connection0),
++ LWindowSz = ?DEFAULT_WINDOW_SIZE,
++ LPacketSz = ?DEFAULT_PACKET_SIZE,
++ Channel = #channel{type = Type,
++ sys = "none",
++ user = ChannelPid,
++ local_id = ChannelId,
++ recv_window_size = LWindowSz,
++ recv_packet_size = LPacketSz,
++ send_window_size = RWindowSz,
++ send_packet_size = RPacketSz,
++ send_buf = queue:new()
++ },
++ ssh_channel:cache_update(Cache, Channel),
++ OpenConfMsg = channel_open_confirmation_msg(RemoteId, ChannelId,
++ LWindowSz, LPacketSz),
++ {OpenMsg, Connection} =
++ reply_msg(Channel, Connection1,
++ {open, Channel, {forwarded_tcpip,
++ decode_ip(Address), Port,
++ decode_ip(Orig), OrigPort}}),
++ {{replies, [{connection_reply, OpenConfMsg},
++ OpenMsg]}, Connection}
++ end;
++
++ MinAcceptedPackSz > RPacketSz ->
+ FailMsg = channel_open_failure_msg(RemoteId,
+- ?SSH_OPEN_CONNECT_FAILED,
+- "Connection refused", "en"),
+- {{replies,
+- [{connection_reply, FailMsg}]}, Connection0};
+- ChannelPid ->
+- {ChannelId, Connection1} = new_channel_id(Connection0),
+- LWindowSz = ?DEFAULT_WINDOW_SIZE,
+- LPacketSz = ?DEFAULT_PACKET_SIZE,
+- Channel = #channel{type = Type,
+- sys = "none",
+- user = ChannelPid,
+- local_id = ChannelId,
+- recv_window_size = LWindowSz,
+- recv_packet_size = LPacketSz,
+- send_window_size = RWindowSz,
+- send_packet_size = RPacketSz},
+- ssh_channel:cache_update(Cache, Channel),
+- OpenConfMsg = channel_open_confirmation_msg(RemoteId, ChannelId,
+- LWindowSz, LPacketSz),
+- {OpenMsg, Connection} =
+- reply_msg(Channel, Connection1,
+- {open, Channel, {forwarded_tcpip,
+- decode_ip(Address), Port,
+- decode_ip(Orig), OrigPort}}),
+- {{replies, [{connection_reply, OpenConfMsg},
+- OpenMsg]}, Connection}
++ ?SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
++ lists:concat(["Maximum packet size below ",MinAcceptedPackSz,
++ " not supported"]), "en"),
++ {{replies, [{connection_reply, FailMsg}]}, Connection0}
+ end;
+
++
+ handle_msg(#ssh_msg_channel_open{channel_type = "forwarded-tcpip",
+ sender_channel = RemoteId},
+ Connection, client) ->
+@@ -917,7 +944,8 @@ start_channel(Cb, Id, Args, SubSysSup, Exec) ->
+ %%--------------------------------------------------------------------
+ %%% Internal functions
+ %%--------------------------------------------------------------------
+-setup_session(#connection{channel_cache = Cache} = Connection0,
++setup_session(#connection{channel_cache = Cache
++ } = Connection0,
+ RemoteId,
+ Type, WindowSize, PacketSize) ->
+ {ChannelId, Connection} = new_channel_id(Connection0),
+@@ -929,6 +957,7 @@ setup_session(#connection{channel_cache = Cache} = Connection0,
+ recv_packet_size = ?DEFAULT_PACKET_SIZE,
+ send_window_size = WindowSize,
+ send_packet_size = PacketSize,
++ send_buf = queue:new(),
+ remote_id = RemoteId
+ },
+ ssh_channel:cache_update(Cache, Channel),
+@@ -1024,63 +1053,74 @@ request_reply_or_data(#channel{local_id = ChannelId, user = ChannelPid},
+
+ update_send_window(Channel, _, undefined,
+ #connection{channel_cache = Cache}) ->
+- do_update_send_window(Channel, Channel#channel.send_buf, Cache);
++ do_update_send_window(Channel, Cache);
+
+-update_send_window(Channel, DataType, Data,
++update_send_window(#channel{send_buf = SendBuffer} = Channel, DataType, Data,
+ #connection{channel_cache = Cache}) ->
+- do_update_send_window(Channel, Channel#channel.send_buf ++ [{DataType, Data}], Cache).
++ do_update_send_window(Channel#channel{send_buf = queue:in({DataType, Data}, SendBuffer)},
++ Cache).
+
+-do_update_send_window(Channel0, Buf0, Cache) ->
+- {Buf1, NewSz, Buf2} = get_window(Buf0,
+- Channel0#channel.send_packet_size,
+- Channel0#channel.send_window_size),
+-
+- Channel = Channel0#channel{send_window_size = NewSz, send_buf = Buf2},
++do_update_send_window(Channel0, Cache) ->
++ {SendMsgs, Channel} = get_window(Channel0, []),
+ ssh_channel:cache_update(Cache, Channel),
+- {Buf1, Channel}.
++ {SendMsgs, Channel}.
+
+-get_window(Bs, PSz, WSz) ->
+- get_window(Bs, PSz, WSz, []).
+-
+-get_window(Bs, _PSz, 0, Acc) ->
+- {lists:reverse(Acc), 0, Bs};
+-get_window([B0 = {DataType, Bin} | Bs], PSz, WSz, Acc) ->
+- BSz = size(Bin),
+- if BSz =< WSz -> %% will fit into window
+- if BSz =< PSz -> %% will fit into a packet
+- get_window(Bs, PSz, WSz-BSz, [B0|Acc]);
+- true -> %% split into packet size
+- <<Bin1:PSz/binary, Bin2/binary>> = Bin,
+- get_window([setelement(2, B0, Bin2) | Bs],
+- PSz, WSz-PSz,
+- [{DataType, Bin1}|Acc])
++get_window(#channel{send_window_size = 0
++ } = Channel, Acc) ->
++ {lists:reverse(Acc), Channel};
++get_window(#channel{send_packet_size = 0
++ } = Channel, Acc) ->
++ {lists:reverse(Acc), Channel};
++get_window(#channel{send_buf = Buffer,
++ send_packet_size = PacketSize,
++ send_window_size = WindowSize0
++ } = Channel, Acc0) ->
++ case queue:out(Buffer) of
++ {{value, {_, Data} = Msg}, NewBuffer} ->
++ case handle_send_window(Msg, size(Data), PacketSize, WindowSize0, Acc0) of
++ {WindowSize, Acc, {_, <<>>}} ->
++ {lists:reverse(Acc), Channel#channel{send_window_size = WindowSize,
++ send_buf = NewBuffer}};
++ {WindowSize, Acc, Rest} ->
++ get_window(Channel#channel{send_window_size = WindowSize,
++ send_buf = queue:in_r(Rest, NewBuffer)}, Acc)
+ end;
+- WSz =< PSz -> %% use rest of window
+- <<Bin1:WSz/binary, Bin2/binary>> = Bin,
+- get_window([setelement(2, B0, Bin2) | Bs],
+- PSz, WSz-WSz,
+- [{DataType, Bin1}|Acc]);
+- true -> %% use packet size
+- <<Bin1:PSz/binary, Bin2/binary>> = Bin,
+- get_window([setelement(2, B0, Bin2) | Bs],
+- PSz, WSz-PSz,
+- [{DataType, Bin1}|Acc])
++ {empty, NewBuffer} ->
++ {[], Channel#channel{send_buf = NewBuffer}}
++ end.
++
++handle_send_window(Msg = {Type, Data}, Size, PacketSize, WindowSize, Acc) when Size =< WindowSize ->
++ case Size =< PacketSize of
++ true ->
++ {WindowSize - Size, [Msg | Acc], {Type, <<>>}};
++ false ->
++ <<Msg1:PacketSize/binary, Msg2/binary>> = Data,
++ {WindowSize - PacketSize, [{Type, Msg1} | Acc], {Type, Msg2}}
+ end;
+-get_window([], _PSz, WSz, Acc) ->
+- {lists:reverse(Acc), WSz, []}.
++handle_send_window({Type, Data}, _, PacketSize, WindowSize, Acc) when WindowSize =< PacketSize ->
++ <<Msg1:WindowSize/binary, Msg2/binary>> = Data,
++ {WindowSize - WindowSize, [{Type, Msg1} | Acc], {Type, Msg2}};
++handle_send_window({Type, Data}, _, PacketSize, WindowSize, Acc) ->
++ <<Msg1:PacketSize/binary, Msg2/binary>> = Data,
++ {WindowSize - PacketSize, [{Type, Msg1} | Acc], {Type, Msg2}}.
+
+ flow_control(Channel, Cache) ->
+ flow_control([window_adjusted], Channel, Cache).
+-
++
+ flow_control([], Channel, Cache) ->
+ ssh_channel:cache_update(Cache, Channel),
+ [];
+-
+ flow_control([_|_], #channel{flow_control = From,
+- send_buf = []} = Channel, Cache) when From =/= undefined ->
+- [{flow_control, Cache, Channel, From, ok}];
++ send_buf = Buffer} = Channel, Cache) when From =/= undefined ->
++ case queue:is_empty(Buffer) of
++ true ->
++ ssh_channel:cache_update(Cache, Channel#channel{flow_control = undefined}),
++ [{flow_control, Cache, Channel, From, ok}];
++ false ->
++ []
++ end;
+ flow_control(_,_,_) ->
+- [].
++ [].
+
+ pty_req(ConnectionHandler, Channel, Term, Width, Height,
+ PixWidth, PixHeight, PtyOpts, TimeOut) ->
+diff --git lib/ssh/src/ssh_connection_handler.erl lib/ssh/src/ssh_connection_handler.erl
+index 68523aa..e1f2e05 100644
+--- lib/ssh/src/ssh_connection_handler.erl
++++ lib/ssh/src/ssh_connection_handler.erl
+@@ -1,7 +1,7 @@
+ %%
+ %% %CopyrightBegin%
+ %%
+-%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
++%% Copyright Ericsson AB 2008-2015. All Rights Reserved.
+ %%
+ %% The contents of this file are subject to the Erlang Public License,
+ %% Version 1.1, (the "License"); you may not use this file except in
+@@ -751,7 +751,9 @@ handle_sync_event({open, ChannelPid, Type, InitialWindowSize, MaxPacketSize, Dat
+ user = ChannelPid,
+ local_id = ChannelId,
+ recv_window_size = InitialWindowSize,
+- recv_packet_size = MaxPacketSize},
++ recv_packet_size = MaxPacketSize,
++ send_buf = queue:new()
++ },
+ ssh_channel:cache_update(Cache, Channel),
+ State = add_request(true, ChannelId, From, State2),
+ start_timeout(ChannelId, From, Timeout),
+@@ -1241,10 +1243,9 @@ event(Event, StateName, State) ->
+ handle_disconnect(DisconnectMsg, State);
+ throw:{ErrorToDisplay, #ssh_msg_disconnect{} = DisconnectMsg} ->
+ handle_disconnect(DisconnectMsg, State, ErrorToDisplay);
+- _:Error ->
+- log_error(Error),
++ _:_ ->
+ handle_disconnect(#ssh_msg_disconnect{code = error_code(StateName),
+- description = "Internal error",
++ description = "Invalid state",
+ language = "en"}, State)
+ end.
+ error_code(key_exchange) ->
+diff --git lib/ssh/src/ssh_info.erl lib/ssh/src/ssh_info.erl
+index 9a91875..30df32c 100644
+--- lib/ssh/src/ssh_info.erl
++++ lib/ssh/src/ssh_info.erl
+@@ -27,18 +27,21 @@
+ -compile(export_all).
+
+ print() ->
++ print(user).
++
++print(D) ->
+ try supervisor:which_children(ssh_sup)
+ of
+ _ ->
+- io:nl(),
+- print_general(),
+- io:nl(),
+- underline("Client part", $=),
+- print_clients(),
+- io:nl(),
+- underline("Server part", $=),
+- print_servers(),
+- io:nl(),
++ io:nl(D),
++ print_general(D),
++ io:nl(D),
++ underline(D, "Client part", $=),
++ print_clients(D),
++ io:nl(D),
++ underline(D, "Server part", $=),
++ print_servers(D),
++ io:nl(D),
+ %% case os:type() of
+ %% {unix,_} ->
+ %% io:nl(),
+@@ -50,90 +53,95 @@ print() ->
+ %% catch io:format(os:cmd("netstat -tpn"));
+ %% _ -> ok
+ %% end,
+- underline("Supervisors", $=),
+- walk_sups(ssh_sup),
+- io:nl()
++ underline(D, "Supervisors", $=),
++ walk_sups(D, ssh_sup),
++ io:nl(D)
+ catch
+ _:_ ->
+- io:format("Ssh not found~n",[])
++ io:format(D,"Ssh not found~n",[])
+ end.
+
+ %%%================================================================
+-print_general() ->
++print_general(D) ->
+ {_Name, Slogan, Ver} = lists:keyfind(ssh,1,application:which_applications()),
+- underline(io_lib:format("~s ~s", [Slogan, Ver]), $=),
+- io:format('This printout is generated ~s. ~n',[datetime()]).
++ underline(D, io_lib:format("~s ~s", [Slogan, Ver]), $=),
++ io:format(D, 'This printout is generated ~s. ~n',[datetime()]).
+
+ %%%================================================================
+-print_clients() ->
++print_clients(D) ->
++ PrintClient = fun(X) -> print_client(D,X) end,
+ try
+- lists:foreach(fun print_client/1, supervisor:which_children(sshc_sup))
++ lists:foreach(PrintClient, supervisor:which_children(sshc_sup))
+ catch
+ C:E ->
+- io:format('***FAILED: ~p:~p~n',[C,E])
++ io:format(D, '***FAILED: ~p:~p~n',[C,E])
+ end.
+
+-print_client({undefined,Pid,supervisor,[ssh_connection_handler]}) ->
++print_client(D, {undefined,Pid,supervisor,[ssh_connection_handler]}) ->
+ {{Local,Remote},_Str} = ssh_connection_handler:get_print_info(Pid),
+- io:format(" Local=~s Remote=~s~n",[fmt_host_port(Local),fmt_host_port(Remote)]);
+-print_client(Other) ->
+- io:format(" [[Other 1: ~p]]~n",[Other]).
++ io:format(D, " Local=~s Remote=~s~n",[fmt_host_port(Local),fmt_host_port(Remote)]);
++print_client(D, Other) ->
++ io:format(D, " [[Other 1: ~p]]~n",[Other]).
+
+
+ %%%================================================================
+-print_servers() ->
++print_servers(D) ->
++ PrintServer = fun(X) -> print_server(D,X) end,
+ try
+- lists:foreach(fun print_server/1, supervisor:which_children(sshd_sup))
++ lists:foreach(PrintServer, supervisor:which_children(sshd_sup))
+ catch
+ C:E ->
+- io:format('***FAILED: ~p:~p~n',[C,E])
++ io:format(D, '***FAILED: ~p:~p~n',[C,E])
+ end.
+
+-print_server({{server,ssh_system_sup,LocalHost,LocalPort},Pid,supervisor,[ssh_system_sup]}) when is_pid(Pid) ->
+- io:format('Local=~s (~p children)~n',[fmt_host_port({LocalHost,LocalPort}),
+- ssh_acceptor:number_of_connections(Pid)]),
+- lists:foreach(fun print_system_sup/1, supervisor:which_children(Pid));
+-print_server(Other) ->
+- io:format(" [[Other 2: ~p]]~n",[Other]).
++print_server(D, {{server,ssh_system_sup,LocalHost,LocalPort},Pid,supervisor,[ssh_system_sup]}) when is_pid(Pid) ->
++ io:format(D, 'Local=~s (~p children)~n',[fmt_host_port({LocalHost,LocalPort}),
++ ssh_acceptor:number_of_connections(Pid)]),
++ PrintSystemSup = fun(X) -> print_system_sup(D,X) end,
++ lists:foreach(PrintSystemSup, supervisor:which_children(Pid));
++print_server(D, Other) ->
++ io:format(D, " [[Other 2: ~p]]~n",[Other]).
+
+-print_system_sup({Ref,Pid,supervisor,[ssh_subsystem_sup]}) when is_reference(Ref),
++print_system_sup(D, {Ref,Pid,supervisor,[ssh_subsystem_sup]}) when is_reference(Ref),
+ is_pid(Pid) ->
+- lists:foreach(fun print_channels/1, supervisor:which_children(Pid));
+-print_system_sup({{ssh_acceptor_sup,LocalHost,LocalPort}, Pid,supervisor, [ssh_acceptor_sup]}) when is_pid(Pid) ->
+- io:format(" [Acceptor for ~s]~n",[fmt_host_port({LocalHost,LocalPort})]);
+-print_system_sup(Other) ->
+- io:format(" [[Other 3: ~p]]~n",[Other]).
++ PrintChannels = fun(X) -> print_channels(D,X) end,
++ lists:foreach(PrintChannels, supervisor:which_children(Pid));
++print_system_sup(D, {{ssh_acceptor_sup,LocalHost,LocalPort}, Pid,supervisor, [ssh_acceptor_sup]}) when is_pid(Pid) ->
++ io:format(D, " [Acceptor for ~s]~n",[fmt_host_port({LocalHost,LocalPort})]);
++print_system_sup(D, Other) ->
++ io:format(D, " [[Other 3: ~p]]~n",[Other]).
+
+-print_channels({{server,ssh_channel_sup,_,_},Pid,supervisor,[ssh_channel_sup]}) when is_pid(Pid) ->
+- lists:foreach(fun print_channel/1, supervisor:which_children(Pid));
+-print_channels(Other) ->
+- io:format(" [[Other 4: ~p]]~n",[Other]).
++print_channels(D, {{server,ssh_channel_sup,_,_},Pid,supervisor,[ssh_channel_sup]}) when is_pid(Pid) ->
++ PrintChannel = fun(X) -> print_channel(D,X) end,
++ lists:foreach(PrintChannel, supervisor:which_children(Pid));
++print_channels(D, Other) ->
++ io:format(D, " [[Other 4: ~p]]~n",[Other]).
+
+
+-print_channel({Ref,Pid,worker,[ssh_channel]}) when is_reference(Ref),
+- is_pid(Pid) ->
++print_channel(D, {Ref,Pid,worker,[ssh_channel]}) when is_reference(Ref),
++ is_pid(Pid) ->
+ {{ConnManager,ChannelID}, Str} = ssh_channel:get_print_info(Pid),
+ {{Local,Remote},StrM} = ssh_connection_handler:get_print_info(ConnManager),
+- io:format(' ch ~p: ~s ~s',[ChannelID, StrM, Str]),
+- io:format(" Local=~s Remote=~s~n",[fmt_host_port(Local),fmt_host_port(Remote)]);
+-print_channel(Other) ->
+- io:format(" [[Other 5: ~p]]~n",[Other]).
++ io:format(D, ' ch ~p: ~s ~s',[ChannelID, StrM, Str]),
++ io:format(D, " Local=~s Remote=~s~n",[fmt_host_port(Local),fmt_host_port(Remote)]);
++print_channel(D, Other) ->
++ io:format(D, " [[Other 5: ~p]]~n",[Other]).
+
+ %%%================================================================
+ -define(inc(N), (N+4)).
+
+-walk_sups(StartPid) ->
+- io:format("Start at ~p, ~s.~n",[StartPid,dead_or_alive(StartPid)]),
+- walk_sups(children(StartPid), _Indent=?inc(0)).
++walk_sups(D, StartPid) ->
++ io:format(D, "Start at ~p, ~s.~n",[StartPid,dead_or_alive(StartPid)]),
++ walk_sups(D, children(StartPid), _Indent=?inc(0)).
+
+-walk_sups([H={_,Pid,SupOrWorker,_}|T], Indent) ->
+- indent(Indent), io:format('~200p ~p is ~s~n',[H,Pid,dead_or_alive(Pid)]),
++walk_sups(D, [H={_,Pid,SupOrWorker,_}|T], Indent) ->
++ indent(D, Indent), io:format(D, '~200p ~p is ~s~n',[H,Pid,dead_or_alive(Pid)]),
+ case SupOrWorker of
+- supervisor -> walk_sups(children(Pid), ?inc(Indent));
++ supervisor -> walk_sups(D, children(Pid), ?inc(Indent));
+ _ -> ok
+ end,
+- walk_sups(T, Indent);
+-walk_sups([], _) ->
++ walk_sups(D, T, Indent);
++walk_sups(_D, [], _) ->
+ ok.
+
+ dead_or_alive(Name) when is_atom(Name) ->
+@@ -149,7 +157,7 @@ dead_or_alive(Pid) when is_pid(Pid) ->
+ _ -> "alive"
+ end.
+
+-indent(I) -> io:format('~*c',[I,$ ]).
++indent(D, I) -> io:format(D,'~*c',[I,$ ]).
+
+ children(Pid) ->
+ Parent = self(),
+@@ -166,16 +174,16 @@ children(Pid) ->
+ end.
+
+ %%%================================================================
+-underline(Str) ->
+- underline(Str, $-).
++underline(D, Str) ->
++ underline(D, Str, $-).
+
+-underline(Str, LineChar) ->
++underline(D, Str, LineChar) ->
+ Len = lists:flatlength(Str),
+- io:format('~s~n',[Str]),
+- line(Len,LineChar).
++ io:format(D, '~s~n',[Str]),
++ line(D,Len,LineChar).
+
+-line(Len, Char) ->
+- io:format('~*c~n', [Len,Char]).
++line(D, Len, Char) ->
++ io:format(D, '~*c~n', [Len,Char]).
+
+
+ datetime() ->
+@@ -188,6 +196,6 @@ fmt_host_port({Host,Port}) -> io_lib:format('~s:~p',[Host,Port]).
+
+
+
+-nyi() ->
+- io:format('Not yet implemented~n',[]),
++nyi(D) ->
++ io:format(D,'Not yet implemented~n',[]),
+ nyi.
+diff --git lib/ssh/test/ssh_basic_SUITE.erl lib/ssh/test/ssh_basic_SUITE.erl
+index 45c0303..81c7b5c 100644
+--- lib/ssh/test/ssh_basic_SUITE.erl
++++ lib/ssh/test/ssh_basic_SUITE.erl
+@@ -50,6 +50,8 @@ all() ->
+ double_close,
+ ssh_connect_timeout,
+ ssh_connect_arg4_timeout,
++ packet_size_zero,
++ ssh_daemon_minimal_remote_max_packet_size_option,
+ {group, hardening_tests}
+ ].
+
+@@ -757,6 +759,64 @@ ms_passed(N1={_,_,M1}, N2={_,_,M2}) ->
+ 1000 * (Min*60 + Sec + (M2-M1)/1000000).
+
+ %%--------------------------------------------------------------------
++packet_size_zero(Config) ->
++ SystemDir = ?config(data_dir, Config),
++ PrivDir = ?config(priv_dir, Config),
++ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
++ file:make_dir(UserDir),
++
++ {Server, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
++ {user_dir, UserDir},
++ {user_passwords, [{"vego", "morot"}]}]),
++ Conn =
++ ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
++ {user_dir, UserDir},
++ {user_interaction, false},
++ {user, "vego"},
++ {password, "morot"}]),
++
++ {ok,Chan} = ssh_connection:session_channel(Conn, 1000, _MaxPacketSize=0, 60000),
++ ok = ssh_connection:shell(Conn, Chan),
++
++ ssh:close(Conn),
++ ssh:stop_daemon(Server),
++
++ receive
++ {ssh_cm,Conn,{data,Chan,_Type,_Msg1}} = M ->
++ ct:pal("Got ~p",[M]),
++ ct:fail(doesnt_obey_max_packet_size_0)
++ after 5000 ->
++ ok
++ end.
++
++%%--------------------------------------------------------------------
++ssh_daemon_minimal_remote_max_packet_size_option(Config) ->
++ SystemDir = ?config(data_dir, Config),
++ PrivDir = ?config(priv_dir, Config),
++ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
++ file:make_dir(UserDir),
++
++ {Server, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
++ {user_dir, UserDir},
++ {user_passwords, [{"vego", "morot"}]},
++ {failfun, fun ssh_test_lib:failfun/2},
++ {minimal_remote_max_packet_size, 14}]),
++ Conn =
++ ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
++ {user_dir, UserDir},
++ {user_interaction, false},
++ {user, "vego"},
++ {password, "morot"}]),
++
++ %% Try the limits of the minimal_remote_max_packet_size:
++ {ok, _ChannelId} = ssh_connection:session_channel(Conn, 100, 14, infinity),
++ {open_error,_,"Maximum packet size below 14 not supported",_} =
++ ssh_connection:session_channel(Conn, 100, 13, infinity),
++
++ ssh:close(Conn),
++ ssh:stop_daemon(Server).
++
++%%--------------------------------------------------------------------
+ ssh_connect_negtimeout_parallel(Config) -> ssh_connect_negtimeout(Config,true).
+ ssh_connect_negtimeout_sequential(Config) -> ssh_connect_negtimeout(Config,false).
+
+@@ -970,7 +1030,7 @@ max_sessions(Config, ParallelLogin, Connect0) when is_function(Connect0,2) ->
+
+ %% Due to timing the error message may or may not be delivered to
+ %% the "tcp-application" before the socket closed message is recived
+-check_error("Internal error") ->
++check_error("Invalid state") ->
+ ok;
+ check_error("Connection closed") ->
+ ok;
+diff --git lib/ssh/vsn.mk lib/ssh/vsn.mk
+index 0d90278..fec8dac 100644
+--- lib/ssh/vsn.mk
++++ lib/ssh/vsn.mk
+@@ -1,5 +1,4 @@
+ #-*-makefile-*- ; force emacs to enter makefile-mode
+
+-SSH_VSN = 3.2
++SSH_VSN = 3.2.1
+ APP_VSN = "ssh-$(SSH_VSN)"
+-
+diff --git otp_versions.table otp_versions.table
+index 64ffd82..a82f535 100644
+--- otp_versions.table
++++ otp_versions.table
+@@ -1,3 +1,4 @@
++OTP-17.5.1 : ssh-3.2.1 # asn1-3.0.4 common_test-1.10 compiler-5.0.4 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.5 debugger-4.0.3 dialyzer-2.7.4 diameter-1.9 edoc-0.7.16 eldap-1.1.1 erl_docgen-0.3.7 erl_interface-3.7.20 erts-6.4 et-1.5 eunit-2.2.9 gs-1.5.16 hipe-3.11.3 ic-4.3.6 inets-5.10.6 jinterface-1.5.12 kernel-3.2 megaco-3.17.3 mnesia-4.12.5 observer-2.0.4 odbc-2.10.22 orber-3.7.1 os_mon-2.3.1 ose-1.0.2 otp_mibs-1.0.10 parsetools-2.0.12 percept-0.8.10 public_key-0.23 reltool-0.6.6 runtime_tools-1.8.16 sasl-2.4.1 snmp-5.1.1 ssl-6.0 stdlib-2.4 syntax_tools-1.6.18 test_server-3.8 tools-2.7.2 typer-0.9.8 webtool-0.8.10 wx-1.3.3 xmerl-1.3.7 :
+ OTP-17.5 : asn1-3.0.4 common_test-1.10 compiler-5.0.4 crypto-3.5 debugger-4.0.3 dialyzer-2.7.4 diameter-1.9 eldap-1.1.1 erts-6.4 hipe-3.11.3 inets-5.10.6 kernel-3.2 mnesia-4.12.5 observer-2.0.4 os_mon-2.3.1 public_key-0.23 runtime_tools-1.8.16 ssh-3.2 ssl-6.0 stdlib-2.4 syntax_tools-1.6.18 test_server-3.8 tools-2.7.2 wx-1.3.3 # cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 edoc-0.7.16 erl_docgen-0.3.7 erl_interface-3.7.20 et-1.5 eunit-2.2.9 gs-1.5.16 ic-4.3.6 jinterface-1.5.12 megaco-3.17.3 odbc-2.10.22 orber-3.7.1 ose-1.0.2 otp_mibs-1.0.10 parsetools-2.0.12 percept-0.8.10 reltool-0.6.6 sasl-2.4.1 snmp-5.1.1 typer-0.9.8 webtool-0.8.10 xmerl-1.3.7 :
+ OTP-17.4.1 : erts-6.3.1 inets-5.10.5 # asn1-3.0.3 common_test-1.9 compiler-5.0.3 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.4.2 debugger-4.0.2 dialyzer-2.7.3 diameter-1.8 edoc-0.7.16 eldap-1.1 erl_docgen-0.3.7 erl_interface-3.7.20 et-1.5 eunit-2.2.9 gs-1.5.16 hipe-3.11.2 ic-4.3.6 jinterface-1.5.12 kernel-3.1 megaco-3.17.3 mnesia-4.12.4 observer-2.0.3 odbc-2.10.22 orber-3.7.1 os_mon-2.3 ose-1.0.2 otp_mibs-1.0.10 parsetools-2.0.12 percept-0.8.10 public_key-0.22.1 reltool-0.6.6 runtime_tools-1.8.15 sasl-2.4.1 snmp-5.1.1 ssh-3.1 ssl-5.3.8 stdlib-2.3 syntax_tools-1.6.17 test_server-3.7.2 tools-2.7.1 typer-0.9.8 webtool-0.8.10 wx-1.3.2 xmerl-1.3.7 :
+ OTP-17.4 : asn1-3.0.3 common_test-1.9 compiler-5.0.3 crypto-3.4.2 debugger-4.0.2 dialyzer-2.7.3 diameter-1.8 edoc-0.7.16 eldap-1.1 erl_docgen-0.3.7 erl_interface-3.7.20 erts-6.3 eunit-2.2.9 hipe-3.11.2 inets-5.10.4 jinterface-1.5.12 kernel-3.1 megaco-3.17.3 mnesia-4.12.4 observer-2.0.3 odbc-2.10.22 otp_mibs-1.0.10 parsetools-2.0.12 percept-0.8.10 runtime_tools-1.8.15 snmp-5.1.1 ssh-3.1 ssl-5.3.8 stdlib-2.3 syntax_tools-1.6.17 test_server-3.7.2 tools-2.7.1 wx-1.3.2 # cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 et-1.5 gs-1.5.16 ic-4.3.6 orber-3.7.1 os_mon-2.3 ose-1.0.2 public_key-0.22.1 reltool-0.6.6 sasl-2.4.1 typer-0.9.8 webtool-0.8.10 xmerl-1.3.7 :