- Sort OPTIONS_DEFINE and OPTIONS_DEFAULT. - Create SKIP files for applications disabled by port options. - Remove disabled applications from ${STAGEDIR}. - Check if dtraceall is actually loaded when DTRACE is enabled.
1358 lines
52 KiB
Groff
1358 lines
52 KiB
Groff
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 :
|