aboutsummaryrefslogtreecommitdiff
path: root/packages/bun-usockets/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/bun-usockets/src')
-rw-r--r--packages/bun-usockets/src/bsd.c738
-rw-r--r--packages/bun-usockets/src/context.c575
-rw-r--r--packages/bun-usockets/src/crypto/openssl.c1732
-rw-r--r--packages/bun-usockets/src/crypto/root_certs.h3438
-rw-r--r--packages/bun-usockets/src/crypto/sni_tree.cpp218
-rw-r--r--packages/bun-usockets/src/eventing/epoll_kqueue.c568
-rw-r--r--packages/bun-usockets/src/internal/eventing/epoll_kqueue.h72
-rw-r--r--packages/bun-usockets/src/internal/internal.h245
-rw-r--r--packages/bun-usockets/src/internal/loop_data.h38
-rw-r--r--packages/bun-usockets/src/internal/networking/bsd.h108
-rw-r--r--packages/bun-usockets/src/libusockets.h408
-rw-r--r--packages/bun-usockets/src/loop.c364
-rw-r--r--packages/bun-usockets/src/quic.c1071
-rw-r--r--packages/bun-usockets/src/quic.h68
-rw-r--r--packages/bun-usockets/src/socket.c295
-rw-r--r--packages/bun-usockets/src/udp.c147
16 files changed, 10085 insertions, 0 deletions
diff --git a/packages/bun-usockets/src/bsd.c b/packages/bun-usockets/src/bsd.c
new file mode 100644
index 000000000..7683acd7d
--- /dev/null
+++ b/packages/bun-usockets/src/bsd.c
@@ -0,0 +1,738 @@
+/*
+ * Authored by Alex Hultman, 2018-2021.
+ * Intellectual property of third-party.
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Todo: this file should lie in networking/bsd.c */
+
+#define __APPLE_USE_RFC_3542
+
+#include "libusockets.h"
+#include "internal/internal.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef _WIN32
+//#define _GNU_SOURCE
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#endif
+
+/* Internal structure of packet buffer */
+struct us_internal_udp_packet_buffer {
+#if defined(_WIN32) || defined(__APPLE__)
+ char *buf[LIBUS_UDP_MAX_NUM];
+ size_t len[LIBUS_UDP_MAX_NUM];
+ struct sockaddr_storage addr[LIBUS_UDP_MAX_NUM];
+#else
+ struct mmsghdr msgvec[LIBUS_UDP_MAX_NUM];
+ struct iovec iov[LIBUS_UDP_MAX_NUM];
+ struct sockaddr_storage addr[LIBUS_UDP_MAX_NUM];
+ char control[LIBUS_UDP_MAX_NUM][256];
+#endif
+};
+
+/* We need to emulate sendmmsg, recvmmsg on platform who don't have it */
+int bsd_sendmmsg(LIBUS_SOCKET_DESCRIPTOR fd, void *msgvec, unsigned int vlen, int flags) {
+#if defined(__APPLE__)
+
+struct mmsghdr {
+ struct msghdr msg_hdr; /* Message header */
+ unsigned int msg_len; /* Number of bytes transmitted */
+};
+
+ struct mmsghdr *hdrs = (struct mmsghdr *) msgvec;
+
+ for (int i = 0; i < vlen; i++) {
+ int ret = sendmsg(fd, &hdrs[i].msg_hdr, flags);
+ if (ret == -1) {
+ if (i) {
+ return i;
+ } else {
+ return -1;
+ }
+ } else {
+ hdrs[i].msg_len = ret;
+ }
+ }
+
+ return vlen;
+
+#elif defined(_WIN32)
+
+ struct us_internal_udp_packet_buffer *packet_buffer = (struct us_internal_udp_packet_buffer *) msgvec;
+
+ /* Let's just use sendto here */
+ /* Winsock does not have sendmsg, while macOS has, however, we simply use sendto since both macOS and Winsock has it.
+ * Besides, you should use Linux either way to get best performance with the sendmmsg */
+
+
+ // while we do not get error, send next
+
+ for (int i = 0; i < LIBUS_UDP_MAX_NUM; i++) {
+ // need to support ipv6 addresses also!
+ int ret = sendto(fd, packet_buffer->buf[i], packet_buffer->len[i], flags, (struct sockaddr *)&packet_buffer->addr[i], sizeof(struct sockaddr_in));
+
+ if (ret == -1) {
+ // if we fail then we need to buffer up, no that's not our problem
+ // we do need to register poll out though and have a callback for it
+ return i;
+ }
+
+ //printf("sendto: %d\n", ret);
+ }
+
+ return LIBUS_UDP_MAX_NUM; // one message
+#else
+ return sendmmsg(fd, (struct mmsghdr *)msgvec, vlen, flags | MSG_NOSIGNAL);
+#endif
+}
+
+int bsd_recvmmsg(LIBUS_SOCKET_DESCRIPTOR fd, void *msgvec, unsigned int vlen, int flags, void *timeout) {
+#if defined(_WIN32) || defined(__APPLE__)
+ struct us_internal_udp_packet_buffer *packet_buffer = (struct us_internal_udp_packet_buffer *) msgvec;
+
+
+ for (int i = 0; i < LIBUS_UDP_MAX_NUM; i++) {
+ socklen_t addr_len = sizeof(struct sockaddr_storage);
+ int ret = recvfrom(fd, packet_buffer->buf[i], LIBUS_UDP_MAX_SIZE, flags, (struct sockaddr *)&packet_buffer->addr[i], &addr_len);
+
+ if (ret == -1) {
+ return i;
+ }
+
+ packet_buffer->len[i] = ret;
+ }
+
+ return LIBUS_UDP_MAX_NUM;
+#else
+ // we need to set controllen for ip packet
+ for (int i = 0; i < vlen; i++) {
+ ((struct mmsghdr *)msgvec)[i].msg_hdr.msg_controllen = 256;
+ }
+
+ return recvmmsg(fd, (struct mmsghdr *)msgvec, vlen, flags, 0);
+#endif
+}
+
+// this one is needed for knowing the destination addr of udp packet
+// an udp socket can only bind to one port, and that port never changes
+// this function returns ONLY the IP address, not any port
+int bsd_udp_packet_buffer_local_ip(void *msgvec, int index, char *ip) {
+#if defined(_WIN32) || defined(__APPLE__)
+ return 0; // not supported
+#else
+ struct msghdr *mh = &((struct mmsghdr *) msgvec)[index].msg_hdr;
+ for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL; cmsg = CMSG_NXTHDR(mh, cmsg)) {
+ // ipv6 or ipv4
+ if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
+ struct in_pktinfo *pi = (struct in_pktinfo *) CMSG_DATA(cmsg);
+ memcpy(ip, &pi->ipi_addr, 4);
+ return 4;
+ }
+
+ if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
+ struct in6_pktinfo *pi6 = (struct in6_pktinfo *) CMSG_DATA(cmsg);
+ memcpy(ip, &pi6->ipi6_addr, 16);
+ return 16;
+ }
+ }
+
+ return 0; // no length
+
+#endif
+}
+
+char *bsd_udp_packet_buffer_peer(void *msgvec, int index) {
+#if defined(_WIN32) || defined(__APPLE__)
+ struct us_internal_udp_packet_buffer *packet_buffer = (struct us_internal_udp_packet_buffer *) msgvec;
+ return (char *)&packet_buffer->addr[index];
+#else
+ return ((struct mmsghdr *) msgvec)[index].msg_hdr.msg_name;
+#endif
+}
+
+char *bsd_udp_packet_buffer_payload(void *msgvec, int index) {
+#if defined(_WIN32) || defined(__APPLE__)
+ struct us_internal_udp_packet_buffer *packet_buffer = (struct us_internal_udp_packet_buffer *) msgvec;
+ return packet_buffer->buf[index];
+#else
+ return ((struct mmsghdr *) msgvec)[index].msg_hdr.msg_iov[0].iov_base;
+#endif
+}
+
+int bsd_udp_packet_buffer_payload_length(void *msgvec, int index) {
+#if defined(_WIN32) || defined(__APPLE__)
+ struct us_internal_udp_packet_buffer *packet_buffer = (struct us_internal_udp_packet_buffer *) msgvec;
+ return packet_buffer->len[index];
+#else
+ return ((struct mmsghdr *) msgvec)[index].msg_len;
+#endif
+}
+
+void bsd_udp_buffer_set_packet_payload(struct us_udp_packet_buffer_t *send_buf, int index, int offset, void *payload, int length, void *peer_addr) {
+#if defined(_WIN32) || defined(__APPLE__)
+ struct us_internal_udp_packet_buffer *packet_buffer = (struct us_internal_udp_packet_buffer *) send_buf;
+
+ memcpy(packet_buffer->buf[index], payload, length);
+ memcpy(&packet_buffer->addr[index], peer_addr, sizeof(struct sockaddr_storage));
+
+ packet_buffer->len[index] = length;
+#else
+ //printf("length: %d, offset: %d\n", length, offset);
+
+ struct mmsghdr *ss = (struct mmsghdr *) send_buf;
+
+ // copy the peer address
+ memcpy(ss[index].msg_hdr.msg_name, peer_addr, /*ss[index].msg_hdr.msg_namelen*/ sizeof(struct sockaddr_in));
+
+ // set control length to 0
+ ss[index].msg_hdr.msg_controllen = 0;
+
+ // copy the payload
+
+ ss[index].msg_hdr.msg_iov->iov_len = length + offset;
+
+
+ memcpy(((char *) ss[index].msg_hdr.msg_iov->iov_base) + offset, payload, length);
+#endif
+}
+
+/* The maximum UDP payload size is 64kb, but in IPV6 you can have jumbopackets larger than so.
+ * We do not support those jumbo packets currently, but will safely ignore them.
+ * Any sane sender would assume we don't support them if we consistently drop them.
+ * Therefore a udp_packet_buffer_t will be 64 MB in size (64kb * 1024). */
+void *bsd_create_udp_packet_buffer() {
+#if defined(_WIN32) || defined(__APPLE__)
+ struct us_internal_udp_packet_buffer *b = malloc(sizeof(struct us_internal_udp_packet_buffer) + LIBUS_UDP_MAX_SIZE * LIBUS_UDP_MAX_NUM);
+
+ for (int i = 0; i < LIBUS_UDP_MAX_NUM; i++) {
+ b->buf[i] = ((char *) b) + sizeof(struct us_internal_udp_packet_buffer) + LIBUS_UDP_MAX_SIZE * i;
+ }
+
+ return (struct us_udp_packet_buffer_t *) b;
+#else
+ /* Allocate 64kb times 1024 */
+ struct us_internal_udp_packet_buffer *b = malloc(sizeof(struct us_internal_udp_packet_buffer) + LIBUS_UDP_MAX_SIZE * LIBUS_UDP_MAX_NUM);
+
+ for (int n = 0; n < LIBUS_UDP_MAX_NUM; ++n) {
+
+ b->iov[n].iov_base = &((char *) (b + 1))[n * LIBUS_UDP_MAX_SIZE];
+ b->iov[n].iov_len = LIBUS_UDP_MAX_SIZE;
+
+ b->msgvec[n].msg_hdr = (struct msghdr) {
+ .msg_name = &b->addr,
+ .msg_namelen = sizeof (struct sockaddr_storage),
+
+ .msg_iov = &b->iov[n],
+ .msg_iovlen = 1,
+
+ .msg_control = b->control[n],
+ .msg_controllen = 256,
+ };
+ }
+
+ return (struct us_udp_packet_buffer_t *) b;
+#endif
+}
+
+LIBUS_SOCKET_DESCRIPTOR apple_no_sigpipe(LIBUS_SOCKET_DESCRIPTOR fd) {
+#ifdef __APPLE__
+ if (fd != LIBUS_SOCKET_ERROR) {
+ int no_sigpipe = 1;
+ setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *) &no_sigpipe, sizeof(int));
+ }
+#endif
+ return fd;
+}
+
+LIBUS_SOCKET_DESCRIPTOR bsd_set_nonblocking(LIBUS_SOCKET_DESCRIPTOR fd) {
+#ifdef _WIN32
+ /* Libuv will set windows sockets as non-blocking */
+#else
+ fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
+#endif
+ return fd;
+}
+
+void bsd_socket_nodelay(LIBUS_SOCKET_DESCRIPTOR fd, int enabled) {
+ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *) &enabled, sizeof(enabled));
+}
+
+void bsd_socket_flush(LIBUS_SOCKET_DESCRIPTOR fd) {
+ // Linux TCP_CORK has the same underlying corking mechanism as with MSG_MORE
+#ifdef TCP_CORK
+ int enabled = 0;
+ setsockopt(fd, IPPROTO_TCP, TCP_CORK, (void *) &enabled, sizeof(int));
+#endif
+}
+
+LIBUS_SOCKET_DESCRIPTOR bsd_create_socket(int domain, int type, int protocol) {
+ // returns INVALID_SOCKET on error
+ int flags = 0;
+#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
+ flags = SOCK_CLOEXEC | SOCK_NONBLOCK;
+#endif
+
+ LIBUS_SOCKET_DESCRIPTOR created_fd = socket(domain, type | flags, protocol);
+
+ return bsd_set_nonblocking(apple_no_sigpipe(created_fd));
+}
+
+void bsd_close_socket(LIBUS_SOCKET_DESCRIPTOR fd) {
+#ifdef _WIN32
+ closesocket(fd);
+#else
+ close(fd);
+#endif
+}
+
+void bsd_shutdown_socket(LIBUS_SOCKET_DESCRIPTOR fd) {
+#ifdef _WIN32
+ shutdown(fd, SD_SEND);
+#else
+ shutdown(fd, SHUT_WR);
+#endif
+}
+
+void bsd_shutdown_socket_read(LIBUS_SOCKET_DESCRIPTOR fd) {
+#ifdef _WIN32
+ shutdown(fd, SD_RECEIVE);
+#else
+ shutdown(fd, SHUT_RD);
+#endif
+}
+
+void internal_finalize_bsd_addr(struct bsd_addr_t *addr) {
+ // parse, so to speak, the address
+ if (addr->mem.ss_family == AF_INET6) {
+ addr->ip = (char *) &((struct sockaddr_in6 *) addr)->sin6_addr;
+ addr->ip_length = sizeof(struct in6_addr);
+ addr->port = ntohs(((struct sockaddr_in6 *) addr)->sin6_port);
+ } else if (addr->mem.ss_family == AF_INET) {
+ addr->ip = (char *) &((struct sockaddr_in *) addr)->sin_addr;
+ addr->ip_length = sizeof(struct in_addr);
+ addr->port = ntohs(((struct sockaddr_in *) addr)->sin_port);
+ } else {
+ addr->ip_length = 0;
+ addr->port = -1;
+ }
+}
+
+int bsd_local_addr(LIBUS_SOCKET_DESCRIPTOR fd, struct bsd_addr_t *addr) {
+ addr->len = sizeof(addr->mem);
+ if (getsockname(fd, (struct sockaddr *) &addr->mem, &addr->len)) {
+ return -1;
+ }
+ internal_finalize_bsd_addr(addr);
+ return 0;
+}
+
+int bsd_remote_addr(LIBUS_SOCKET_DESCRIPTOR fd, struct bsd_addr_t *addr) {
+ addr->len = sizeof(addr->mem);
+ if (getpeername(fd, (struct sockaddr *) &addr->mem, &addr->len)) {
+ return -1;
+ }
+ internal_finalize_bsd_addr(addr);
+ return 0;
+}
+
+char *bsd_addr_get_ip(struct bsd_addr_t *addr) {
+ return addr->ip;
+}
+
+int bsd_addr_get_ip_length(struct bsd_addr_t *addr) {
+ return addr->ip_length;
+}
+
+int bsd_addr_get_port(struct bsd_addr_t *addr) {
+ return addr->port;
+}
+
+// called by dispatch_ready_poll
+LIBUS_SOCKET_DESCRIPTOR bsd_accept_socket(LIBUS_SOCKET_DESCRIPTOR fd, struct bsd_addr_t *addr) {
+ LIBUS_SOCKET_DESCRIPTOR accepted_fd;
+ addr->len = sizeof(addr->mem);
+
+#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
+ // Linux, FreeBSD
+ accepted_fd = accept4(fd, (struct sockaddr *) addr, &addr->len, SOCK_CLOEXEC | SOCK_NONBLOCK);
+#else
+ // Windows, OS X
+ accepted_fd = accept(fd, (struct sockaddr *) addr, &addr->len);
+
+#endif
+
+ /* We cannot rely on addr since it is not initialized if failed */
+ if (accepted_fd == LIBUS_SOCKET_ERROR) {
+ return LIBUS_SOCKET_ERROR;
+ }
+
+ internal_finalize_bsd_addr(addr);
+
+ return bsd_set_nonblocking(apple_no_sigpipe(accepted_fd));
+}
+
+int bsd_recv(LIBUS_SOCKET_DESCRIPTOR fd, void *buf, int length, int flags) {
+ return recv(fd, buf, length, flags);
+}
+
+int bsd_send(LIBUS_SOCKET_DESCRIPTOR fd, const char *buf, int length, int msg_more) {
+
+ // MSG_MORE (Linux), MSG_PARTIAL (Windows), TCP_NOPUSH (BSD)
+
+#ifndef MSG_NOSIGNAL
+#define MSG_NOSIGNAL 0
+#endif
+
+#ifdef MSG_MORE
+
+ // for Linux we do not want signals
+ return send(fd, buf, length, ((msg_more != 0) * MSG_MORE) | MSG_NOSIGNAL);
+
+#else
+
+ // use TCP_NOPUSH
+
+ return send(fd, buf, length, MSG_NOSIGNAL);
+
+#endif
+}
+
+int bsd_would_block() {
+#ifdef _WIN32
+ return WSAGetLastError() == WSAEWOULDBLOCK;
+#else
+ return errno == EWOULDBLOCK;// || errno == EAGAIN;
+#endif
+}
+
+// return LIBUS_SOCKET_ERROR or the fd that represents listen socket
+// listen both on ipv6 and ipv4
+LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket(const char *host, int port, int options) {
+ struct addrinfo hints, *result;
+ memset(&hints, 0, sizeof(struct addrinfo));
+
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ char port_string[16];
+ snprintf(port_string, 16, "%d", port);
+
+ if (getaddrinfo(host, port_string, &hints, &result)) {
+ return LIBUS_SOCKET_ERROR;
+ }
+
+ LIBUS_SOCKET_DESCRIPTOR listenFd = LIBUS_SOCKET_ERROR;
+ struct addrinfo *listenAddr;
+ for (struct addrinfo *a = result; a && listenFd == LIBUS_SOCKET_ERROR; a = a->ai_next) {
+ if (a->ai_family == AF_INET6) {
+ listenFd = bsd_create_socket(a->ai_family, a->ai_socktype, a->ai_protocol);
+ listenAddr = a;
+ }
+ }
+
+ for (struct addrinfo *a = result; a && listenFd == LIBUS_SOCKET_ERROR; a = a->ai_next) {
+ if (a->ai_family == AF_INET) {
+ listenFd = bsd_create_socket(a->ai_family, a->ai_socktype, a->ai_protocol);
+ listenAddr = a;
+ }
+ }
+
+ if (listenFd == LIBUS_SOCKET_ERROR) {
+ freeaddrinfo(result);
+ return LIBUS_SOCKET_ERROR;
+ }
+
+ if (port != 0) {
+ /* Otherwise, always enable SO_REUSEPORT and SO_REUSEADDR _unless_ options specify otherwise */
+#ifdef _WIN32
+ if (options & LIBUS_LISTEN_EXCLUSIVE_PORT) {
+ int optval2 = 1;
+ setsockopt(listenFd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (void *) &optval2, sizeof(optval2));
+ } else {
+ int optval3 = 1;
+ setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, (void *) &optval3, sizeof(optval3));
+ }
+#else
+ #if /*defined(__linux) &&*/ defined(SO_REUSEPORT)
+ if (!(options & LIBUS_LISTEN_EXCLUSIVE_PORT)) {
+ int optval = 1;
+ setsockopt(listenFd, SOL_SOCKET, SO_REUSEPORT, (void *) &optval, sizeof(optval));
+ }
+ #endif
+ int enabled = 1;
+ setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, (void *) &enabled, sizeof(enabled));
+#endif
+
+ }
+
+#ifdef IPV6_V6ONLY
+ int disabled = 0;
+ setsockopt(listenFd, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &disabled, sizeof(disabled));
+#endif
+
+ if (bind(listenFd, listenAddr->ai_addr, (socklen_t) listenAddr->ai_addrlen) || listen(listenFd, 512)) {
+ bsd_close_socket(listenFd);
+ freeaddrinfo(result);
+ return LIBUS_SOCKET_ERROR;
+ }
+
+ freeaddrinfo(result);
+ return listenFd;
+}
+
+#ifndef _WIN32
+#include <sys/un.h>
+#else
+#include <afunix.h>
+#include <io.h>
+#endif
+#include <sys/stat.h>
+#include <stddef.h>
+LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket_unix(const char *path, int options) {
+
+ LIBUS_SOCKET_DESCRIPTOR listenFd = LIBUS_SOCKET_ERROR;
+
+ listenFd = bsd_create_socket(AF_UNIX, SOCK_STREAM, 0);
+
+ if (listenFd == LIBUS_SOCKET_ERROR) {
+ return LIBUS_SOCKET_ERROR;
+ }
+
+#ifndef _WIN32
+ // 700 permission by default
+ fchmod(listenFd, S_IRWXU);
+#else
+ _chmod(path, S_IREAD | S_IWRITE | S_IEXEC);
+#endif
+
+ struct sockaddr_un server_address;
+ memset(&server_address, 0, sizeof(server_address));
+ server_address.sun_family = AF_UNIX;
+ strcpy(server_address.sun_path, path);
+ int size = offsetof(struct sockaddr_un, sun_path) + strlen(server_address.sun_path);
+#ifdef _WIN32
+ _unlink(path);
+#else
+ unlink(path);
+#endif
+
+ if (bind(listenFd, (struct sockaddr *)&server_address, size) || listen(listenFd, 512)) {
+ bsd_close_socket(listenFd);
+ return LIBUS_SOCKET_ERROR;
+ }
+
+ return listenFd;
+}
+
+LIBUS_SOCKET_DESCRIPTOR bsd_create_udp_socket(const char *host, int port) {
+ struct addrinfo hints, *result;
+ memset(&hints, 0, sizeof(struct addrinfo));
+
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+
+ char port_string[16];
+ snprintf(port_string, 16, "%d", port);
+
+ if (getaddrinfo(host, port_string, &hints, &result)) {
+ return LIBUS_SOCKET_ERROR;
+ }
+
+ LIBUS_SOCKET_DESCRIPTOR listenFd = LIBUS_SOCKET_ERROR;
+ struct addrinfo *listenAddr;
+ for (struct addrinfo *a = result; a && listenFd == LIBUS_SOCKET_ERROR; a = a->ai_next) {
+ if (a->ai_family == AF_INET6) {
+ listenFd = bsd_create_socket(a->ai_family, a->ai_socktype, a->ai_protocol);
+ listenAddr = a;
+ }
+ }
+
+ for (struct addrinfo *a = result; a && listenFd == LIBUS_SOCKET_ERROR; a = a->ai_next) {
+ if (a->ai_family == AF_INET) {
+ listenFd = bsd_create_socket(a->ai_family, a->ai_socktype, a->ai_protocol);
+ listenAddr = a;
+ }
+ }
+
+ if (listenFd == LIBUS_SOCKET_ERROR) {
+ freeaddrinfo(result);
+ return LIBUS_SOCKET_ERROR;
+ }
+
+ if (port != 0) {
+ /* Should this also go for UDP? */
+ int enabled = 1;
+ setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, (void *) &enabled, sizeof(enabled));
+ }
+
+#ifdef IPV6_V6ONLY
+ int disabled = 0;
+ setsockopt(listenFd, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &disabled, sizeof(disabled));
+#endif
+
+ /* We need destination address for udp packets in both ipv6 and ipv4 */
+
+/* On FreeBSD this option seems to be called like so */
+#ifndef IPV6_RECVPKTINFO
+#define IPV6_RECVPKTINFO IPV6_PKTINFO
+#endif
+
+ int enabled = 1;
+ if (setsockopt(listenFd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (void *) &enabled, sizeof(enabled)) == -1) {
+ if (errno == 92) {
+ if (setsockopt(listenFd, IPPROTO_IP, IP_PKTINFO, (void *) &enabled, sizeof(enabled)) != 0) {
+ printf("Error setting IPv4 pktinfo!\n");
+ }
+ } else {
+ printf("Error setting IPv6 pktinfo!\n");
+ }
+ }
+
+ /* These are used for getting the ECN */
+ if (setsockopt(listenFd, IPPROTO_IPV6, IPV6_RECVTCLASS, (void *) &enabled, sizeof(enabled)) == -1) {
+ if (errno == 92) {
+ if (setsockopt(listenFd, IPPROTO_IP, IP_RECVTOS, (void *) &enabled, sizeof(enabled)) != 0) {
+ printf("Error setting IPv4 ECN!\n");
+ }
+ } else {
+ printf("Error setting IPv6 ECN!\n");
+ }
+ }
+
+ /* We bind here as well */
+ if (bind(listenFd, listenAddr->ai_addr, (socklen_t) listenAddr->ai_addrlen)) {
+ bsd_close_socket(listenFd);
+ freeaddrinfo(result);
+ return LIBUS_SOCKET_ERROR;
+ }
+
+ freeaddrinfo(result);
+ return listenFd;
+}
+
+int bsd_udp_packet_buffer_ecn(void *msgvec, int index) {
+
+#if defined(_WIN32) || defined(__APPLE__)
+ printf("ECN not supported!\n");
+#else
+ // we should iterate all control messages once, after recvmmsg and then only fetch them with these functions
+ struct msghdr *mh = &((struct mmsghdr *) msgvec)[index].msg_hdr;
+ for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL; cmsg = CMSG_NXTHDR(mh, cmsg)) {
+ // do we need to get TOS from ipv6 also?
+ if (cmsg->cmsg_level == IPPROTO_IP) {
+ if (cmsg->cmsg_type == IP_TOS) {
+ uint8_t tos = *(uint8_t *)CMSG_DATA(cmsg);
+ return tos & 3;
+ }
+ }
+
+ if (cmsg->cmsg_level == IPPROTO_IPV6) {
+ if (cmsg->cmsg_type == IPV6_TCLASS) {
+ // is this correct?
+ uint8_t tos = *(uint8_t *)CMSG_DATA(cmsg);
+ return tos & 3;
+ }
+ }
+ }
+#endif
+
+ printf("We got no ECN!\n");
+
+ return 0; // no ecn defaults to 0
+}
+
+static int bsd_do_connect(struct addrinfo *result, int fd)
+{
+ return connect(fd, result->ai_addr, (socklen_t) result->ai_addrlen);
+}
+
+LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket(const char *host, int port, const char *source_host, int options) {
+ struct addrinfo hints, *result;
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ char port_string[16];
+ snprintf(port_string, 16, "%d", port);
+
+ if (getaddrinfo(host, port_string, &hints, &result) != 0) {
+ return LIBUS_SOCKET_ERROR;
+ }
+
+ LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_socket(result->ai_family, result->ai_socktype, result->ai_protocol);
+ if (fd == LIBUS_SOCKET_ERROR) {
+ freeaddrinfo(result);
+ return LIBUS_SOCKET_ERROR;
+ }
+
+ if (source_host) {
+ struct addrinfo *interface_result;
+ if (!getaddrinfo(source_host, NULL, NULL, &interface_result)) {
+ int ret = bind(fd, interface_result->ai_addr, (socklen_t) interface_result->ai_addrlen);
+ freeaddrinfo(interface_result);
+ if (ret == LIBUS_SOCKET_ERROR) {
+ bsd_close_socket(fd);
+ freeaddrinfo(result);
+ return LIBUS_SOCKET_ERROR;
+ }
+ }
+ }
+
+ do {
+ if (bsd_do_connect(result, fd) != 0 && errno != EINPROGRESS) {
+ bsd_close_socket(fd);
+ freeaddrinfo(result);
+ return LIBUS_SOCKET_ERROR;
+ }
+ } while (errno == EINTR);
+
+ freeaddrinfo(result);
+
+ return fd;
+}
+
+LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket_unix(const char *server_path, int options) {
+
+ struct sockaddr_un server_address;
+ memset(&server_address, 0, sizeof(server_address));
+ server_address.sun_family = AF_UNIX;
+ strcpy(server_address.sun_path, server_path);
+ int size = offsetof(struct sockaddr_un, sun_path) + strlen(server_address.sun_path);
+
+ LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_socket(AF_UNIX, SOCK_STREAM, 0);
+
+ if (fd == LIBUS_SOCKET_ERROR) {
+ return LIBUS_SOCKET_ERROR;
+ }
+
+ if (connect(fd, (struct sockaddr *)&server_address, size) != 0 && errno != EINPROGRESS) {
+ bsd_close_socket(fd);
+ return LIBUS_SOCKET_ERROR;
+ }
+
+ return fd;
+} \ No newline at end of file
diff --git a/packages/bun-usockets/src/context.c b/packages/bun-usockets/src/context.c
new file mode 100644
index 000000000..a4d243e85
--- /dev/null
+++ b/packages/bun-usockets/src/context.c
@@ -0,0 +1,575 @@
+/*
+ * Authored by Alex Hultman, 2018-2019.
+ * Intellectual property of third-party.
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libusockets.h"
+#include "internal/internal.h"
+#include <stdlib.h>
+#include <string.h>
+
+int default_is_low_prio_handler(struct us_socket_t *s) {
+ return 0;
+}
+
+/* Shared with SSL */
+
+unsigned short us_socket_context_timestamp(int ssl, struct us_socket_context_t *context) {
+ return context->timestamp;
+}
+int us_raw_root_certs(struct us_cert_string_t**out){
+ return us_internal_raw_root_certs(out);
+}
+
+void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls) {
+ /* us_listen_socket_t extends us_socket_t so we close in similar ways */
+ if (!us_socket_is_closed(0, &ls->s)) {
+ us_internal_socket_context_unlink_listen_socket(ls->s.context, ls);
+ us_poll_stop((struct us_poll_t *) &ls->s, ls->s.context->loop);
+ bsd_close_socket(us_poll_fd((struct us_poll_t *) &ls->s));
+
+ /* Link this socket to the close-list and let it be deleted after this iteration */
+ ls->s.next = ls->s.context->loop->data.closed_head;
+ ls->s.context->loop->data.closed_head = &ls->s;
+
+ /* Any socket with prev = context is marked as closed */
+ ls->s.prev = (struct us_socket_t *) ls->s.context;
+ }
+
+ /* We cannot immediately free a listen socket as we can be inside an accept loop */
+}
+
+void us_socket_context_close(int ssl, struct us_socket_context_t *context) {
+ /* Begin by closing all listen sockets */
+ struct us_listen_socket_t *ls = context->head_listen_sockets;
+ while (ls) {
+ struct us_listen_socket_t *nextLS = (struct us_listen_socket_t *) ls->s.next;
+ us_listen_socket_close(ssl, ls);
+ ls = nextLS;
+ }
+
+ /* Then close all regular sockets */
+ struct us_socket_t *s = context->head_sockets;
+ while (s) {
+ struct us_socket_t *nextS = s->next;
+ us_socket_close(ssl, s, 0, 0);
+ s = nextS;
+ }
+}
+
+void us_internal_socket_context_unlink_listen_socket(struct us_socket_context_t *context, struct us_listen_socket_t *ls) {
+ /* We have to properly update the iterator used to sweep sockets for timeouts */
+ if (ls == (struct us_listen_socket_t *) context->iterator) {
+ context->iterator = ls->s.next;
+ }
+
+ if (ls->s.prev == ls->s.next) {
+ context->head_listen_sockets = 0;
+ } else {
+ if (ls->s.prev) {
+ ls->s.prev->next = ls->s.next;
+ } else {
+ context->head_listen_sockets = (struct us_listen_socket_t *) ls->s.next;
+ }
+ if (ls->s.next) {
+ ls->s.next->prev = ls->s.prev;
+ }
+ }
+}
+
+void us_internal_socket_context_unlink_socket(struct us_socket_context_t *context, struct us_socket_t *s) {
+ /* We have to properly update the iterator used to sweep sockets for timeouts */
+ if (s == context->iterator) {
+ context->iterator = s->next;
+ }
+
+ if (s->prev == s->next) {
+ context->head_sockets = 0;
+ } else {
+ if (s->prev) {
+ s->prev->next = s->next;
+ } else {
+ context->head_sockets = s->next;
+ }
+ if (s->next) {
+ s->next->prev = s->prev;
+ }
+ }
+}
+
+/* We always add in the top, so we don't modify any s.next */
+void us_internal_socket_context_link_listen_socket(struct us_socket_context_t *context, struct us_listen_socket_t *ls) {
+ ls->s.context = context;
+ ls->s.next = (struct us_socket_t *) context->head_listen_sockets;
+ ls->s.prev = 0;
+ if (context->head_listen_sockets) {
+ context->head_listen_sockets->s.prev = &ls->s;
+ }
+ context->head_listen_sockets = ls;
+}
+
+/* We always add in the top, so we don't modify any s.next */
+void us_internal_socket_context_link_socket(struct us_socket_context_t *context, struct us_socket_t *s) {
+ s->context = context;
+ s->next = context->head_sockets;
+ s->prev = 0;
+ if (context->head_sockets) {
+ context->head_sockets->prev = s;
+ }
+ context->head_sockets = s;
+}
+
+struct us_loop_t *us_socket_context_loop(int ssl, struct us_socket_context_t *context) {
+ return context->loop;
+}
+
+/* Not shared with SSL */
+
+/* Lookup userdata by server name pattern */
+void *us_socket_context_find_server_name_userdata(int ssl, struct us_socket_context_t *context, const char *hostname_pattern) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ return us_internal_ssl_socket_context_find_server_name_userdata((struct us_internal_ssl_socket_context_t *) context, hostname_pattern);
+ }
+#endif
+ return NULL;
+}
+
+/* Get userdata attached to this SNI-routed socket, or nullptr if default */
+void *us_socket_server_name_userdata(int ssl, struct us_socket_t *s) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ return us_internal_ssl_socket_get_sni_userdata((struct us_internal_ssl_socket_t *) s);
+ }
+#endif
+ return NULL;
+}
+
+/* Add SNI context */
+void us_socket_context_add_server_name(int ssl, struct us_socket_context_t *context, const char *hostname_pattern, struct us_socket_context_options_t options, void *user) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ us_internal_ssl_socket_context_add_server_name((struct us_internal_ssl_socket_context_t *) context, hostname_pattern, options, user);
+ }
+#endif
+}
+void us_bun_socket_context_add_server_name(int ssl, struct us_socket_context_t *context, const char *hostname_pattern, struct us_bun_socket_context_options_t options, void *user) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ us_bun_internal_ssl_socket_context_add_server_name((struct us_internal_ssl_socket_context_t *) context, hostname_pattern, options, user);
+ }
+#endif
+}
+
+/* Remove SNI context */
+void us_socket_context_remove_server_name(int ssl, struct us_socket_context_t *context, const char *hostname_pattern) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ us_internal_ssl_socket_context_remove_server_name((struct us_internal_ssl_socket_context_t *) context, hostname_pattern);
+ }
+#endif
+}
+
+/* I don't like this one - maybe rename it to on_missing_server_name? */
+
+/* Called when SNI matching fails - not if a match could be made.
+ * You may modify the context by adding/removing names in this callback.
+ * If the correct name is added immediately in the callback, it will be used */
+void us_socket_context_on_server_name(int ssl, struct us_socket_context_t *context, void (*cb)(struct us_socket_context_t *, const char *hostname)) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ us_internal_ssl_socket_context_on_server_name((struct us_internal_ssl_socket_context_t *) context, (void (*)(struct us_internal_ssl_socket_context_t *, const char *hostname)) cb);
+ }
+#endif
+}
+
+/* Todo: get native context from SNI pattern */
+
+void *us_socket_context_get_native_handle(int ssl, struct us_socket_context_t *context) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ return us_internal_ssl_socket_context_get_native_handle((struct us_internal_ssl_socket_context_t *) context);
+ }
+#endif
+
+ /* There is no native handle for a non-SSL socket context */
+ return 0;
+}
+
+/* Options is currently only applicable for SSL - this will change with time (prefer_low_memory is one example) */
+struct us_socket_context_t *us_create_socket_context(int ssl, struct us_loop_t *loop, int context_ext_size, struct us_socket_context_options_t options) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ /* This function will call us, again, with SSL = false and a bigger ext_size */
+ return (struct us_socket_context_t *) us_internal_create_ssl_socket_context(loop, context_ext_size, options);
+ }
+#endif
+
+ /* This path is taken once either way - always BEFORE whatever SSL may do LATER.
+ * context_ext_size will however be modified larger in case of SSL, to hold SSL extensions */
+
+ struct us_socket_context_t *context = us_malloc(sizeof(struct us_socket_context_t) + context_ext_size);
+ context->loop = loop;
+ context->head_sockets = 0;
+ context->head_listen_sockets = 0;
+ context->iterator = 0;
+ context->next = 0;
+ context->is_low_prio = default_is_low_prio_handler;
+
+ /* Begin at 0 */
+ context->timestamp = 0;
+ context->long_timestamp = 0;
+ context->global_tick = 0;
+
+ us_internal_loop_link(loop, context);
+
+ /* If we are called from within SSL code, SSL code will make further changes to us */
+ return context;
+}
+
+struct us_socket_context_t *us_create_bun_socket_context(int ssl, struct us_loop_t *loop, int context_ext_size, struct us_bun_socket_context_options_t options) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ /* This function will call us, again, with SSL = false and a bigger ext_size */
+ return (struct us_socket_context_t *) us_internal_bun_create_ssl_socket_context(loop, context_ext_size, options);
+ }
+#endif
+
+ /* This path is taken once either way - always BEFORE whatever SSL may do LATER.
+ * context_ext_size will however be modified larger in case of SSL, to hold SSL extensions */
+
+ struct us_socket_context_t *context = us_malloc(sizeof(struct us_socket_context_t) + context_ext_size);
+ context->loop = loop;
+ context->head_sockets = 0;
+ context->head_listen_sockets = 0;
+ context->iterator = 0;
+ context->next = 0;
+ context->is_low_prio = default_is_low_prio_handler;
+
+ /* Begin at 0 */
+ context->timestamp = 0;
+ context->long_timestamp = 0;
+ context->global_tick = 0;
+
+ us_internal_loop_link(loop, context);
+
+ /* If we are called from within SSL code, SSL code will make further changes to us */
+ return context;
+}
+
+
+struct us_bun_verify_error_t us_socket_verify_error(int ssl, struct us_socket_t *socket) {
+ #ifndef LIBUS_NO_SSL
+ if (ssl) {
+ /* This function will call us again with SSL=false */
+ return us_internal_verify_error((struct us_internal_ssl_socket_t *)socket);
+ }
+ #endif
+
+ return (struct us_bun_verify_error_t) { .error = 0, .code = NULL, .reason = NULL };
+}
+
+
+void us_socket_context_free(int ssl, struct us_socket_context_t *context) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ /* This function will call us again with SSL=false */
+ us_internal_ssl_socket_context_free((struct us_internal_ssl_socket_context_t *) context);
+ return;
+ }
+#endif
+
+ /* This path is taken once either way - always AFTER whatever SSL may do BEFORE.
+ * This is the opposite order compared to when creating the context - SSL code is cleaning up before non-SSL */
+
+ us_internal_loop_unlink(context->loop, context);
+ us_free(context);
+}
+
+struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_context_t *context, const char *host, int port, int options, int socket_ext_size) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ return us_internal_ssl_socket_context_listen((struct us_internal_ssl_socket_context_t *) context, host, port, options, socket_ext_size);
+ }
+#endif
+
+ LIBUS_SOCKET_DESCRIPTOR listen_socket_fd = bsd_create_listen_socket(host, port, options);
+
+ if (listen_socket_fd == LIBUS_SOCKET_ERROR) {
+ return 0;
+ }
+
+ struct us_poll_t *p = us_create_poll(context->loop, 0, sizeof(struct us_listen_socket_t));
+ us_poll_init(p, listen_socket_fd, POLL_TYPE_SEMI_SOCKET);
+ us_poll_start(p, context->loop, LIBUS_SOCKET_READABLE);
+
+ struct us_listen_socket_t *ls = (struct us_listen_socket_t *) p;
+
+ ls->s.context = context;
+ ls->s.timeout = 255;
+ ls->s.long_timeout = 255;
+ ls->s.low_prio_state = 0;
+ ls->s.next = 0;
+ us_internal_socket_context_link_listen_socket(context, ls);
+
+ ls->socket_ext_size = socket_ext_size;
+
+ return ls;
+}
+
+struct us_listen_socket_t *us_socket_context_listen_unix(int ssl, struct us_socket_context_t *context, const char *path, int options, int socket_ext_size) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ return us_internal_ssl_socket_context_listen_unix((struct us_internal_ssl_socket_context_t *) context, path, options, socket_ext_size);
+ }
+#endif
+
+ LIBUS_SOCKET_DESCRIPTOR listen_socket_fd = bsd_create_listen_socket_unix(path, options);
+
+ if (listen_socket_fd == LIBUS_SOCKET_ERROR) {
+ return 0;
+ }
+
+ struct us_poll_t *p = us_create_poll(context->loop, 0, sizeof(struct us_listen_socket_t));
+ us_poll_init(p, listen_socket_fd, POLL_TYPE_SEMI_SOCKET);
+ us_poll_start(p, context->loop, LIBUS_SOCKET_READABLE);
+
+ struct us_listen_socket_t *ls = (struct us_listen_socket_t *) p;
+
+ ls->s.context = context;
+ ls->s.timeout = 255;
+ ls->s.long_timeout = 255;
+ ls->s.low_prio_state = 0;
+ ls->s.next = 0;
+ us_internal_socket_context_link_listen_socket(context, ls);
+
+ ls->socket_ext_size = socket_ext_size;
+
+ return ls;
+}
+
+struct us_socket_t *us_socket_context_connect(int ssl, struct us_socket_context_t *context, const char *host, int port, const char *source_host, int options, int socket_ext_size) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ return (struct us_socket_t *) us_internal_ssl_socket_context_connect((struct us_internal_ssl_socket_context_t *) context, host, port, source_host, options, socket_ext_size);
+ }
+#endif
+
+ LIBUS_SOCKET_DESCRIPTOR connect_socket_fd = bsd_create_connect_socket(host, port, source_host, options);
+ if (connect_socket_fd == LIBUS_SOCKET_ERROR) {
+ return 0;
+ }
+
+ /* Connect sockets are semi-sockets just like listen sockets */
+ struct us_poll_t *p = us_create_poll(context->loop, 0, sizeof(struct us_socket_t) + socket_ext_size);
+ us_poll_init(p, connect_socket_fd, POLL_TYPE_SEMI_SOCKET);
+ us_poll_start(p, context->loop, LIBUS_SOCKET_WRITABLE);
+
+ struct us_socket_t *connect_socket = (struct us_socket_t *) p;
+
+ /* Link it into context so that timeout fires properly */
+ connect_socket->context = context;
+ connect_socket->timeout = 255;
+ connect_socket->long_timeout = 255;
+ connect_socket->low_prio_state = 0;
+ us_internal_socket_context_link_socket(context, connect_socket);
+
+ return connect_socket;
+}
+
+struct us_socket_t *us_socket_context_connect_unix(int ssl, struct us_socket_context_t *context, const char *server_path, int options, int socket_ext_size) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ return (struct us_socket_t *) us_internal_ssl_socket_context_connect_unix((struct us_internal_ssl_socket_context_t *) context, server_path, options, socket_ext_size);
+ }
+#endif
+
+ LIBUS_SOCKET_DESCRIPTOR connect_socket_fd = bsd_create_connect_socket_unix(server_path, options);
+ if (connect_socket_fd == LIBUS_SOCKET_ERROR) {
+ return 0;
+ }
+
+ /* Connect sockets are semi-sockets just like listen sockets */
+ struct us_poll_t *p = us_create_poll(context->loop, 0, sizeof(struct us_socket_t) + socket_ext_size);
+ us_poll_init(p, connect_socket_fd, POLL_TYPE_SEMI_SOCKET);
+ us_poll_start(p, context->loop, LIBUS_SOCKET_WRITABLE);
+
+ struct us_socket_t *connect_socket = (struct us_socket_t *) p;
+
+ /* Link it into context so that timeout fires properly */
+ connect_socket->context = context;
+ connect_socket->timeout = 255;
+ connect_socket->long_timeout = 255;
+ connect_socket->low_prio_state = 0;
+ us_internal_socket_context_link_socket(context, connect_socket);
+
+ return connect_socket;
+}
+
+struct us_socket_context_t *us_create_child_socket_context(int ssl, struct us_socket_context_t *context, int context_ext_size) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ return (struct us_socket_context_t *) us_internal_create_child_ssl_socket_context((struct us_internal_ssl_socket_context_t *) context, context_ext_size);
+ }
+#endif
+
+ /* For TCP we simply create a new context as nothing is shared */
+ struct us_socket_context_options_t options = {0};
+ return us_create_socket_context(ssl, context->loop, context_ext_size, options);
+}
+
+/* Note: This will set timeout to 0 */
+struct us_socket_t *us_socket_context_adopt_socket(int ssl, struct us_socket_context_t *context, struct us_socket_t *s, int ext_size) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ return (struct us_socket_t *) us_internal_ssl_socket_context_adopt_socket((struct us_internal_ssl_socket_context_t *) context, (struct us_internal_ssl_socket_t *) s, ext_size);
+ }
+#endif
+
+ /* Cannot adopt a closed socket */
+ if (us_socket_is_closed(ssl, s)) {
+ return s;
+ }
+
+ if (s->low_prio_state != 1) {
+ /* This properly updates the iterator if in on_timeout */
+ us_internal_socket_context_unlink_socket(s->context, s);
+ }
+
+ struct us_socket_t *new_s = (struct us_socket_t *) us_poll_resize(&s->p, s->context->loop, sizeof(struct us_socket_t) + ext_size);
+ new_s->timeout = 255;
+ new_s->long_timeout = 255;
+
+ if (new_s->low_prio_state == 1) {
+ /* update pointers in low-priority queue */
+ if (!new_s->prev) new_s->context->loop->data.low_prio_head = new_s;
+ else new_s->prev->next = new_s;
+
+ if (new_s->next) new_s->next->prev = new_s;
+ } else {
+ us_internal_socket_context_link_socket(context, new_s);
+ }
+
+ return new_s;
+}
+
+
+void us_socket_context_on_open(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_open)(struct us_socket_t *s, int is_client, char *ip, int ip_length)) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ us_internal_ssl_socket_context_on_open((struct us_internal_ssl_socket_context_t *) context, (struct us_internal_ssl_socket_t * (*)(struct us_internal_ssl_socket_t *, int, char *, int)) on_open);
+ return;
+ }
+#endif
+
+ context->on_open = on_open;
+}
+
+void us_socket_context_on_close(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_close)(struct us_socket_t *s, int code, void *reason)) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ us_internal_ssl_socket_context_on_close((struct us_internal_ssl_socket_context_t *) context, (struct us_internal_ssl_socket_t * (*)(struct us_internal_ssl_socket_t *, int code, void *reason)) on_close);
+ return;
+ }
+#endif
+
+ context->on_close = on_close;
+}
+
+void us_socket_context_on_data(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_data)(struct us_socket_t *s, char *data, int length)) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ us_internal_ssl_socket_context_on_data((struct us_internal_ssl_socket_context_t *) context, (struct us_internal_ssl_socket_t * (*)(struct us_internal_ssl_socket_t *, char *, int)) on_data);
+ return;
+ }
+#endif
+
+ context->on_data = on_data;
+}
+
+void us_socket_context_on_writable(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_writable)(struct us_socket_t *s)) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ us_internal_ssl_socket_context_on_writable((struct us_internal_ssl_socket_context_t *) context, (struct us_internal_ssl_socket_t * (*)(struct us_internal_ssl_socket_t *)) on_writable);
+ return;
+ }
+#endif
+
+ context->on_writable = on_writable;
+}
+
+void us_socket_context_on_long_timeout(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_long_timeout)(struct us_socket_t *)) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ us_internal_ssl_socket_context_on_long_timeout((struct us_internal_ssl_socket_context_t *) context, (struct us_internal_ssl_socket_t * (*)(struct us_internal_ssl_socket_t *)) on_long_timeout);
+ return;
+ }
+#endif
+
+ context->on_socket_long_timeout = on_long_timeout;
+}
+
+void us_socket_context_on_timeout(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_timeout)(struct us_socket_t *)) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ us_internal_ssl_socket_context_on_timeout((struct us_internal_ssl_socket_context_t *) context, (struct us_internal_ssl_socket_t * (*)(struct us_internal_ssl_socket_t *)) on_timeout);
+ return;
+ }
+#endif
+
+ context->on_socket_timeout = on_timeout;
+}
+
+void us_socket_context_on_end(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_end)(struct us_socket_t *)) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ us_internal_ssl_socket_context_on_end((struct us_internal_ssl_socket_context_t *) context, (struct us_internal_ssl_socket_t * (*)(struct us_internal_ssl_socket_t *)) on_end);
+ return;
+ }
+#endif
+
+ context->on_end = on_end;
+}
+
+void us_socket_context_on_connect_error(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_connect_error)(struct us_socket_t *s, int code)) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ us_internal_ssl_socket_context_on_connect_error((struct us_internal_ssl_socket_context_t *) context, (struct us_internal_ssl_socket_t * (*)(struct us_internal_ssl_socket_t *, int)) on_connect_error);
+ return;
+ }
+#endif
+
+ context->on_connect_error = on_connect_error;
+}
+
+void *us_socket_context_ext(int ssl, struct us_socket_context_t *context) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ return us_internal_ssl_socket_context_ext((struct us_internal_ssl_socket_context_t *) context);
+ }
+#endif
+
+ return context + 1;
+}
+
+
+void us_socket_context_on_handshake(int ssl, struct us_socket_context_t *context, void (*on_handshake)(struct us_socket_context_t *, int success, struct us_bun_verify_error_t verify_error, void* custom_data), void* custom_data) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ us_internal_on_ssl_handshake((struct us_internal_ssl_socket_context_t *) context, on_handshake, custom_data);
+ return;
+ }
+#endif
+} \ No newline at end of file
diff --git a/packages/bun-usockets/src/crypto/openssl.c b/packages/bun-usockets/src/crypto/openssl.c
new file mode 100644
index 000000000..b6466bcf9
--- /dev/null
+++ b/packages/bun-usockets/src/crypto/openssl.c
@@ -0,0 +1,1732 @@
+/*
+ * Authored by Alex Hultman, 2018-2019.
+ * Intellectual property of third-party.
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if (defined(LIBUS_USE_OPENSSL) || defined(LIBUS_USE_WOLFSSL))
+
+/* These are in sni_tree.cpp */
+void *sni_new();
+void sni_free(void *sni, void(*cb)(void *));
+int sni_add(void *sni, const char *hostname, void *user);
+void *sni_remove(void *sni, const char *hostname);
+void *sni_find(void *sni, const char *hostname);
+
+#include "libusockets.h"
+#include "internal/internal.h"
+#include <string.h>
+
+/* This module contains the entire OpenSSL implementation
+ * of the SSL socket and socket context interfaces. */
+#ifdef LIBUS_USE_OPENSSL
+#include <openssl/ssl.h>
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include <openssl/dh.h>
+#elif LIBUS_USE_WOLFSSL
+#include <wolfssl/options.h>
+#include <wolfssl/openssl/ssl.h>
+#include <wolfssl/openssl/bio.h>
+#include <wolfssl/openssl/err.h>
+#include <wolfssl/openssl/dh.h>
+#endif
+
+#include "./root_certs.h"
+#include <stdatomic.h>
+static const root_certs_size = sizeof(root_certs) / sizeof(root_certs[0]);
+static X509* root_cert_instances[root_certs_size] = {NULL};
+static atomic_flag root_cert_instances_lock = ATOMIC_FLAG_INIT;
+static atomic_bool root_cert_instances_initialized = 0;
+
+struct loop_ssl_data {
+ char *ssl_read_input, *ssl_read_output;
+ unsigned int ssl_read_input_length;
+ unsigned int ssl_read_input_offset;
+
+ struct us_socket_t *ssl_socket;
+
+ int last_write_was_msg_more;
+ int msg_more;
+
+ BIO *shared_rbio;
+ BIO *shared_wbio;
+ BIO_METHOD *shared_biom;
+};
+
+struct us_internal_ssl_socket_context_t {
+ struct us_socket_context_t sc;
+
+ // this thing can be shared with other socket contexts via socket transfer!
+ // maybe instead of holding once you hold many, a vector or set
+ // when a socket that belongs to another socket context transfers to a new socket context
+ SSL_CTX *ssl_context;
+ int is_parent;
+
+ /* These decorate the base implementation */
+ struct us_internal_ssl_socket_t *(*on_open)(struct us_internal_ssl_socket_t *, int is_client, char *ip, int ip_length);
+ struct us_internal_ssl_socket_t *(*on_data)(struct us_internal_ssl_socket_t *, char *data, int length);
+ struct us_internal_ssl_socket_t *(*on_writable)(struct us_internal_ssl_socket_t *);
+ struct us_internal_ssl_socket_t *(*on_close)(struct us_internal_ssl_socket_t *, int code, void *reason);
+
+ /* Called for missing SNI hostnames, if not NULL */
+ void (*on_server_name)(struct us_internal_ssl_socket_context_t *, const char *hostname);
+
+ /* Pointer to sni tree, created when the context is created and freed likewise when freed */
+ void *sni;
+
+ int pending_handshake;
+ void (*on_handshake)(struct us_internal_ssl_socket_t *, int success, struct us_bun_verify_error_t verify_error, void* custom_data);
+ void* handshake_data;
+};
+
+// same here, should or shouldn't it contain s?
+struct us_internal_ssl_socket_t {
+ struct us_socket_t s;
+ SSL *ssl;
+ int ssl_write_wants_read; // we use this for now
+ int ssl_read_wants_write;
+};
+
+int passphrase_cb(char *buf, int size, int rwflag, void *u) {
+ const char *passphrase = (const char *) u;
+ size_t passphrase_length = strlen(passphrase);
+ memcpy(buf, passphrase, passphrase_length);
+ // put null at end? no?
+ return (int) passphrase_length;
+}
+
+int BIO_s_custom_create(BIO *bio) {
+ BIO_set_init(bio, 1);
+ return 1;
+}
+
+long BIO_s_custom_ctrl(BIO *bio, int cmd, long num, void *user) {
+ switch(cmd) {
+ case BIO_CTRL_FLUSH:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
+
+int BIO_s_custom_write(BIO *bio, const char *data, int length) {
+ struct loop_ssl_data *loop_ssl_data = (struct loop_ssl_data *) BIO_get_data(bio);
+
+ loop_ssl_data->last_write_was_msg_more = loop_ssl_data->msg_more || length == 16413;
+ int written = us_socket_write(0, loop_ssl_data->ssl_socket, data, length, loop_ssl_data->last_write_was_msg_more);
+
+ if (!written) {
+ BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY | BIO_FLAGS_WRITE);
+ return -1;
+ }
+
+ // printf("BIO_s_custom_write returns: %d\n", written);
+
+ return written;
+}
+
+int BIO_s_custom_read(BIO *bio, char *dst, int length) {
+ struct loop_ssl_data *loop_ssl_data = (struct loop_ssl_data *) BIO_get_data(bio);
+
+ //printf("BIO_s_custom_read\n");
+
+ if (!loop_ssl_data->ssl_read_input_length) {
+ BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY | BIO_FLAGS_READ);
+ return -1;
+ }
+
+ if ((unsigned int) length > loop_ssl_data->ssl_read_input_length) {
+ length = loop_ssl_data->ssl_read_input_length;
+ }
+
+ memcpy(dst, loop_ssl_data->ssl_read_input + loop_ssl_data->ssl_read_input_offset, length);
+
+ loop_ssl_data->ssl_read_input_offset += length;
+ loop_ssl_data->ssl_read_input_length -= length;
+ return length;
+}
+
+
+struct us_internal_ssl_socket_t *ssl_on_open(struct us_internal_ssl_socket_t *s, int is_client, char *ip, int ip_length) {
+
+ struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s);
+
+ struct us_loop_t *loop = us_socket_context_loop(0, &context->sc);
+ struct loop_ssl_data *loop_ssl_data = (struct loop_ssl_data *) loop->data.ssl_data;
+
+ s->ssl = SSL_new(context->ssl_context);
+ s->ssl_write_wants_read = 0;
+ s->ssl_read_wants_write = 0;
+
+ SSL_set_bio(s->ssl, loop_ssl_data->shared_rbio, loop_ssl_data->shared_wbio);
+
+ BIO_up_ref(loop_ssl_data->shared_rbio);
+ BIO_up_ref(loop_ssl_data->shared_wbio);
+
+ if (is_client) {
+ SSL_set_connect_state(s->ssl);
+ } else {
+ SSL_set_accept_state(s->ssl);
+ }
+
+ struct us_internal_ssl_socket_t * result = (struct us_internal_ssl_socket_t *) context->on_open(s, is_client, ip, ip_length);
+
+ // Hello Message!
+ if(context->pending_handshake) {
+ us_internal_ssl_handshake(s, context->on_handshake, context->handshake_data);
+ }
+
+ return result;
+}
+
+
+void us_internal_on_ssl_handshake(struct us_internal_ssl_socket_context_t * context, void (*on_handshake)(struct us_internal_ssl_socket_t *, int success, struct us_bun_verify_error_t verify_error, void* custom_data), void* custom_data) {
+ context->pending_handshake = 1;
+ context->on_handshake = on_handshake;
+ context->handshake_data = custom_data;
+}
+
+void us_internal_ssl_handshake(struct us_internal_ssl_socket_t *s, void (*on_handshake)(struct us_internal_ssl_socket_t *, int success, struct us_bun_verify_error_t verify_error, void* custom_data), void* custom_data) {
+ struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s);
+
+ // will start on_open, on_writable or on_data
+ if(!s->ssl) {
+
+ context->pending_handshake = 1;
+ context->on_handshake = on_handshake;
+ context->handshake_data = custom_data;
+ return;
+ }
+
+ struct us_loop_t *loop = us_socket_context_loop(0, &context->sc);
+ struct loop_ssl_data *loop_ssl_data = (struct loop_ssl_data *) loop->data.ssl_data;
+
+ loop_ssl_data->ssl_socket = &s->s;
+
+ if (us_socket_is_closed(0, &s->s) || us_internal_ssl_socket_is_shut_down(s)) {
+ context->pending_handshake = 0;
+ context->on_handshake = NULL;
+ context->handshake_data = NULL;
+
+ struct us_bun_verify_error_t verify_error = (struct us_bun_verify_error_t) { .error = 0, .code = NULL, .reason = NULL };
+ if(on_handshake != NULL) {
+ on_handshake(s, 0, verify_error, custom_data);
+ }
+ return;
+ }
+
+
+ int result = SSL_do_handshake(s->ssl);
+
+ if (result <= 0) {
+ int err = SSL_get_error(s->ssl, result);
+ // as far as I know these are the only errors we want to handle
+ if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) {
+ context->pending_handshake = 0;
+ context->on_handshake = NULL;
+ context->handshake_data = NULL;
+
+ struct us_bun_verify_error_t verify_error = us_internal_verify_error(s);
+ // clear per thread error queue if it may contain something
+ if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL) {
+ ERR_clear_error();
+ }
+
+ // error
+ if(on_handshake != NULL) {
+ on_handshake(s, 0, verify_error, custom_data);
+ }
+ return;
+ } else {
+ context->pending_handshake = 1;
+ context->on_handshake = on_handshake;
+ context->handshake_data = custom_data;
+ // Ensure that we'll cycle through internal openssl's state
+ if (!us_socket_is_closed(0, &s->s) && !us_internal_ssl_socket_is_shut_down(s)) {
+ us_socket_write(1, loop_ssl_data->ssl_socket, "\0", 0, 0);
+ }
+
+ }
+ } else {
+ context->pending_handshake = 0;
+ context->on_handshake = NULL;
+ context->handshake_data = NULL;
+
+
+ struct us_bun_verify_error_t verify_error = us_internal_verify_error(s);
+ // success
+ if(on_handshake != NULL) {
+ on_handshake(s, 1, verify_error, custom_data);
+ }
+ // Ensure that we'll cycle through internal openssl's state
+ if (!us_socket_is_closed(0, &s->s) && !us_internal_ssl_socket_is_shut_down(s)) {
+ us_socket_write(1, loop_ssl_data->ssl_socket, "\0", 0, 0);
+ }
+ }
+
+}
+
+
+struct us_internal_ssl_socket_t *us_internal_ssl_socket_close(struct us_internal_ssl_socket_t *s, int code, void *reason) {
+ struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s);
+ if (context->pending_handshake) {
+ context->pending_handshake = 0;
+ }
+ return (struct us_internal_ssl_socket_t *) us_socket_close(0, (struct us_socket_t *) s, code, reason);
+}
+
+struct us_internal_ssl_socket_t *ssl_on_close(struct us_internal_ssl_socket_t *s, int code, void *reason) {
+ struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s);
+ if (context->pending_handshake) {
+ context->pending_handshake = 0;
+ }
+ SSL_free(s->ssl);
+
+ return context->on_close(s, code, reason);
+}
+
+struct us_internal_ssl_socket_t *ssl_on_end(struct us_internal_ssl_socket_t *s) {
+ if(&s->s) {
+ struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s);
+ if (context && context->pending_handshake) {
+ context->pending_handshake = 0;
+ }
+ }
+ // whatever state we are in, a TCP FIN is always an answered shutdown
+
+ /* Todo: this should report CLEANLY SHUTDOWN as reason */
+ return us_internal_ssl_socket_close(s, 0, NULL);
+}
+
+// this whole function needs a complete clean-up
+struct us_internal_ssl_socket_t *ssl_on_data(struct us_internal_ssl_socket_t *s, void *data, int length) {
+
+ // note: this context can change when we adopt the socket!
+ struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s);
+
+
+ struct us_loop_t *loop = us_socket_context_loop(0, &context->sc);
+ struct loop_ssl_data *loop_ssl_data = (struct loop_ssl_data *) loop->data.ssl_data;
+
+ if(context->pending_handshake) {
+ us_internal_ssl_handshake(s, context->on_handshake, context->handshake_data);
+ }
+
+ // note: if we put data here we should never really clear it (not in write either, it still should be available for SSL_write to read from!)
+ loop_ssl_data->ssl_read_input = data;
+ loop_ssl_data->ssl_read_input_length = length;
+ loop_ssl_data->ssl_read_input_offset = 0;
+ loop_ssl_data->ssl_socket = &s->s;
+ loop_ssl_data->msg_more = 0;
+
+ if (us_socket_is_closed(0, &s->s)) {
+ return s;
+ }
+
+ if (us_internal_ssl_socket_is_shut_down(s)) {
+
+
+ int ret = 0;
+ if ((ret = SSL_shutdown(s->ssl)) == 1) {
+ // two phase shutdown is complete here
+ //printf("Two step SSL shutdown complete\n");
+
+ /* Todo: this should also report some kind of clean shutdown */
+ return us_internal_ssl_socket_close(s, 0, NULL);
+ } else if (ret < 0) {
+
+ int err = SSL_get_error(s->ssl, ret);
+
+ if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL) {
+ // we need to clear the error queue in case these added to the thread local queue
+ ERR_clear_error();
+ }
+
+ }
+
+ // no further processing of data when in shutdown state
+ return s;
+ }
+
+ // bug checking: this loop needs a lot of attention and clean-ups and check-ups
+ int read = 0;
+ restart:
+ while (1) {
+ int just_read = SSL_read(s->ssl, loop_ssl_data->ssl_read_output + LIBUS_RECV_BUFFER_PADDING + read, LIBUS_RECV_BUFFER_LENGTH - read);
+
+ if (just_read <= 0) {
+ int err = SSL_get_error(s->ssl, just_read);
+
+ // as far as I know these are the only errors we want to handle
+ if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) {
+
+ if (err == SSL_ERROR_ZERO_RETURN) {
+ // zero return can be EOF/FIN, if we have data just signal on_data and close
+ if (read) {
+ context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s);
+
+ s = context->on_data(s, loop_ssl_data->ssl_read_output + LIBUS_RECV_BUFFER_PADDING, read);
+ if (us_socket_is_closed(0, &s->s)) {
+ return s;
+ }
+ }
+ // terminate connection here
+ return us_internal_ssl_socket_close(s, 0, NULL);
+ }
+
+ if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL) {
+ // clear per thread error queue if it may contain something
+
+ ERR_clear_error();
+ }
+
+ // terminate connection here
+ return us_internal_ssl_socket_close(s, 0, NULL);
+ } else {
+ // emit the data we have and exit
+
+ if (err == SSL_ERROR_WANT_WRITE) {
+ // here we need to trigger writable event next ssl_read!
+ s->ssl_read_wants_write = 1;
+ }
+
+ // assume we emptied the input buffer fully or error here as well!
+ if (loop_ssl_data->ssl_read_input_length) {
+ return us_internal_ssl_socket_close(s, 0, NULL);
+ }
+
+ // cannot emit zero length to app
+ if (!read) {
+ break;
+ }
+
+ context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s);
+
+ s = context->on_data(s, loop_ssl_data->ssl_read_output + LIBUS_RECV_BUFFER_PADDING, read);
+ if (us_socket_is_closed(0, &s->s)) {
+ return s;
+ }
+
+ break;
+ }
+
+ }
+
+ read += just_read;
+
+ // at this point we might be full and need to emit the data to application and start over
+ if (read == LIBUS_RECV_BUFFER_LENGTH) {
+
+ context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s);
+
+ // emit data and restart
+ s = context->on_data(s, loop_ssl_data->ssl_read_output + LIBUS_RECV_BUFFER_PADDING, read);
+ if (us_socket_is_closed(0, &s->s)) {
+ return s;
+ }
+
+ read = 0;
+ goto restart;
+ }
+ }
+
+ // trigger writable if we failed last write with want read
+ if (s->ssl_write_wants_read) {
+ s->ssl_write_wants_read = 0;
+
+ // make sure to update context before we call (context can change if the user adopts the socket!)
+ context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s);
+
+ s = (struct us_internal_ssl_socket_t *) context->sc.on_writable(&s->s); // cast here!
+ // if we are closed here, then exit
+ if (us_socket_is_closed(0, &s->s)) {
+ return s;
+ }
+ }
+
+ // check this then?
+ if (SSL_get_shutdown(s->ssl) & SSL_RECEIVED_SHUTDOWN) {
+ //printf("SSL_RECEIVED_SHUTDOWN\n");
+
+ //exit(-2);
+
+ // not correct anyways!
+ s = us_internal_ssl_socket_close(s, 0, NULL);
+
+ //us_
+ }
+
+ return s;
+}
+
+struct us_internal_ssl_socket_t *ssl_on_writable(struct us_internal_ssl_socket_t *s) {
+
+ struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s);
+
+ if(context->pending_handshake) {
+ us_internal_ssl_handshake(s, context->on_handshake, context->handshake_data);
+ }
+
+ // todo: cork here so that we efficiently output both from reading and from writing?
+ if (s->ssl_read_wants_write) {
+ s->ssl_read_wants_write = 0;
+
+ // make sure to update context before we call (context can change if the user adopts the socket!)
+ context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s);
+
+ // if this one fails to write data, it sets ssl_read_wants_write again
+ s = (struct us_internal_ssl_socket_t *) context->sc.on_data(&s->s, 0, 0); // cast here!
+ }
+
+ // should this one come before we have read? should it come always? spurious on_writable is okay
+ s = context->on_writable(s);
+
+ return s;
+}
+
+/* Lazily inits loop ssl data first time */
+void us_internal_init_loop_ssl_data(struct us_loop_t *loop) {
+ if (!loop->data.ssl_data) {
+ struct loop_ssl_data *loop_ssl_data = us_malloc(sizeof(struct loop_ssl_data));
+ loop_ssl_data->ssl_read_input_length = 0;
+ loop_ssl_data->ssl_read_input_offset = 0;
+ loop_ssl_data->last_write_was_msg_more = 0;
+ loop_ssl_data->msg_more = 0;
+
+ loop_ssl_data->ssl_read_output = us_malloc(LIBUS_RECV_BUFFER_LENGTH + LIBUS_RECV_BUFFER_PADDING * 2);
+
+ OPENSSL_init_ssl(0, NULL);
+
+ loop_ssl_data->shared_biom = BIO_meth_new(BIO_TYPE_MEM, "µS BIO");
+ BIO_meth_set_create(loop_ssl_data->shared_biom, BIO_s_custom_create);
+ BIO_meth_set_write(loop_ssl_data->shared_biom, BIO_s_custom_write);
+ BIO_meth_set_read(loop_ssl_data->shared_biom, BIO_s_custom_read);
+ BIO_meth_set_ctrl(loop_ssl_data->shared_biom, BIO_s_custom_ctrl);
+
+ loop_ssl_data->shared_rbio = BIO_new(loop_ssl_data->shared_biom);
+ loop_ssl_data->shared_wbio = BIO_new(loop_ssl_data->shared_biom);
+ BIO_set_data(loop_ssl_data->shared_rbio, loop_ssl_data);
+ BIO_set_data(loop_ssl_data->shared_wbio, loop_ssl_data);
+
+ loop->data.ssl_data = loop_ssl_data;
+ }
+}
+
+/* Called by loop free, clears any loop ssl data */
+void us_internal_free_loop_ssl_data(struct us_loop_t *loop) {
+ struct loop_ssl_data *loop_ssl_data = (struct loop_ssl_data *) loop->data.ssl_data;
+
+ if (loop_ssl_data) {
+ us_free(loop_ssl_data->ssl_read_output);
+
+ BIO_free(loop_ssl_data->shared_rbio);
+ BIO_free(loop_ssl_data->shared_wbio);
+
+ BIO_meth_free(loop_ssl_data->shared_biom);
+
+ us_free(loop_ssl_data);
+ }
+}
+
+// we throttle reading data for ssl sockets that are in init state. here we actually use
+// the kernel buffering to our advantage
+int ssl_is_low_prio(struct us_internal_ssl_socket_t *s) {
+ /* We use SSL_in_before() instead of SSL_in_init(), because only the first step is CPU intensive, and we want to
+ * speed up the rest of connection establishing if the CPU intensive work is already done, so fully established
+ * connections increase lineary over time under high load */
+ return SSL_in_init(s->ssl);
+}
+
+/* Per-context functions */
+void *us_internal_ssl_socket_context_get_native_handle(struct us_internal_ssl_socket_context_t *context) {
+ return context->ssl_context;
+}
+
+struct us_internal_ssl_socket_context_t *us_internal_create_child_ssl_socket_context(struct us_internal_ssl_socket_context_t *context, int context_ext_size) {
+ /* Create a new non-SSL context */
+ struct us_socket_context_options_t options = {0};
+ struct us_internal_ssl_socket_context_t *child_context = (struct us_internal_ssl_socket_context_t *) us_create_socket_context(0, context->sc.loop, sizeof(struct us_internal_ssl_socket_context_t) + context_ext_size, options);
+
+ /* The only thing we share is SSL_CTX */
+ child_context->ssl_context = context->ssl_context;
+ child_context->is_parent = 0;
+
+ return child_context;
+}
+
+/* Common function for creating a context from options.
+ * We must NOT free a SSL_CTX with only SSL_CTX_free! Also free any password */
+void free_ssl_context(SSL_CTX *ssl_context) {
+ if (!ssl_context) {
+ return;
+ }
+
+ /* If we have set a password string, free it here */
+ void *password = SSL_CTX_get_default_passwd_cb_userdata(ssl_context);
+ /* OpenSSL returns NULL if we have no set password */
+ us_free(password);
+
+ SSL_CTX_free(ssl_context);
+}
+
+
+
+// This callback is used to avoid the default passphrase callback in OpenSSL
+// which will typically prompt for the passphrase. The prompting is designed
+// for the OpenSSL CLI, but works poorly for this case because it involves
+// synchronous interaction with the controlling terminal, something we never
+// want, and use this function to avoid it.
+int us_no_password_callback(char* buf, int size, int rwflag, void* u) {
+ return 0;
+}
+
+
+static X509 * us_ssl_ctx_get_X509_without_callback_from(struct us_cert_string_t content) {
+ X509 *x = NULL;
+ BIO *in;
+
+ ERR_clear_error(); // clear error stack for SSL_CTX_use_certificate()
+
+ in = BIO_new_mem_buf(content.str, content.len);
+ if (in == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
+ goto end;
+ }
+
+ x = PEM_read_bio_X509(in, NULL, us_no_password_callback, NULL);
+ if (x == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_PEM_LIB);
+ goto end;
+ }
+
+ return x;
+
+end:
+ X509_free(x);
+ BIO_free(in);
+ return NULL;
+}
+
+int us_internal_raw_root_certs(struct us_cert_string_t** out) {
+ *out = root_certs;
+ return root_certs_size;
+}
+
+void us_internal_init_root_certs() {
+ if(atomic_load(&root_cert_instances_initialized) == 1) return;
+
+ while(atomic_flag_test_and_set_explicit(&root_cert_instances_lock, memory_order_acquire));
+
+ // if some thread already created it after we acquired the lock we skip and release the lock
+ if(atomic_load(&root_cert_instances_initialized) == 0) {
+ for (size_t i = 0; i < root_certs_size; i++) {
+ root_cert_instances[i] = us_ssl_ctx_get_X509_without_callback_from(root_certs[i]);
+ }
+
+ atomic_store(&root_cert_instances_initialized, 1);
+ }
+
+ atomic_flag_clear_explicit(&root_cert_instances_lock, memory_order_release);
+}
+
+X509_STORE* us_get_default_ca_store() {
+ X509_STORE *store = X509_STORE_new();
+ if (store == NULL) {
+ return NULL;
+ }
+
+ if (!X509_STORE_set_default_paths(store)) {
+ X509_STORE_free(store);
+ return NULL;
+ }
+
+ us_internal_init_root_certs();
+
+ // load all root_cert_instances on the default ca store
+ for (size_t i = 0; i < root_certs_size; i++) {
+ X509* cert = root_cert_instances[i];
+ if(cert == NULL) continue;
+ X509_up_ref(cert);
+ X509_STORE_add_cert(store, cert);
+ }
+
+ return store;
+}
+
+
+/* This function should take any options and return SSL_CTX - which has to be free'd with
+ * our destructor function - free_ssl_context() */
+SSL_CTX *create_ssl_context_from_options(struct us_socket_context_options_t options) {
+ /* Create the context */
+ SSL_CTX *ssl_context = SSL_CTX_new(TLS_method());
+
+ /* Default options we rely on - changing these will break our logic */
+ SSL_CTX_set_read_ahead(ssl_context, 1);
+ SSL_CTX_set_mode(ssl_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+
+ /* Anything below TLS 1.2 is disabled */
+ SSL_CTX_set_min_proto_version(ssl_context, TLS1_2_VERSION);
+
+ /* The following are helpers. You may easily implement whatever you want by using the native handle directly */
+
+ /* Important option for lowering memory usage, but lowers performance slightly */
+ if (options.ssl_prefer_low_memory_usage) {
+ SSL_CTX_set_mode(ssl_context, SSL_MODE_RELEASE_BUFFERS);
+ }
+
+ if (options.passphrase) {
+ /* When freeing the CTX we need to check SSL_CTX_get_default_passwd_cb_userdata and
+ * free it if set */
+ SSL_CTX_set_default_passwd_cb_userdata(ssl_context, (void *) strdup(options.passphrase));
+ SSL_CTX_set_default_passwd_cb(ssl_context, passphrase_cb);
+ }
+
+ /* This one most probably do not need the cert_file_name string to be kept alive */
+ if (options.cert_file_name) {
+ if (SSL_CTX_use_certificate_chain_file(ssl_context, options.cert_file_name) != 1) {
+ free_ssl_context(ssl_context);
+ return NULL;
+ }
+ }
+
+ /* Same as above - we can discard this string afterwards I suppose */
+ if (options.key_file_name) {
+ if (SSL_CTX_use_PrivateKey_file(ssl_context, options.key_file_name, SSL_FILETYPE_PEM) != 1) {
+ free_ssl_context(ssl_context);
+ return NULL;
+ }
+ }
+
+ if (options.ca_file_name) {
+ STACK_OF(X509_NAME) *ca_list;
+ ca_list = SSL_load_client_CA_file(options.ca_file_name);
+ if(ca_list == NULL) {
+ free_ssl_context(ssl_context);
+ return NULL;
+ }
+ SSL_CTX_set_client_CA_list(ssl_context, ca_list);
+ if (SSL_CTX_load_verify_locations(ssl_context, options.ca_file_name, NULL) != 1) {
+ free_ssl_context(ssl_context);
+ return NULL;
+ }
+ SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER, NULL);
+ }
+
+ if (options.dh_params_file_name) {
+ /* Set up ephemeral DH parameters. */
+ DH *dh_2048 = NULL;
+ FILE *paramfile;
+ paramfile = fopen(options.dh_params_file_name, "r");
+
+ if (paramfile) {
+ dh_2048 = PEM_read_DHparams(paramfile, NULL, NULL, NULL);
+ fclose(paramfile);
+ } else {
+ free_ssl_context(ssl_context);
+ return NULL;
+ }
+
+ if (dh_2048 == NULL) {
+ free_ssl_context(ssl_context);
+ return NULL;
+ }
+
+ const long set_tmp_dh = SSL_CTX_set_tmp_dh(ssl_context, dh_2048);
+ DH_free(dh_2048);
+
+ if (set_tmp_dh != 1) {
+ free_ssl_context(ssl_context);
+ return NULL;
+ }
+
+ /* OWASP Cipher String 'A+' (https://www.owasp.org/index.php/TLS_Cipher_String_Cheat_Sheet) */
+ if (SSL_CTX_set_cipher_list(ssl_context, "DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256") != 1) {
+ free_ssl_context(ssl_context);
+ return NULL;
+ }
+ }
+
+ if (options.ssl_ciphers) {
+ if (SSL_CTX_set_cipher_list(ssl_context, options.ssl_ciphers) != 1) {
+ free_ssl_context(ssl_context);
+ return NULL;
+ }
+ }
+
+ /* This must be free'd with free_ssl_context, not SSL_CTX_free */
+ return ssl_context;
+}
+
+
+
+int us_ssl_ctx_use_privatekey_content(SSL_CTX *ctx, const char *content, int type) {
+ int reason_code, ret = 0;
+ BIO *in;
+ EVP_PKEY *pkey = NULL;
+ in = BIO_new_mem_buf(content, strlen(content));
+ if (in == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
+ goto end;
+ }
+
+ if (type == SSL_FILETYPE_PEM) {
+ reason_code = ERR_R_PEM_LIB;
+ pkey = PEM_read_bio_PrivateKey(in, NULL, SSL_CTX_get_default_passwd_cb(ctx),
+ SSL_CTX_get_default_passwd_cb_userdata(ctx));
+ } else if (type == SSL_FILETYPE_ASN1) {
+ reason_code = ERR_R_ASN1_LIB;
+ pkey = d2i_PrivateKey_bio(in, NULL);
+ } else {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SSL_FILETYPE);
+ goto end;
+ }
+
+ if (pkey == NULL) {
+ OPENSSL_PUT_ERROR(SSL, reason_code);
+ goto end;
+ }
+ ret = SSL_CTX_use_PrivateKey(ctx, pkey);
+ EVP_PKEY_free(pkey);
+
+end:
+ BIO_free(in);
+ return ret;
+}
+
+X509 * us_ssl_ctx_get_X509_from(SSL_CTX *ctx, const char *content) {
+ X509 *x = NULL;
+ BIO *in;
+
+ ERR_clear_error(); // clear error stack for SSL_CTX_use_certificate()
+
+ in = BIO_new_mem_buf(content, strlen(content));
+ if (in == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
+ goto end;
+ }
+
+ x = PEM_read_bio_X509(in, NULL, SSL_CTX_get_default_passwd_cb(ctx),
+ SSL_CTX_get_default_passwd_cb_userdata(ctx));
+ if (x == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_PEM_LIB);
+ goto end;
+ }
+
+ return x;
+
+end:
+ X509_free(x);
+ BIO_free(in);
+ return NULL;
+}
+
+int us_ssl_ctx_use_certificate_chain(SSL_CTX *ctx, const char *content) {
+ BIO *in;
+ int ret = 0;
+ X509 *x = NULL;
+
+ ERR_clear_error(); // clear error stack for SSL_CTX_use_certificate()
+
+ in = BIO_new_mem_buf(content, strlen(content));
+ if (in == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
+ goto end;
+ }
+
+ x = PEM_read_bio_X509_AUX(in, NULL, SSL_CTX_get_default_passwd_cb(ctx),
+ SSL_CTX_get_default_passwd_cb_userdata(ctx));
+ if (x == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_PEM_LIB);
+ goto end;
+ }
+
+ ret = SSL_CTX_use_certificate(ctx, x);
+
+ if (ERR_peek_error() != 0) {
+ ret = 0; // Key/certificate mismatch doesn't imply ret==0 ...
+ }
+
+ if (ret) {
+ // If we could set up our certificate, now proceed to the CA
+ // certificates.
+ X509 *ca;
+ int r;
+ uint32_t err;
+
+ SSL_CTX_clear_chain_certs(ctx);
+
+ while ((ca = PEM_read_bio_X509(in, NULL, SSL_CTX_get_default_passwd_cb(ctx),
+ SSL_CTX_get_default_passwd_cb_userdata(ctx))) !=
+ NULL) {
+ r = SSL_CTX_add0_chain_cert(ctx, ca);
+ if (!r) {
+ X509_free(ca);
+ ret = 0;
+ goto end;
+ }
+ // Note that we must not free r if it was successfully added to the chain
+ // (while we must free the main certificate, since its reference count is
+ // increased by SSL_CTX_use_certificate).
+ }
+
+ // When the while loop ends, it's usually just EOF.
+ err = ERR_peek_last_error();
+ if (ERR_GET_LIB(err) == ERR_LIB_PEM &&
+ ERR_GET_REASON(err) == PEM_R_NO_START_LINE) {
+ ERR_clear_error();
+ } else {
+ ret = 0; // some real error
+ }
+ }
+
+end:
+ X509_free(x);
+ BIO_free(in);
+ return ret;
+}
+
+const char* us_X509_error_code(long err) { // NOLINT(runtime/int)
+ const char* code = "UNSPECIFIED";
+#define CASE_X509_ERR(CODE) case X509_V_ERR_##CODE: code = #CODE; break;
+ switch (err) {
+ // if you modify anything in here, *please* update the respective section in
+ // doc/api/tls.md as well
+ CASE_X509_ERR(UNABLE_TO_GET_ISSUER_CERT)
+ CASE_X509_ERR(UNABLE_TO_GET_CRL)
+ CASE_X509_ERR(UNABLE_TO_DECRYPT_CERT_SIGNATURE)
+ CASE_X509_ERR(UNABLE_TO_DECRYPT_CRL_SIGNATURE)
+ CASE_X509_ERR(UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY)
+ CASE_X509_ERR(CERT_SIGNATURE_FAILURE)
+ CASE_X509_ERR(CRL_SIGNATURE_FAILURE)
+ CASE_X509_ERR(CERT_NOT_YET_VALID)
+ CASE_X509_ERR(CERT_HAS_EXPIRED)
+ CASE_X509_ERR(CRL_NOT_YET_VALID)
+ CASE_X509_ERR(CRL_HAS_EXPIRED)
+ CASE_X509_ERR(ERROR_IN_CERT_NOT_BEFORE_FIELD)
+ CASE_X509_ERR(ERROR_IN_CERT_NOT_AFTER_FIELD)
+ CASE_X509_ERR(ERROR_IN_CRL_LAST_UPDATE_FIELD)
+ CASE_X509_ERR(ERROR_IN_CRL_NEXT_UPDATE_FIELD)
+ CASE_X509_ERR(OUT_OF_MEM)
+ CASE_X509_ERR(DEPTH_ZERO_SELF_SIGNED_CERT)
+ CASE_X509_ERR(SELF_SIGNED_CERT_IN_CHAIN)
+ CASE_X509_ERR(UNABLE_TO_GET_ISSUER_CERT_LOCALLY)
+ CASE_X509_ERR(UNABLE_TO_VERIFY_LEAF_SIGNATURE)
+ CASE_X509_ERR(CERT_CHAIN_TOO_LONG)
+ CASE_X509_ERR(CERT_REVOKED)
+ CASE_X509_ERR(INVALID_CA)
+ CASE_X509_ERR(PATH_LENGTH_EXCEEDED)
+ CASE_X509_ERR(INVALID_PURPOSE)
+ CASE_X509_ERR(CERT_UNTRUSTED)
+ CASE_X509_ERR(CERT_REJECTED)
+ CASE_X509_ERR(HOSTNAME_MISMATCH)
+ }
+#undef CASE_X509_ERR
+ return code;
+}
+
+long us_internal_verify_peer_certificate( // NOLINT(runtime/int)
+ const SSL* ssl,
+ long def) { // NOLINT(runtime/int)
+ long err = def; // NOLINT(runtime/int)
+ X509* peer_cert = SSL_get_peer_certificate(ssl);
+ if (peer_cert) {
+ X509_free(peer_cert);
+ err = SSL_get_verify_result(ssl);
+ } else {
+ const SSL_CIPHER* curr_cipher = SSL_get_current_cipher(ssl);
+
+ const SSL_SESSION* sess = SSL_get_session(ssl);
+ // Allow no-cert for PSK authentication in TLS1.2 and lower.
+ // In TLS1.3 check that session was reused because TLS1.3 PSK
+ // looks like session resumption.
+ if ((curr_cipher && SSL_CIPHER_get_auth_nid(curr_cipher) == NID_auth_psk) ||
+ (sess && SSL_SESSION_get_protocol_version(sess) == TLS1_3_VERSION &&
+ SSL_session_reused(ssl))) {
+ return X509_V_OK;
+ }
+ }
+ return err;
+}
+
+
+struct us_bun_verify_error_t us_internal_verify_error(struct us_internal_ssl_socket_t *s) {
+
+ if (us_socket_is_closed(0, &s->s) || us_internal_ssl_socket_is_shut_down(s)) {
+ return (struct us_bun_verify_error_t) { .error = 0, .code = NULL, .reason = NULL };
+ }
+
+ SSL* ssl = s->ssl;
+ long x509_verify_error = // NOLINT(runtime/int)
+ us_internal_verify_peer_certificate(
+ ssl,
+ X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT);
+
+ if (x509_verify_error == X509_V_OK)
+ return (struct us_bun_verify_error_t) { .error = x509_verify_error, .code = NULL, .reason = NULL };
+
+ const char* reason = X509_verify_cert_error_string(x509_verify_error);
+ const char* code = us_X509_error_code(x509_verify_error);
+
+ return (struct us_bun_verify_error_t) { .error = x509_verify_error, .code = code, .reason = reason };
+}
+
+int us_verify_callback(int preverify_ok, X509_STORE_CTX* ctx) {
+ // From https://www.openssl.org/docs/man1.1.1/man3/SSL_verify_cb:
+ //
+ // If VerifyCallback returns 1, the verification process is continued. If
+ // VerifyCallback always returns 1, the TLS/SSL handshake will not be
+ // terminated with respect to verification failures and the connection will
+ // be established. The calling process can however retrieve the error code
+ // of the last verification error using SSL_get_verify_result(3) or by
+ // maintaining its own error storage managed by VerifyCallback.
+ //
+ // Since we cannot perform I/O quickly enough with X509_STORE_CTX_ APIs in
+ // this callback, we ignore all preverify_ok errors and let the handshake
+ // continue. It is imperative that the user use Connection::VerifyError after
+ // the 'secure' callback has been made.
+ return 1;
+}
+
+SSL_CTX *create_ssl_context_from_bun_options(struct us_bun_socket_context_options_t options) {
+ /* Create the context */
+ SSL_CTX *ssl_context = SSL_CTX_new(TLS_method());
+
+ /* Default options we rely on - changing these will break our logic */
+ SSL_CTX_set_read_ahead(ssl_context, 1);
+ SSL_CTX_set_mode(ssl_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+
+ /* Anything below TLS 1.2 is disabled */
+ SSL_CTX_set_min_proto_version(ssl_context, TLS1_2_VERSION);
+
+ /* The following are helpers. You may easily implement whatever you want by using the native handle directly */
+
+ /* Important option for lowering memory usage, but lowers performance slightly */
+ if (options.ssl_prefer_low_memory_usage) {
+ SSL_CTX_set_mode(ssl_context, SSL_MODE_RELEASE_BUFFERS);
+ }
+
+
+ if (options.passphrase) {
+ /* When freeing the CTX we need to check SSL_CTX_get_default_passwd_cb_userdata and
+ * free it if set */
+ SSL_CTX_set_default_passwd_cb_userdata(ssl_context, (void *) strdup(options.passphrase));
+ SSL_CTX_set_default_passwd_cb(ssl_context, passphrase_cb);
+ }
+
+ /* This one most probably do not need the cert_file_name string to be kept alive */
+ if (options.cert_file_name) {
+ if (SSL_CTX_use_certificate_chain_file(ssl_context, options.cert_file_name) != 1) {
+ free_ssl_context(ssl_context);
+ return NULL;
+ }
+ } else if (options.cert && options.cert_count > 0) {
+ for(unsigned int i = 0; i < options.cert_count; i++) {
+ if (us_ssl_ctx_use_certificate_chain(ssl_context, options.cert[i]) != 1) {
+ free_ssl_context(ssl_context);
+ return NULL;
+ }
+ }
+ }
+
+ /* Same as above - we can discard this string afterwards I suppose */
+ if (options.key_file_name) {
+ if (SSL_CTX_use_PrivateKey_file(ssl_context, options.key_file_name, SSL_FILETYPE_PEM) != 1) {
+ free_ssl_context(ssl_context);
+ return NULL;
+ }
+ } else if (options.key && options.key_count > 0) {
+ for(unsigned int i = 0; i < options.key_count; i++){
+ if (us_ssl_ctx_use_privatekey_content(ssl_context, options.key[i], SSL_FILETYPE_PEM) != 1) {
+ free_ssl_context(ssl_context);
+ return NULL;
+ }
+ }
+ }
+
+ if (options.ca_file_name) {
+ SSL_CTX_set_cert_store(ssl_context, us_get_default_ca_store());
+
+ STACK_OF(X509_NAME) *ca_list;
+ ca_list = SSL_load_client_CA_file(options.ca_file_name);
+ if(ca_list == NULL) {
+ free_ssl_context(ssl_context);
+ return NULL;
+ }
+
+ SSL_CTX_set_client_CA_list(ssl_context, ca_list);
+ if (SSL_CTX_load_verify_locations(ssl_context, options.ca_file_name, NULL) != 1) {
+ free_ssl_context(ssl_context);
+ return NULL;
+ }
+
+ if(options.reject_unauthorized) {
+ SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, us_verify_callback);
+ } else {
+ SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER, us_verify_callback);
+ }
+
+ }else if (options.ca && options.ca_count > 0) {
+ X509_STORE* cert_store = NULL;
+
+ for(unsigned int i = 0; i < options.ca_count; i++){
+ X509* ca_cert = us_ssl_ctx_get_X509_from(ssl_context, options.ca[i]);
+ if (ca_cert == NULL){
+ free_ssl_context(ssl_context);
+ return NULL;
+ }
+
+ if (cert_store == NULL) {
+ cert_store = us_get_default_ca_store();
+ SSL_CTX_set_cert_store(ssl_context, cert_store);
+ }
+
+ X509_STORE_add_cert(cert_store, ca_cert);
+ if(!SSL_CTX_add_client_CA(ssl_context, ca_cert)){
+ free_ssl_context(ssl_context);
+ return NULL;
+ }
+ if(options.reject_unauthorized) {
+ SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, us_verify_callback);
+ } else {
+ SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER, us_verify_callback);
+ }
+ }
+ } else {
+ if(options.request_cert) {
+ SSL_CTX_set_cert_store(ssl_context, us_get_default_ca_store());
+
+ if(options.reject_unauthorized) {
+ SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, us_verify_callback);
+ } else {
+ SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER, us_verify_callback);
+ }
+ }
+ }
+ if (options.dh_params_file_name) {
+ /* Set up ephemeral DH parameters. */
+ DH *dh_2048 = NULL;
+ FILE *paramfile;
+ paramfile = fopen(options.dh_params_file_name, "r");
+
+ if (paramfile) {
+ dh_2048 = PEM_read_DHparams(paramfile, NULL, NULL, NULL);
+ fclose(paramfile);
+ } else {
+ free_ssl_context(ssl_context);
+ return NULL;
+ }
+
+ if (dh_2048 == NULL) {
+ free_ssl_context(ssl_context);
+ return NULL;
+ }
+
+ const long set_tmp_dh = SSL_CTX_set_tmp_dh(ssl_context, dh_2048);
+ DH_free(dh_2048);
+
+ if (set_tmp_dh != 1) {
+ free_ssl_context(ssl_context);
+ return NULL;
+ }
+
+ /* OWASP Cipher String 'A+' (https://www.owasp.org/index.php/TLS_Cipher_String_Cheat_Sheet) */
+ if (SSL_CTX_set_cipher_list(ssl_context, "DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256") != 1) {
+ free_ssl_context(ssl_context);
+ return NULL;
+ }
+ }
+
+ if (options.ssl_ciphers) {
+ if (SSL_CTX_set_cipher_list(ssl_context, options.ssl_ciphers) != 1) {
+ free_ssl_context(ssl_context);
+ return NULL;
+ }
+ }
+
+ if (options.secure_options) {
+ SSL_CTX_set_options(ssl_context, options.secure_options);
+ }
+
+ /* This must be free'd with free_ssl_context, not SSL_CTX_free */
+ return ssl_context;
+}
+
+/* Returns a servername's userdata if any */
+void *us_internal_ssl_socket_context_find_server_name_userdata(struct us_internal_ssl_socket_context_t *context, const char *hostname_pattern) {
+
+ /* We can use sni_find because looking up a "wildcard pattern" will match the exact literal "wildcard pattern" first,
+ * before it matches by the very wildcard itself, so it works fine (exact match is the only thing we care for here) */
+ SSL_CTX *ssl_context = sni_find(context->sni, hostname_pattern);
+
+ if (ssl_context) {
+ return SSL_CTX_get_ex_data(ssl_context, 0);
+ }
+
+ return 0;
+}
+
+/* Returns either nullptr or the previously set user data attached to this SSL's selected SNI context */
+void *us_internal_ssl_socket_get_sni_userdata(struct us_internal_ssl_socket_t *s) {
+ return SSL_CTX_get_ex_data(SSL_get_SSL_CTX(s->ssl), 0);
+}
+
+/* Todo: return error on failure? */
+void us_internal_ssl_socket_context_add_server_name(struct us_internal_ssl_socket_context_t *context, const char *hostname_pattern, struct us_socket_context_options_t options, void *user) {
+
+ /* Try and construct an SSL_CTX from options */
+ SSL_CTX *ssl_context = create_ssl_context_from_options(options);
+
+ /* Attach the user data to this context */
+ if (1 != SSL_CTX_set_ex_data(ssl_context, 0, user)) {
+ printf("CANNOT SET EX DATA!\n");
+ }
+
+ /* We do not want to hold any nullptr's in our SNI tree */
+ if (ssl_context) {
+ if (sni_add(context->sni, hostname_pattern, ssl_context)) {
+ /* If we already had that name, ignore */
+ free_ssl_context(ssl_context);
+ }
+ }
+}
+
+void us_bun_internal_ssl_socket_context_add_server_name(struct us_internal_ssl_socket_context_t *context, const char *hostname_pattern, struct us_bun_socket_context_options_t options, void *user) {
+
+ /* Try and construct an SSL_CTX from options */
+ SSL_CTX *ssl_context = create_ssl_context_from_bun_options(options);
+
+ /* Attach the user data to this context */
+ if (1 != SSL_CTX_set_ex_data(ssl_context, 0, user)) {
+ printf("CANNOT SET EX DATA!\n");
+ }
+
+ /* We do not want to hold any nullptr's in our SNI tree */
+ if (ssl_context) {
+ if (sni_add(context->sni, hostname_pattern, ssl_context)) {
+ /* If we already had that name, ignore */
+ free_ssl_context(ssl_context);
+ }
+ }
+}
+
+void us_internal_ssl_socket_context_on_server_name(struct us_internal_ssl_socket_context_t *context, void (*cb)(struct us_internal_ssl_socket_context_t *, const char *hostname)) {
+ context->on_server_name = cb;
+}
+
+void us_internal_ssl_socket_context_remove_server_name(struct us_internal_ssl_socket_context_t *context, const char *hostname_pattern) {
+
+ /* The same thing must happen for sni_free, that's why we have a callback */
+ SSL_CTX *sni_node_ssl_context = (SSL_CTX *) sni_remove(context->sni, hostname_pattern);
+ free_ssl_context(sni_node_ssl_context);
+}
+
+/* Returns NULL or SSL_CTX. May call missing server name callback */
+SSL_CTX *resolve_context(struct us_internal_ssl_socket_context_t *context, const char *hostname) {
+
+ /* Try once first */
+ void *user = sni_find(context->sni, hostname);
+ if (!user) {
+ /* Emit missing hostname then try again */
+ if (!context->on_server_name) {
+ /* We have no callback registered, so fail */
+ return NULL;
+ }
+
+ context->on_server_name(context, hostname);
+
+ /* Last try */
+ user = sni_find(context->sni, hostname);
+ }
+
+ return user;
+}
+
+// arg is context
+int sni_cb(SSL *ssl, int *al, void *arg) {
+
+ if (ssl) {
+ const char *hostname = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+ if (hostname && hostname[0]) {
+ /* Try and resolve (match) required hostname with what we have registered */
+ SSL_CTX *resolved_ssl_context = resolve_context((struct us_internal_ssl_socket_context_t *) arg, hostname);
+ if (resolved_ssl_context) {
+ //printf("Did find matching SNI context for hostname: <%s>!\n", hostname);
+ SSL_set_SSL_CTX(ssl, resolved_ssl_context);
+ } else {
+ /* Call a blocking callback notifying of missing context */
+ }
+
+ }
+
+ return SSL_TLSEXT_ERR_OK;
+ }
+
+ /* Can we even come here ever? */
+ return SSL_TLSEXT_ERR_NOACK;
+}
+
+struct us_internal_ssl_socket_context_t *us_internal_create_ssl_socket_context(struct us_loop_t *loop, int context_ext_size, struct us_socket_context_options_t options) {
+ /* If we haven't initialized the loop data yet, do so .
+ * This is needed because loop data holds shared OpenSSL data and
+ * the function is also responsible for initializing OpenSSL */
+ us_internal_init_loop_ssl_data(loop);
+
+ /* First of all we try and create the SSL context from options */
+ SSL_CTX *ssl_context = create_ssl_context_from_options(options);
+ if (!ssl_context) {
+ /* We simply fail early if we cannot even create the OpenSSL context */
+ return NULL;
+ }
+
+ /* Otherwise ee continue by creating a non-SSL context, but with larger ext to hold our SSL stuff */
+ struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_create_socket_context(0, loop, sizeof(struct us_internal_ssl_socket_context_t) + context_ext_size, options);
+
+ /* I guess this is the only optional callback */
+ context->on_server_name = NULL;
+
+ /* Then we extend its SSL parts */
+ context->ssl_context = ssl_context;//create_ssl_context_from_options(options);
+ context->is_parent = 1;
+
+ context->pending_handshake = 0;
+ context->on_handshake = NULL;
+ context->handshake_data = NULL;
+
+ /* We, as parent context, may ignore data */
+ context->sc.is_low_prio = (int (*)(struct us_socket_t *)) ssl_is_low_prio;
+
+ /* Parent contexts may use SNI */
+ SSL_CTX_set_tlsext_servername_callback(context->ssl_context, sni_cb);
+ SSL_CTX_set_tlsext_servername_arg(context->ssl_context, context);
+
+ /* Also create the SNI tree */
+ context->sni = sni_new();
+
+ return context;
+}
+struct us_internal_ssl_socket_context_t *us_internal_bun_create_ssl_socket_context(struct us_loop_t *loop, int context_ext_size, struct us_bun_socket_context_options_t options) {
+ /* If we haven't initialized the loop data yet, do so .
+ * This is needed because loop data holds shared OpenSSL data and
+ * the function is also responsible for initializing OpenSSL */
+ us_internal_init_loop_ssl_data(loop);
+
+ /* First of all we try and create the SSL context from options */
+ SSL_CTX *ssl_context = create_ssl_context_from_bun_options(options);
+ if (!ssl_context) {
+ /* We simply fail early if we cannot even create the OpenSSL context */
+ return NULL;
+ }
+
+ /* Otherwise ee continue by creating a non-SSL context, but with larger ext to hold our SSL stuff */
+ struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_create_bun_socket_context(0, loop, sizeof(struct us_internal_ssl_socket_context_t) + context_ext_size, options);
+
+ /* I guess this is the only optional callback */
+ context->on_server_name = NULL;
+
+ /* Then we extend its SSL parts */
+ context->ssl_context = ssl_context;//create_ssl_context_from_options(options);
+ context->is_parent = 1;
+ context->pending_handshake = 0;
+ context->on_handshake = NULL;
+ context->handshake_data = NULL;
+
+ /* We, as parent context, may ignore data */
+ context->sc.is_low_prio = (int (*)(struct us_socket_t *)) ssl_is_low_prio;
+
+ /* Parent contexts may use SNI */
+ SSL_CTX_set_tlsext_servername_callback(context->ssl_context, sni_cb);
+ SSL_CTX_set_tlsext_servername_arg(context->ssl_context, context);
+
+ /* Also create the SNI tree */
+ context->sni = sni_new();
+
+ return context;
+}
+
+/* Our destructor for hostnames, used below */
+void sni_hostname_destructor(void *user) {
+ /* Some nodes hold null, so this one must ignore this case */
+ free_ssl_context((SSL_CTX *) user);
+}
+
+void us_internal_ssl_socket_context_free(struct us_internal_ssl_socket_context_t *context) {
+ /* If we are parent then we need to free our OpenSSL context */
+ if (context->is_parent) {
+ free_ssl_context(context->ssl_context);
+
+ /* Here we need to register a temporary callback for all still-existing hostnames
+ * and their contexts. Only parents have an SNI tree */
+ sni_free(context->sni, sni_hostname_destructor);
+ }
+
+ us_socket_context_free(0, &context->sc);
+}
+
+struct us_listen_socket_t *us_internal_ssl_socket_context_listen(struct us_internal_ssl_socket_context_t *context, const char *host, int port, int options, int socket_ext_size) {
+ return us_socket_context_listen(0, &context->sc, host, port, options, sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) + socket_ext_size);
+}
+
+struct us_listen_socket_t *us_internal_ssl_socket_context_listen_unix(struct us_internal_ssl_socket_context_t *context, const char *path, int options, int socket_ext_size) {
+ return us_socket_context_listen_unix(0, &context->sc, path, options, sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) + socket_ext_size);
+}
+
+struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_connect(struct us_internal_ssl_socket_context_t *context, const char *host, int port, const char *source_host, int options, int socket_ext_size) {
+ return (struct us_internal_ssl_socket_t *) us_socket_context_connect(0, &context->sc, host, port, source_host, options, sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) + socket_ext_size);
+}
+
+struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_connect_unix(struct us_internal_ssl_socket_context_t *context, const char *server_path, int options, int socket_ext_size) {
+ return (struct us_internal_ssl_socket_t *) us_socket_context_connect_unix(0, &context->sc, server_path, options, sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) + socket_ext_size);
+}
+
+void us_internal_ssl_socket_context_on_open(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *(*on_open)(struct us_internal_ssl_socket_t *s, int is_client, char *ip, int ip_length)) {
+ us_socket_context_on_open(0, &context->sc, (struct us_socket_t *(*)(struct us_socket_t *, int, char *, int)) ssl_on_open);
+ context->on_open = on_open;
+}
+
+void us_internal_ssl_socket_context_on_close(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *(*on_close)(struct us_internal_ssl_socket_t *s, int code, void *reason)) {
+ us_socket_context_on_close(0, (struct us_socket_context_t *) context, (struct us_socket_t *(*)(struct us_socket_t *, int, void *)) ssl_on_close);
+ context->on_close = on_close;
+}
+
+void us_internal_ssl_socket_context_on_data(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *(*on_data)(struct us_internal_ssl_socket_t *s, char *data, int length)) {
+ us_socket_context_on_data(0, (struct us_socket_context_t *) context, (struct us_socket_t *(*)(struct us_socket_t *, char *, int)) ssl_on_data);
+ context->on_data = on_data;
+}
+
+void us_internal_ssl_socket_context_on_writable(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *(*on_writable)(struct us_internal_ssl_socket_t *s)) {
+ us_socket_context_on_writable(0, (struct us_socket_context_t *) context, (struct us_socket_t *(*)(struct us_socket_t *)) ssl_on_writable);
+ context->on_writable = on_writable;
+}
+
+void us_internal_ssl_socket_context_on_timeout(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *(*on_timeout)(struct us_internal_ssl_socket_t *s)) {
+ us_socket_context_on_timeout(0, (struct us_socket_context_t *) context, (struct us_socket_t *(*)(struct us_socket_t *)) on_timeout);
+}
+
+void us_internal_ssl_socket_context_on_long_timeout(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *(*on_long_timeout)(struct us_internal_ssl_socket_t *s)) {
+ us_socket_context_on_long_timeout(0, (struct us_socket_context_t *) context, (struct us_socket_t *(*)(struct us_socket_t *)) on_long_timeout);
+}
+
+/* We do not really listen to passed FIN-handler, we entirely override it with our handler since SSL doesn't really have support for half-closed sockets */
+void us_internal_ssl_socket_context_on_end(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *(*on_end)(struct us_internal_ssl_socket_t *)) {
+ us_socket_context_on_end(0, (struct us_socket_context_t *) context, (struct us_socket_t *(*)(struct us_socket_t *)) ssl_on_end);
+}
+
+void us_internal_ssl_socket_context_on_connect_error(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *(*on_connect_error)(struct us_internal_ssl_socket_t *, int code)) {
+ us_socket_context_on_connect_error(0, (struct us_socket_context_t *) context, (struct us_socket_t *(*)(struct us_socket_t *, int)) on_connect_error);
+}
+
+void *us_internal_ssl_socket_context_ext(struct us_internal_ssl_socket_context_t *context) {
+ return context + 1;
+}
+
+/* Per socket functions */
+void *us_internal_ssl_socket_get_native_handle(struct us_internal_ssl_socket_t *s) {
+ return s->ssl;
+}
+
+int us_internal_ssl_socket_raw_write(struct us_internal_ssl_socket_t *s, const char *data, int length, int msg_more) {
+
+ if (us_socket_is_closed(0, &s->s) || us_internal_ssl_socket_is_shut_down(s)) {
+ return 0;
+ }
+ return us_socket_write(0, &s->s, data, length, msg_more);
+}
+
+int us_internal_ssl_socket_write(struct us_internal_ssl_socket_t *s, const char *data, int length, int msg_more) {
+
+
+ if (us_socket_is_closed(0, &s->s) || us_internal_ssl_socket_is_shut_down(s)) {
+ return 0;
+ }
+
+ struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s);
+
+ struct us_loop_t *loop = us_socket_context_loop(0, &context->sc);
+ struct loop_ssl_data *loop_ssl_data = (struct loop_ssl_data *) loop->data.ssl_data;
+
+ // it makes literally no sense to touch this here! it should start at 0 and ONLY be set and reset by the on_data function!
+ // the way is is now, triggering a write from a read will essentially delete all input data!
+ // what we need to do is to check if this ever is non-zero and print a warning
+
+
+
+ loop_ssl_data->ssl_read_input_length = 0;
+
+
+ loop_ssl_data->ssl_socket = &s->s;
+ loop_ssl_data->msg_more = msg_more;
+ loop_ssl_data->last_write_was_msg_more = 0;
+ //printf("Calling SSL_write\n");
+ int written = SSL_write(s->ssl, data, length);
+ //printf("Returning from SSL_write\n");
+ loop_ssl_data->msg_more = 0;
+
+ if (loop_ssl_data->last_write_was_msg_more && !msg_more) {
+ us_socket_flush(0, &s->s);
+ }
+
+ if (written > 0) {
+ return written;
+ } else {
+ int err = SSL_get_error(s->ssl, written);
+ if (err == SSL_ERROR_WANT_READ) {
+ // here we need to trigger writable event next ssl_read!
+ s->ssl_write_wants_read = 1;
+ } else if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL) {
+ // these two errors may add to the error queue, which is per thread and must be cleared
+ ERR_clear_error();
+
+ // all errors here except for want write are critical and should not happen
+ }
+
+ return 0;
+ }
+}
+
+void *us_internal_ssl_socket_ext(struct us_internal_ssl_socket_t *s) {
+ return s + 1;
+}
+
+int us_internal_ssl_socket_is_shut_down(struct us_internal_ssl_socket_t *s) {
+ return us_socket_is_shut_down(0, &s->s) || SSL_get_shutdown(s->ssl) & SSL_SENT_SHUTDOWN;
+}
+
+void us_internal_ssl_socket_shutdown(struct us_internal_ssl_socket_t *s) {
+ if (!us_socket_is_closed(0, &s->s) && !us_internal_ssl_socket_is_shut_down(s)) {
+ struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s);
+ struct us_loop_t *loop = us_socket_context_loop(0, &context->sc);
+ struct loop_ssl_data *loop_ssl_data = (struct loop_ssl_data *) loop->data.ssl_data;
+
+ // also makes no sense to touch this here!
+ // however the idea is that if THIS socket is not the same as ssl_socket then this data is not for me
+ // but this is not correct as it is currently anyways, any data available should be properly reset
+ loop_ssl_data->ssl_read_input_length = 0;
+
+
+ // essentially we need two of these: one for CURRENT CALL and one for CURRENT SOCKET WITH DATA
+ // if those match in the BIO function then you may read, if not then you may not read
+ // we need ssl_read_socket to be set in on_data and checked in the BIO
+ loop_ssl_data->ssl_socket = &s->s;
+
+
+ loop_ssl_data->msg_more = 0;
+
+ // sets SSL_SENT_SHUTDOWN no matter what (not actually true if error!)
+ int ret = SSL_shutdown(s->ssl);
+ if (ret == 0) {
+ ret = SSL_shutdown(s->ssl);
+ }
+
+ if (ret < 0) {
+
+ int err = SSL_get_error(s->ssl, ret);
+ if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL) {
+ // clear
+ ERR_clear_error();
+ }
+
+ // we get here if we are shutting down while still in init
+ us_socket_shutdown(0, &s->s);
+ }
+ }
+}
+
+struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_adopt_socket(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *s, int ext_size) {
+ // todo: this is completely untested
+ return (struct us_internal_ssl_socket_t *) us_socket_context_adopt_socket(0, &context->sc, &s->s, sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) + ext_size);
+}
+
+
+
+struct us_internal_ssl_socket_t * ssl_wrapped_context_on_close(struct us_internal_ssl_socket_t *s, int code, void *reason) {
+ struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s);
+ struct us_wrapped_socket_context_t* wrapped_context = (struct us_wrapped_socket_context_t *)us_internal_ssl_socket_context_ext(context);
+
+ if (wrapped_context->events.on_close) {
+ wrapped_context->events.on_close((struct us_socket_t*)s, code, reason);
+ }
+
+ // writting here can cause the context to not be writable anymore but its the user responsability to check for that
+ if (wrapped_context->old_events.on_close) {
+ wrapped_context->old_events.on_close((struct us_socket_t*)s, code, reason);
+ }
+
+ return s;
+}
+
+
+struct us_internal_ssl_socket_t * ssl_wrapped_context_on_writable(struct us_internal_ssl_socket_t *s) {
+ struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s);
+ struct us_wrapped_socket_context_t* wrapped_context = (struct us_wrapped_socket_context_t *)us_internal_ssl_socket_context_ext(context);
+
+ if (wrapped_context->events.on_writable) {
+ wrapped_context->events.on_writable((struct us_socket_t*)s);
+ }
+
+ // writting here can cause the context to not be writable anymore but its the user responsability to check for that
+ if (wrapped_context->old_events.on_writable) {
+ wrapped_context->old_events.on_writable((struct us_socket_t*)s);
+ }
+
+ return s;
+}
+
+
+struct us_internal_ssl_socket_t * ssl_wrapped_context_on_data(struct us_internal_ssl_socket_t *s, char *data, int length) {
+ struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s);
+ struct us_wrapped_socket_context_t* wrapped_context = (struct us_wrapped_socket_context_t *)us_internal_ssl_socket_context_ext(context);
+ // raw data if needed
+ if (wrapped_context->old_events.on_data) {
+ wrapped_context->old_events.on_data((struct us_socket_t*)s, data, length);
+ }
+ // ssl wrapped data
+ return ssl_on_data(s, data, length);
+}
+
+struct us_internal_ssl_socket_t * ssl_wrapped_context_on_timeout(struct us_internal_ssl_socket_t * s) {
+ struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s);
+ struct us_wrapped_socket_context_t* wrapped_context = (struct us_wrapped_socket_context_t *)us_internal_ssl_socket_context_ext(context);
+
+ if (wrapped_context->events.on_timeout) {
+ wrapped_context->events.on_timeout((struct us_socket_t*)s);
+ }
+
+ if (wrapped_context->old_events.on_timeout) {
+ wrapped_context->old_events.on_timeout((struct us_socket_t*)s);
+ }
+
+ return s;
+}
+
+struct us_internal_ssl_socket_t * ssl_wrapped_context_on_long_timeout(struct us_internal_ssl_socket_t * s) {
+ struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s);
+ struct us_wrapped_socket_context_t* wrapped_context = (struct us_wrapped_socket_context_t *)us_internal_ssl_socket_context_ext(context);
+
+ if (wrapped_context->events.on_long_timeout) {
+ wrapped_context->events.on_long_timeout((struct us_socket_t*)s);
+ }
+
+ if (wrapped_context->old_events.on_long_timeout) {
+ wrapped_context->old_events.on_long_timeout((struct us_socket_t*)s);
+ }
+
+ return s;
+}
+
+struct us_internal_ssl_socket_t * ssl_wrapped_context_on_end(struct us_internal_ssl_socket_t * s) {
+ struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s);
+ struct us_wrapped_socket_context_t* wrapped_context = (struct us_wrapped_socket_context_t *)us_internal_ssl_socket_context_ext(context);
+
+ if (wrapped_context->events.on_end) {
+ wrapped_context->events.on_end((struct us_socket_t*)s);
+ }
+
+ if (wrapped_context->old_events.on_end) {
+ wrapped_context->old_events.on_end((struct us_socket_t*)s);
+ }
+ return s;
+}
+
+struct us_internal_ssl_socket_t * ssl_wrapped_on_connect_error(struct us_internal_ssl_socket_t * s, int code) {
+ struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s);
+ struct us_wrapped_socket_context_t* wrapped_context = (struct us_wrapped_socket_context_t *)us_internal_ssl_socket_context_ext(context);
+
+ if (wrapped_context->events.on_connect_error) {
+ wrapped_context->events.on_connect_error((struct us_socket_t*)s, code);
+ }
+
+ if (wrapped_context->old_events.on_connect_error) {
+ wrapped_context->old_events.on_connect_error((struct us_socket_t*)s, code);
+ }
+ return s;
+}
+
+struct us_internal_ssl_socket_t* us_internal_ssl_socket_open(struct us_internal_ssl_socket_t * s, int is_client, char* ip, int ip_length) {
+ // closed
+ if (us_socket_is_closed(0, &s->s)) {
+ return s;
+ }
+ // already opened
+ if (s->ssl) return s;
+
+ // start SSL open
+ return ssl_on_open(s, is_client, ip, ip_length);
+}
+
+struct us_internal_ssl_socket_t *us_internal_ssl_socket_wrap_with_tls(struct us_socket_t *s, struct us_bun_socket_context_options_t options, struct us_socket_events_t events, int socket_ext_size) {
+ /* Cannot wrap a closed socket */
+ if (us_socket_is_closed(0, s)) {
+ return NULL;
+ }
+
+ struct us_socket_context_t * old_context = us_socket_context(0, s);
+
+ struct us_socket_context_t * context = us_create_bun_socket_context(1, old_context->loop, sizeof(struct us_wrapped_socket_context_t), options);
+ struct us_internal_ssl_socket_context_t *tls_context = (struct us_internal_ssl_socket_context_t *) context;
+
+ struct us_wrapped_socket_context_t* wrapped_context = (struct us_wrapped_socket_context_t *)us_internal_ssl_socket_context_ext(tls_context);
+ // we need to fire this events on the old context
+ struct us_socket_events_t old_events = (struct us_socket_events_t) {
+ .on_close = old_context->on_close,
+ .on_data = old_context->on_data,
+ .on_writable = old_context->on_writable,
+ .on_timeout = old_context->on_socket_timeout,
+ .on_long_timeout = old_context->on_socket_long_timeout,
+ .on_end = old_context->on_end,
+ .on_connect_error = old_context->on_connect_error,
+ };
+ wrapped_context->old_events = old_events;
+ wrapped_context->events = events;
+
+
+ // no need to wrap open because socket is already open (only new context will be called so we can configure hostname and ssl stuff normally here before handshake)
+ tls_context->on_open = (struct us_internal_ssl_socket_t *(*)(struct us_internal_ssl_socket_t *, int, char *, int))events.on_open;
+
+ // on handshake is not available on the old context so we just add this
+ if(events.on_handshake){
+ us_internal_on_ssl_handshake(tls_context, (void (*)(struct us_internal_ssl_socket_t *, int, struct us_bun_verify_error_t, void*))events.on_handshake, NULL);
+ }
+
+ // we need to wrap these events because we need to call the old context events as well
+ us_socket_context_on_connect_error(0, context, (struct us_socket_t *(*)(struct us_socket_t *, int)) ssl_wrapped_on_connect_error);
+ us_socket_context_on_end(0, context, (struct us_socket_t *(*)(struct us_socket_t *)) ssl_wrapped_context_on_end);
+ us_socket_context_on_long_timeout(0, context, (struct us_socket_t *(*)(struct us_socket_t *)) ssl_wrapped_context_on_long_timeout);
+ us_socket_context_on_timeout(0, context, (struct us_socket_t *(*)(struct us_socket_t *)) ssl_wrapped_context_on_timeout);
+
+ // special case this will be called after ssl things are done
+
+ // called from ssl_on_data handler is called inside ssl_wrapped_context_on_data
+ tls_context->on_data = (struct us_internal_ssl_socket_t *(*)(struct us_internal_ssl_socket_t *, char *, int))events.on_data;
+ us_socket_context_on_data(0, context, (struct us_socket_t *(*)(struct us_socket_t *, char *, int)) ssl_wrapped_context_on_data);
+
+ // here is the inverse of the above ssl_on_writable will call ssl_wrapped_context_on_writable
+ tls_context->on_writable = ssl_wrapped_context_on_writable;
+ us_socket_context_on_writable(0, context, (struct us_socket_t *(*)(struct us_socket_t *)) ssl_on_writable);
+
+ tls_context->on_close = ssl_wrapped_context_on_close;
+ us_socket_context_on_close(0, context, (struct us_socket_t *(*)(struct us_socket_t *, int, void *)) ssl_on_close);
+
+ // will resize to tls + ext size
+ struct us_internal_ssl_socket_t * socket = (struct us_internal_ssl_socket_t *) us_socket_context_adopt_socket(0, context, s, sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) + socket_ext_size);
+ socket->ssl = NULL;
+ socket->ssl_write_wants_read = 0;
+ socket->ssl_read_wants_write = 0;
+
+ return socket;
+}
+
+#endif
diff --git a/packages/bun-usockets/src/crypto/root_certs.h b/packages/bun-usockets/src/crypto/root_certs.h
new file mode 100644
index 000000000..0eacc2b45
--- /dev/null
+++ b/packages/bun-usockets/src/crypto/root_certs.h
@@ -0,0 +1,3438 @@
+// Maintaining the root certificates
+//
+// `src/crypto/root_certs.h` contains a compiled-in set of root certificates used as trust anchors
+// for TLS certificate validation.
+//
+// The certificates come from Mozilla, specifically NSS's `certdata.txt` file.
+//
+// The PEM encodings of the certificates are converted to C strings, and committed
+// in `src/crypto/root_certs.h`.
+//
+// When to update
+//
+// Root certificates should be updated sometime after Mozilla makes an NSS release,
+// check the NSS release schedule in https://wiki.mozilla.org/NSS:Release_Versions.
+//
+// Process
+//
+// The `generate-root-certs.js` script automates the update of
+// the root certificates, including:
+//
+// * Downloading `certdata.txt` from Mozilla's source control repository.
+// * Running `generate-ca-bundle.pl` to convert the certificates and generate
+// `src/crypto/root_certs.h`.
+// * Using `git diff-files` to determine which certificate have been added and/or
+// removed.
+//
+#include "libusockets.h"
+static struct us_cert_string_t root_certs[] = {
+
+/* GlobalSign Root CA */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMC\n"
+"QkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNV\n"
+"BAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBa\n"
+"MFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdS\n"
+"b290IENBMRswGQYDVQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUA\n"
+"A4IBDwAwggEKAoIBAQDaDuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtI\n"
+"K+6NiY6arymAZavpxy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCO\n"
+"XkNz8kHp1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG\n"
+"snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3\n"
+"dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DP\n"
+"AgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRg\n"
+"e2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUFAAOCAQEA1nPnfE920I2/7LqivjTF\n"
+"KDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY7\n"
+"76BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9\n"
+"LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr\n"
+"+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME\n"
+"HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==\n"
+"-----END CERTIFICATE-----",.len=1258},
+
+/* Entrust.net Premium 2048 Secure Server CA */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVz\n"
+"dC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJl\n"
+"Zi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0\n"
+"ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4\n"
+"KTAeFw05OTEyMjQxNzUwNTFaFw0yOTA3MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0\n"
+"Lm5ldDFAMD4GA1UECxQ3d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVm\n"
+"LiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRl\n"
+"ZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp\n"
+"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtK\n"
+"TY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/EC\n"
+"DNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ\n"
+"/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzWnLLPKQP5L6RQstRIzgUyVYr9smRM\n"
+"DuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVC\n"
+"wQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/\n"
+"BAUwAwEB/zAdBgNVHQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJKoZIhvcNAQEFBQAD\n"
+"ggEBADubj1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPyT/4xmf3IDExo\n"
+"U8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6YfzX1XEC+b\n"
+"BAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5bu/8j72gZyxKTJ1wD\n"
+"LW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+bYQLCIt+jerXmCHG8+c8eS9e\n"
+"nNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/ErfF6adulZkMV8gzURZVE=\n"
+"-----END CERTIFICATE-----",.len=1501},
+
+/* Baltimore CyberTrust Root */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJRTESMBAG\n"
+"A1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlCYWx0aW1v\n"
+"cmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjEL\n"
+"MAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEi\n"
+"MCAGA1UEAxMZQmFsdGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQAD\n"
+"ggEPADCCAQoCggEBAKMEuyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2ygu\n"
+"zmKiYv60iNoS6zjrIZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo\n"
+"6vWrJYeKmpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu\n"
+"XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3z\n"
+"yZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkC\n"
+"AwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1BE3wMBIGA1UdEwEB/wQIMAYB\n"
+"Af8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27\n"
+"TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukM\n"
+"JY2GQE/szKN+OMY3EU/t3WgxjkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhS\n"
+"NzkE1akxehi/oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67\n"
+"G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS\n"
+"R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp\n"
+"-----END CERTIFICATE-----",.len=1258},
+
+/* Entrust Root Certification Authority */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAU\n"
+"BgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMg\n"
+"aW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwg\n"
+"SW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4X\n"
+"DTA2MTEyNzIwMjM0MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQK\n"
+"Ew1FbnRydXN0LCBJbmMuMTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29y\n"
+"cG9yYXRlZCBieSByZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4x\n"
+"LTArBgNVBAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ\n"
+"KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poB\n"
+"j6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypo\n"
+"wCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+\n"
+"SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rV\n"
+"vDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2\n"
+"HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB\n"
+"/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSME\n"
+"GDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE\n"
+"vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4IBAQCT\n"
+"1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISMY/YP\n"
+"yyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa\n"
+"v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE\n"
+"2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPc\n"
+"j2A781q0tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8\n"
+"-----END CERTIFICATE-----",.len=1639},
+
+/* Comodo AAA Services root */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UE\n"
+"CAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21v\n"
+"ZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0\n"
+"MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdy\n"
+"ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENB\n"
+"IExpbWl0ZWQxITAfBgNVBAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZI\n"
+"hvcNAQEBBQADggEPADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686td\n"
+"UIoWMQuaBtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe\n"
+"3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8Ioa\n"
+"E+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULi\n"
+"mAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cmez6KJcfA3Z3mNWgQIJ2P2N7S\n"
+"w4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYD\n"
+"VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDov\n"
+"L2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0\n"
+"dHA6Ly9jcmwuY29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG\n"
+"9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q\n"
+"GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLzRt0vxuBq\n"
+"w8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z8VlI\n"
+"MCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C\n"
+"12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==\n"
+"-----END CERTIFICATE-----",.len=1513},
+
+/* QuoVadis Root CA 2 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNV\n"
+"BAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMjAeFw0w\n"
+"NjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBR\n"
+"dW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDIwggIiMA0GCSqG\n"
+"SIb3DQEBAQUAA4ICDwAwggIKAoICAQCaGMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4Gt\n"
+"Mh6QRr+jhiYaHv5+HBg6XJxgFyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp\n"
+"3MJGF/hd/aTa/55JWpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsR\n"
+"E8Scd3bBrrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp\n"
+"+ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1ksOR1YqI\n"
+"0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/iUcw6UwxI5g69ybR2\n"
+"BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIizPtGo/KPaHbDRsSNU30R2be1B\n"
+"2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og/zOhD7osFRXql7PSorW+8oyWHhqPHWyk\n"
+"YTe5hnMz15eWniN9gqRMgeKh0bpnX5UHoycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyP\n"
+"ZFwDwzqLID9ujWc9Otb+fVuIyV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQAB\n"
+"o4GwMIGtMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwz\n"
+"JQTU7tD2A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL\n"
+"MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1Zh\n"
+"ZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2fBluornFdLwUvZ+YT\n"
+"RYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzng/iN/Ae42l9NLmeyhP3ZRPx3\n"
+"UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2BlfF/nJrP3MpCYUNQ3cVX2kiF495V5+vgt\n"
+"JodmVjB3pjd4M1IQWK4/YY7yarHvGH5KWWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q8\n"
+"0m/DShcK+JDSV6IZUaUtl0HaB0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W\n"
+"6ZM/57Es3zrWIozchLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQj\n"
+"rLhVoQPRTUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD\n"
+"mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0ZohEUGW6y\n"
+"hhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y4aOTHcyKJloJONDO\n"
+"1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza8eOx79+Rj1QqCyXBJhnEUhAF\n"
+"ZdWCEOrCMc0u\n"
+"-----END CERTIFICATE-----",.len=2037},
+
+/* QuoVadis Root CA 3 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNV\n"
+"BAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMzAeFw0w\n"
+"NjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBR\n"
+"dW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDMwggIiMA0GCSqG\n"
+"SIb3DQEBAQUAA4ICDwAwggIKAoICAQDMV0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTP\n"
+"krgEQK0CSzGrvI2RaNggDhoB4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZ\n"
+"z3HmDyl2/7FWeUUrH556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2Objyj\n"
+"Ptr7guXd8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv\n"
+"vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLTmZw67mta\n"
+"a7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhebtfZFG4ZM2mnO4SJ\n"
+"k8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjcT5EOE7zelaTfi5m+rJsziO+1\n"
+"ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDtWAEXMJPpGovgc2PZapKUSU60rUqFxKMi\n"
+"MPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZc6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArl\n"
+"zW1oUevaPwV/izLmE1xr/l9A4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQAB\n"
+"o4IBlTCCAZEwDwYDVR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMw\n"
+"gcUwgZMGCCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0\n"
+"aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0aWZpY2F0\n"
+"ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVudC4wLQYIKwYBBQUH\n"
+"AgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2NwczALBgNVHQ8EBAMCAQYwHQYD\n"
+"VR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4GA1UdIwRnMGWAFPLAE+CCQz777i9nMpY1\n"
+"XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJCTTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEb\n"
+"MBkGA1UEAxMSUXVvVmFkaXMgUm9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62g\n"
+"LEz6wPJv92ZVqyM07ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon\n"
+"24QRiSemd1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd\n"
+"+LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B4f/xI4hR\n"
+"OJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadNt54CrnMAyNojA+j5\n"
+"6hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6xDYBEwa7TIzdfu4V8K5Iu6H6l\n"
+"i92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8S\n"
+"h17rRdhs9ZgC06DYVYoGmRmioHfRMJ6szHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7\n"
+"j2G4aSgWQgRecCocIdiP4b0jWy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEo\n"
+"kt4YutUqKLsRixeTmJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7\n"
+"zTSMmfXK4SVhM7JZG+Ju1zdXtg2pEto=\n"
+"-----END CERTIFICATE-----",.len=2349},
+
+/* Security Communication Root CA */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UE\n"
+"ChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJv\n"
+"b3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEY\n"
+"MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0\n"
+"aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8\n"
+"V6UMbXaKL0u/ZPtM7orw8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzG\n"
+"jGdnSj74cbAZJ6kJDKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1ae\n"
+"V+7AwFb9Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N\n"
+"QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OV\n"
+"YNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZ\n"
+"aNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG\n"
+"9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g0dNq/vu+m22/xwVtWSDEHPC32oRY\n"
+"AmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7K\n"
+"aEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKq\n"
+"L8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfci\n"
+"oU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw==\n"
+"-----END CERTIFICATE-----",.len=1221},
+
+/* XRamp Global CA Root */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkG\n"
+"A1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJh\n"
+"bXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlm\n"
+"aWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcxNDA0WhcNMzUwMTAxMDUzNzE5WjCBgjEL\n"
+"MAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMb\n"
+"WFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2Vy\n"
+"dGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCY\n"
+"JB69FbS638eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP\n"
+"KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7QDxIEM5df\n"
+"T2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4qEHMPJQRfevIpoy3\n"
+"hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRaJSKNNCyy9mgdEm3Tih4U2sSP\n"
+"puIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNViPvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJ\n"
+"KwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O\n"
+"BBYEFMZPoj0GY4QJnM5i5ASsjVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwu\n"
+"eHJhbXBzZWN1cml0eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcN\n"
+"AQEFBQADggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR\n"
+"vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxtqZ4Bfj8p\n"
+"zgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLaIR9NmXmd4c8nnxCb\n"
+"HIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSyi6mx5O+aGtA9aZnuqCij4Tyz\n"
+"8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQO+7ETPTsJ3xCwnR8gooJybQDJbw=\n"
+"-----END CERTIFICATE-----",.len=1509},
+
+/* Go Daddy Class 2 CA */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UE\n"
+"ChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAy\n"
+"IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYy\n"
+"MFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjEx\n"
+"MC8GA1UECxMoR28gRGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAw\n"
+"DQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWiz\n"
+"V3GgXne77ZtJ6XCAPVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HF\n"
+"iH7Eux6wwdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi\n"
+"EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lN\n"
+"f4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44\n"
+"dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLEsNKR1EwRcbNhyz2h/t2oatTj\n"
+"MIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2oatTjoWekZTBjMQswCQYDVQQGEwJV\n"
+"UzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRk\n"
+"eSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJ\n"
+"KoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYX\n"
+"MP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P\n"
+"TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQHmyW74cN\n"
+"xA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VILs9R\n"
+"aRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b\n"
+"vZ8=\n"
+"-----END CERTIFICATE-----",.len=1445},
+
+/* Starfield Class 2 CA */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzElMCMGA1UE\n"
+"ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENs\n"
+"YXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNjI5MTczOTE2WhcNMzQwNjI5\n"
+"MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2ll\n"
+"cywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRo\n"
+"b3JpdHkwggEgMA0GCSqGSIb3DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N\n"
+"78gDGIc/oav7PKaf8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMe\n"
+"j2YcOadN+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0\n"
+"X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aaK4Umkhyn\n"
+"ArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA1W4TNSNe35tfPe/W\n"
+"93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0GA1UdDgQWBBS/X7fRzt0fhvRb\n"
+"Vazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fRzt0fhvRbVazc1xDCDqmI56FspGowaDEL\n"
+"MAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAw\n"
+"BgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG\n"
+"A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1ep\n"
+"oXkJKtv3L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D\n"
+"eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJlxy16paq8\n"
+"U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynpVSJYACPq4xJDKVtH\n"
+"CN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3\n"
+"QBFGmh95DmK/D5fs4C8fF5Q=\n"
+"-----END CERTIFICATE-----",.len=1465},
+
+/* DigiCert Assured ID Root CA */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYD\n"
+"VQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu\n"
+"Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAw\n"
+"MDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQg\n"
+"SW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1\n"
+"cmVkIElEIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOA\n"
+"XLGH87dg+XESpa7cJpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lT\n"
+"XDGEKvYPmDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+\n"
+"wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/l\n"
+"bQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcX\n"
+"xH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQE\n"
+"AwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF66Kv9JLLgjEtUYunpyGd823IDzAf\n"
+"BgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog68\n"
+"3+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqo\n"
+"R+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+\n"
+"fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx\n"
+"H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe+o0bJW1s\n"
+"j6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g==\n"
+"-----END CERTIFICATE-----",.len=1347},
+
+/* DigiCert Global Root CA */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYD\n"
+"VQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu\n"
+"Y29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBa\n"
+"Fw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx\n"
+"GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBS\n"
+"b290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKP\n"
+"C3eQyaKl7hLOllsBCSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscF\n"
+"s3YnFo97nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\n"
+"43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6g\n"
+"SzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSii\n"
+"cNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYD\n"
+"VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm8KPiGxvDl7I90VUwHwYDVR0jBBgw\n"
+"FoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1E\n"
+"nE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDi\n"
+"qw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBA\n"
+"I+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\n"
+"YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQkCAUw7C29\n"
+"C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n"
+"-----END CERTIFICATE-----",.len=1335},
+
+/* DigiCert High Assurance EV Root CA */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYD\n"
+"VQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu\n"
+"Y29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2\n"
+"MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERp\n"
+"Z2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNl\n"
+"cnQgSGlnaCBBc3N1cmFuY2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\n"
+"AQoCggEBAMbM5XPm+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlB\n"
+"WTrT3JTWPNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM\n"
+"xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeB\n"
+"QVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5\n"
+"OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsgEsxBu24LUTi4S8sCAwEAAaNj\n"
+"MGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9H\n"
+"AdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3\n"
+"DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1\n"
+"ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VH\n"
+"MWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2\n"
+"Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCevEsXCS+0\n"
+"yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K\n"
+"-----END CERTIFICATE-----",.len=1363},
+
+/* SwissSign Gold CA - G2 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"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\n"
+"W8mw0FfB+j564ZfJ\n"
+"-----END CERTIFICATE-----",.len=2041},
+
+/* SwissSign Silver CA - G2 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"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\n"
+"5Aq7KkzrCWA5zspi2C5u\n"
+"-----END CERTIFICATE-----",.len=2045},
+
+/* SecureTrust CA */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYD\n"
+"VQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNl\n"
+"Y3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UE\n"
+"BhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1\n"
+"cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7C\n"
+"T8rU4niVWJxB4Q2ZQCQXOZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29\n"
+"vo6pQT64lO0pGtSO0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZ\n"
+"bf2IzIaowW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj\n"
+"7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xH\n"
+"CzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIE\n"
+"Bh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE\n"
+"/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYjaHR0cDovL2NybC5zZWN1cmV0cnVz\n"
+"dC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDt\n"
+"T0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQ\n"
+"f2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cp\n"
+"rp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS\n"
+"CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR3ItHuuG5\n"
+"1WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE=\n"
+"-----END CERTIFICATE-----",.len=1347},
+
+/* Secure Global CA */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYD\n"
+"VQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNl\n"
+"Y3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYD\n"
+"VQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNl\n"
+"Y3VyZSBHbG9iYWwgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxV\n"
+"aQZx5RNoJLNP2MwhR/jxYDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6Mpjh\n"
+"HZevj8fcyTiW89sa/FHtaMbQbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ\n"
+"/kG5VacJjnIFHovdRIWCQtBJwB1g8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnI\n"
+"HmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYVHDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPi\n"
+"XB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGC\n"
+"NxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9E\n"
+"BMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJl\n"
+"dHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBBQUAA4IB\n"
+"AQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+OYo68+aCiV0BN7OrJKQV\n"
+"DpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895\n"
+"P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY\n"
+"iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xcf8LDmBxr\n"
+"ThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW\n"
+"-----END CERTIFICATE-----",.len=1351},
+
+/* COMODO Certification Authority */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkG\n"
+"A1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9y\n"
+"ZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZp\n"
+"Y2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQsw\n"
+"CQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxm\n"
+"b3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRp\n"
+"ZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECL\n"
+"i3LjkRv3UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI\n"
+"2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7eu\n"
+"NJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC\n"
+"8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQF\n"
+"ZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVIrLsm9wIDAQABo4GOMIGLMB0GA1Ud\n"
+"DgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw\n"
+"AwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9D\n"
+"ZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5\n"
+"t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv\n"
+"IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/RxdMosIG\n"
+"lgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmcIGfE\n"
+"7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN\n"
+"+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ==\n"
+"-----END CERTIFICATE-----",.len=1485},
+
+/* COMODO ECC Certification Authority */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UE\n"
+"BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEa\n"
+"MBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlm\n"
+"aWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTEL\n"
+"MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2Fs\n"
+"Zm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0Mg\n"
+"Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmC\n"
+"FYX7deSRFtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J\n"
+"cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZ\n"
+"SBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggq\n"
+"hkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDmfQjGGoe9GKhzvSbKYAydzpmf\n"
+"z1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeAU/7dIOA1mjbRxwG55tzd8/8dLDoW\n"
+"V9mSOdY=\n"
+"-----END CERTIFICATE-----",.len=938},
+
+/* Certigna */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZS\n"
+"MRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMw\n"
+"NVoXDTI3MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczER\n"
+"MA8GA1UEAwwIQ2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ\n"
+"1tazNHUmgh7stL7qXOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lI\n"
+"zw7sebYs5zRLcAglozyHGxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxr\n"
+"yIRWijOp5yIVUxbwzBfsV1/pogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJb\n"
+"zg4ij02Q130yGLMLLGq/jj8UEYkgDncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2\n"
+"JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKfIrjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0T\n"
+"AQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQtCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AU\n"
+"Gu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlt\n"
+"eW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/JSP8wDgYDVR0PAQH/BAQDAgEGMBEG\n"
+"CWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEAhQMeknH2Qq/ho2Ge6/PAD/Kl\n"
+"1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+ImvaYS5/1HI93TDhHkxA\n"
+"GYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1kluPBS1xp81HlDQwY9q\n"
+"cEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY1gkIl2PlwS6w\n"
+"t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/QwWyH8EZE0\n"
+"vkHve52Xdf+XlcCWWC/qu0bXu+TZLg==\n"
+"-----END CERTIFICATE-----",.len=1327},
+
+/* ePKI Root Certification Authority */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"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\n"
+"EZw=\n"
+"-----END CERTIFICATE-----",.len=2029},
+
+/* certSIGN ROOT CA */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREw\n"
+"DwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQx\n"
+"NzIwMDRaFw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lH\n"
+"TjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\n"
+"AQoCggEBALczuX7IJUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oq\n"
+"rl0Hj0rDKH/v+yv6efHHrfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsA\n"
+"fsT8AzNXDe3i+s5dRdY4zTW2ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUo\n"
+"Se1b16kQOA7+j0xbm0bqQfWwCHTD0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwv\n"
+"JoIQ4uNllAoEwF73XVv4EOLQunpL+943AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNC\n"
+"MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPx\n"
+"fIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IBAQA+0hyJLjX8+HXd5n9liPRyTMks1zJO\n"
+"890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6\n"
+"IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0x2DEW8xXjrJ1/RsCCdtZb3KT\n"
+"afcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIltvBTjD4au8as+x6AJzKNI\n"
+"0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7NzTogVZ96edhBiIL5V\n"
+"aZVDADlN9u6wWk5JRFRYX0KD\n"
+"-----END CERTIFICATE-----",.len=1173},
+
+/* NetLock Arany (Class Gold) Főtanúsítvány */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTER\n"
+"MA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFu\n"
+"w7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwws\n"
+"TmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjEx\n"
+"MTUwODIxWhcNMjgxMjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFw\n"
+"ZXN0MRUwEwYDVQQKDAxOZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lh\n"
+"ZMOzayAoQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkg\n"
+"KENsYXNzIEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n"
+"MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFtt\n"
+"vzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn\n"
+"7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5VA1lddkVQZQBr17s9o3x/61k\n"
+"/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7\n"
+"GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiL\n"
+"o0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpn\n"
+"k/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ\n"
+"5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C\n"
+"+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzCbLBQWV2Q\n"
+"WzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5KfnaNwUA\n"
+"SZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu\n"
+"dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E=\n"
+"-----END CERTIFICATE-----",.len=1473},
+
+/* Hongkong Post Root CA 1 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNV\n"
+"BAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4X\n"
+"DTAzMDUxNTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT\n"
+"DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjAN\n"
+"BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSS\n"
+"HSL22oVyaf7XPwnU3ZG1ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8g\n"
+"PW2iNr4joLFutbEnPzlTCeqrauh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7j\n"
+"EAaPIpjhZY4bXSNmO7ilMlHIhqqhqZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9\n"
+"nnV0ttgCXjqQesBCNnLsak3c78QA3xMYV18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208\n"
+"o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNVHRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQE\n"
+"AwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7ih9legYsCmEEIjEy82tvuJxuC52pF7BaL\n"
+"T4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37piol7Yutmcn1KZJ/RyTZXaeQi/cImya\n"
+"T/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5LmeiIAQ32pwL0xch4I+XeTRvhEgC\n"
+"IDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88psT/oROhUVRsPNH4NbLUES\n"
+"7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilTc4afU9hDDl3WY4Jx\n"
+"HYB0yvbiAmvZWg==\n"
+"-----END CERTIFICATE-----",.len=1165},
+
+/* SecureSign RootCA11 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UE\n"
+"ChMiSmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJl\n"
+"U2lnbiBSb290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNV\n"
+"BAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRww\n"
+"GgYDVQQDExNTZWN1cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n"
+"CgKCAQEA/XeqpRyQBTvLTJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1y\n"
+"fIw/XwFndBWW4wI8h9uuywGOwvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyK\n"
+"yiyhFTOVMdrAG/LuYpmGYz+/3ZMqg6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9\n"
+"UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rPO7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V\n"
+"1uinMrPmmECGxc0nEovMe863ETxiYAcjPitAbpSACW22s293bzUIUPsCh8U+iQIDAQABo0Iw\n"
+"QDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZXt94wDgYDVR0PAQH/BAQDAgEGMA8GA1Ud\n"
+"EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKChOBZmLqdWHyGcBvod7bkixTgm2E5P\n"
+"7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4rbnpwrxYO4wJs+0LmGJ1F2FXI\n"
+"6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQOh29Dbx7VFALuUKvVaAY\n"
+"ga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01y8hSyn+B/tlr0/cR\n"
+"7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061lgeLKBObjBmN\n"
+"QSdJQO7e5iNEOdyhIta6A/I=\n"
+"-----END CERTIFICATE-----",.len=1246},
+
+/* Microsec e-Szigno Root CA 2009 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJI\n"
+"VTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMM\n"
+"Hk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0Bl\n"
+"LXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQG\n"
+"EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNV\n"
+"BAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5m\n"
+"b0BlLXN6aWduby5odTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG\n"
+"2KfgQvvPkd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc\n"
+"cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDH\n"
+"QWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqp\n"
+"GrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV\n"
+"87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQF\n"
+"MAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAf\n"
+"BgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3pp\n"
+"Z25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5Dw\n"
+"pL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk\n"
+"ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775tyERzAMB\n"
+"VnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02yULy\n"
+"Mtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi\n"
+"LXpUq3DDfSJlgnCW\n"
+"-----END CERTIFICATE-----",.len=1457},
+
+/* GlobalSign Root CA - R3 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMX\n"
+"R2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMT\n"
+"Ckdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQL\n"
+"ExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UE\n"
+"AxMKR2xvYmFsU2lnbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5Bngi\n"
+"FvXAg7aEyiie/QV2EcWtiHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0M\n"
+"K66X17YUhhB5uzsTgHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL\n"
+"0gRgykmmKPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd\n"
+"QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613\n"
+"t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQD\n"
+"AgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0G\n"
+"CSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7lgAJQayzE4aGKAczymvmdLm6AC2u\n"
+"pArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdW\n"
+"PoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0\n"
+"095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJr\n"
+"lAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH\n"
+"WD9f\n"
+"-----END CERTIFICATE-----",.len=1226},
+
+/* Autoridad de Certificacion Firmaprofesional CIF A62634068 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMx\n"
+"QjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwg\n"
+"Q0lGIEE2MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNV\n"
+"BAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zl\n"
+"c2lvbmFsIENJRiBBNjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDK\n"
+"lmuO6vj78aI14H9M2uDDUtd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOS\n"
+"L/UR5GLXMnE42QQMcas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9\n"
+"qFD0sefGL9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i\n"
+"NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2\n"
+"f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44\n"
+"I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCyZ/QYFpM6/EfY0XiWMR+6Kwxf\n"
+"XZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy\n"
+"9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF\n"
+"8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mV\n"
+"BngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1UdEwEB/wQIMAYBAf8C\n"
+"AQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNHDhpkLzCBpgYD\n"
+"VR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZpcm1hcHJv\n"
+"ZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAAbABh\n"
+"ACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx\n"
+"ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+\n"
+"xDLx51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5x\n"
+"hOW1//qkR71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5\n"
+"eTSSPi5E6PaPT481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5Fl\n"
+"ClrD2VQS3a/DTg4fJl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5k\n"
+"SeTy36LssUzAKh3ntLFlosS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2\n"
+"gHN99ZwExEWN57kci57q13XRcrHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYD\n"
+"NEeyrPsiBsse3RdHHF9mudMaotoRsaS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhr\n"
+"JKyXWERHrVkY8SFlcN7ONGCoQPHzPKTDKCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIyS\n"
+"xZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi6Et8Vcad+qMUu2WFbm5PEn4KPJ2V\n"
+"-----END CERTIFICATE-----",.len=2162},
+
+/* Izenpe.com */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"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\n"
+"1V4tC5h9Pmzb/CaIxw==\n"
+"-----END CERTIFICATE-----",.len=2118},
+
+/* Go Daddy Root Certificate Authority - G2 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNV\n"
+"BAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29t\n"
+"LCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAt\n"
+"IEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAw\n"
+"DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5\n"
+"LmNvbSwgSW5jLjExMC8GA1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3Jp\n"
+"dHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3\n"
+"gElY6SKDE6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH\n"
+"/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLI\n"
+"jWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6\n"
+"gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGRtDtwKj9useiciAF9n9T521Nt\n"
+"YJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO\n"
+"BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3\n"
+"DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC\n"
+"2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95\n"
+"kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo\n"
+"2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPOLPAvTK33\n"
+"sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1\n"
+"-----END CERTIFICATE-----",.len=1363},
+
+/* Starfield Root Certificate Authority - G2 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNV\n"
+"BAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBU\n"
+"ZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRl\n"
+"IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJ\n"
+"BgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYD\n"
+"VQQKExxTdGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQg\n"
+"Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEP\n"
+"ADCCAQoCggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg\n"
+"nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSu\n"
+"S/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhh\n"
+"dM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dNdloedl40wOiWVpmKs/B/pM29\n"
+"3DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbUJtQIBFnQmA4O5t78w+wfkPECAwEA\n"
+"AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n\n"
+"2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWU\n"
+"XuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox\n"
+"9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg\n"
+"8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/KpL/QlwVK\n"
+"vOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZc2T5\n"
+"NnReJaH1ZgUufzkVqSr7UIuOhWn0\n"
+"-----END CERTIFICATE-----",.len=1396},
+
+/* Starfield Services Root Certificate Authority - G2 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNV\n"
+"BAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBU\n"
+"ZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENl\n"
+"cnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1\n"
+"OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNk\n"
+"YWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJT\n"
+"dGFyZmllbGQgU2VydmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw\n"
+"DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p\n"
+"OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2\n"
+"dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS\n"
+"7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufehRhJfGZOozptqbXuNC66DQO4\n"
+"M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFBrMnUVN+HL8cisibMn1lUaJ/8viov\n"
+"xFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC\n"
+"AQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBL\n"
+"NqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynV\n"
+"v/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z\n"
+"qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkdiEDPfUYd\n"
+"/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jzaYyWf\n"
+"/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6\n"
+"-----END CERTIFICATE-----",.len=1420},
+
+/* AffirmTrust Commercial */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMx\n"
+"FDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFs\n"
+"MB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNV\n"
+"BAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjAN\n"
+"BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTW\n"
+"zsO3qyxPxkEylFf6EqdbDuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U\n"
+"6Mje+SJIZMblq8Yrba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNA\n"
+"FxHUdPALMeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1\n"
+"yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1J\n"
+"dX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8w\n"
+"DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAFis\n"
+"9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M\n"
+"06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1Ua\n"
+"ADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjip\n"
+"M1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclN\n"
+"msxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8=\n"
+"-----END CERTIFICATE-----",.len=1201},
+
+/* AffirmTrust Networking */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMx\n"
+"FDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5n\n"
+"MB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNV\n"
+"BAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjAN\n"
+"BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWRE\n"
+"ZY9nZOIG41w3SfYvm4SEHi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ\n"
+"/Ls6rnla1fTWcbuakCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXL\n"
+"viRmVSRLQESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp\n"
+"6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKB\n"
+"Nv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0w\n"
+"DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAIlX\n"
+"shZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t\n"
+"3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA\n"
+"3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzek\n"
+"ujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfx\n"
+"ojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s=\n"
+"-----END CERTIFICATE-----",.len=1201},
+
+/* AffirmTrust Premium */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMx\n"
+"FDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4X\n"
+"DTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoM\n"
+"C0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG\n"
+"9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64t\n"
+"b+eT2TZwamjPjlGjhVtnBKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/\n"
+"0qRY7iZNyaqoe5rZ+jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/\n"
+"K+k8rNrSs8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5\n"
+"HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua\n"
+"2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/\n"
+"9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+SqHZGnEJlPqQewQcDWkYtuJfz\n"
+"t9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m\n"
+"6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKP\n"
+"KrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNC\n"
+"MEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYD\n"
+"VR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2\n"
+"KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMgNt58D2kT\n"
+"iKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC6C1Y\n"
+"91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S\n"
+"L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQ\n"
+"wUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFo\n"
+"oC8k4gmVBtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5Yw\n"
+"H2AG7hsj/oFgIxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/\n"
+"qzWaVYa8GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO\n"
+"RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAlo\n"
+"GRwYQw==\n"
+"-----END CERTIFICATE-----",.len=1887},
+
+/* AffirmTrust Premium ECC */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDAS\n"
+"BgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAe\n"
+"Fw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQK\n"
+"DAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcq\n"
+"hkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQU\n"
+"X+iOGasvLkjmrBhDeKzQN8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR\n"
+"4ptlKymjQjBAMB0GA1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTAD\n"
+"AQH/MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs\n"
+"aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9C\n"
+"a/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ==\n"
+"-----END CERTIFICATE-----",.len=751},
+
+/* Certum Trusted Network CA */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYD\n"
+"VQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlm\n"
+"aWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0Ew\n"
+"HhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UE\n"
+"ChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmlj\n"
+"YXRpb24gQXV0aG9yaXR5MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIB\n"
+"IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/\n"
+"91sts1rHUV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM\n"
+"TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmt\n"
+"VSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM\n"
+"+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8xAcPs3hEtF10fuFDRXhmnad4H\n"
+"MyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQI\n"
+"ds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEB\n"
+"AKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsi\n"
+"srCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv\n"
+"94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY\n"
+"VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI03YnnZot\n"
+"BqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw=\n"
+"-----END CERTIFICATE-----",.len=1351},
+
+/* TWCA Root Certification Authority */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UE\n"
+"CgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2Vy\n"
+"dGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBf\n"
+"MQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSow\n"
+"KAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3\n"
+"DQEBAQUAA4IBDwAwggEKAoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bi\n"
+"hSX0NXIP+FPQQeFEAcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQ\n"
+"sIBct+HHK3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX\n"
+"RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJb\n"
+"KdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxY\n"
+"A7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud\n"
+"DgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG9w0BAQUFAAOCAQEAPNV3PdrfibqH\n"
+"DAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqG\n"
+"fczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4g\n"
+"umlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKu\n"
+"D8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ\n"
+"YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw==\n"
+"-----END CERTIFICATE-----",.len=1266},
+
+/* Security Communication RootCA2 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UE\n"
+"ChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29t\n"
+"bXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTEL\n"
+"MAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAl\n"
+"BgNVBAsTHlNlY3VyaXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEB\n"
+"BQADggEPADCCAQoCggEBANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz3\n"
+"35c9S672XewhtUGrzbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonC\n"
+"v/Q4EpVMVAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ\n"
+"hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhw\n"
+"Hyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCca\n"
+"dfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQE\n"
+"AwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBMOqNErLlFsceTfsgL\n"
+"CkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8\n"
+"AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g6\n"
+"9ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR\n"
+"50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/\n"
+"SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03\n"
+"-----END CERTIFICATE-----",.len=1258},
+
+/* Actalis Authentication Root CA */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCSVQx\n"
+"DjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1ODUyMDk2NzEn\n"
+"MCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENBMB4XDTExMDkyMjExMjIw\n"
+"MloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYD\n"
+"VQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRo\n"
+"ZW50aWNhdGlvbiBSb290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bE\n"
+"pSmkLO/lGMWwUKNvUTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW\n"
+"1V8IbInX4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9\n"
+"KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/gCczWw63\n"
+"igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1YprbrxTIW6HMiRvhMCb8\n"
+"oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ51Q+qDp2JE+BIcXjDwL4k5RH\n"
+"ILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2Fbe8lEfKXGkJh90qX6IuxEAf6ZYGyojnP\n"
+"9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxeKF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4\n"
+"RCHiMUVhUE4Q2OM1fEwZtN4Fv6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U\n"
+"5r0F+7if5t+L4sbnfpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/j\n"
+"Vo23Eek7jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz\n"
+"ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbtifN7OHCU\n"
+"yQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQALe3KHwGCmSUyIWOYd\n"
+"iPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70jsNjLiNmsGe+b7bAEzlgqqI0\n"
+"JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDzWochcYBNy+A4mz+7+uAwTc+G02UQGRjR\n"
+"lwKxK3JCaKygvU5a2hi/a5iB0P2avl4VSM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2ryk\n"
+"OLpn7VU+Xlff1ANATIGk0k9jpwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2T\n"
+"lf05fbsq4/aC4yyXX04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst\n"
+"842/6+OkfcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R\n"
+"K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btUZCzJJ7VL\n"
+"kn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJULysRJyU3eExRarDz\n"
+"zFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaTLnPqZih4zR0Uv6CPLy64Lo7y\n"
+"FIrM6bV8+2ydDKXhlg==\n"
+"-----END CERTIFICATE-----",.len=2045},
+
+/* Buypass Class 2 Root CA */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UE\n"
+"CgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMiBSb290\n"
+"IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1owTjELMAkGA1UEBhMCTk8xHTAb\n"
+"BgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDIg\n"
+"Um9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1\n"
+"aeTuMgHbo4Yf5FkNuud1g1Lr6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXl\n"
+"zwx87vFKu3MwZfPVL4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FV\n"
+"M5I+GC911K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx\n"
+"MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZQmdiXnfg\n"
+"olXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkBarcNuAeBfos4Gzjm\n"
+"CleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clrUs3ERo/ctfPYV3Me6ZQ5BL/T\n"
+"3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLiFRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1L\n"
+"PC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRSP/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIH\n"
+"ZMbXb5tMOA1jrGKvNouicwoN9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVe\n"
+"e7QHIJihdjK4TWxPAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+Bi\n"
+"koL1RpzzuvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h\n"
+"9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462sA20ucS6v\n"
+"xOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3tOluwlN5E40EIosHs\n"
+"Hdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo+fsicdl9sz1Gv7SEr5AcD48S\n"
+"aq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlq\n"
+"YLYdDnkM/crqJIByw5c/8nerQyIKx+u2DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6\n"
+"OBE1/yWDLfJ6v9r9jv6ly0UsH8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6w\n"
+"pJ9qzo6ysmD0oyLQI+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYK\n"
+"beaP4NK75t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h\n"
+"3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPzY11aWOIv\n"
+"4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA=\n"
+"-----END CERTIFICATE-----",.len=1911},
+
+/* Buypass Class 3 Root CA */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UE\n"
+"CgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMyBSb290\n"
+"IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFowTjELMAkGA1UEBhMCTk8xHTAb\n"
+"BgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDMg\n"
+"Um9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEG\n"
+"Mnqb8RB2uACatVI2zSRHsJ8YZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fk\n"
+"oF0LXOBXByow9c3EN3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOo\n"
+"TyrvYLs9tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX\n"
+"0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c/3ATAOux\n"
+"9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8XKhDzzMro6/1rqy6a\n"
+"ny2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTYzIw0bOIjL9kSGiG5VZFvC5F5\n"
+"GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvSO1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon\n"
+"74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3i\n"
+"iZ+gMEuFuZyUJHmPfWupRWgPK9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFM\n"
+"OVLAMLrwjEsCsLa3AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/l\n"
+"b+74suwvTg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj\n"
+"QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdVcSQy9sgL\n"
+"8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXSIGrs/CIBKM+GuIAe\n"
+"qcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2HJLw5QY33KbmkJs4j1xrG0aG\n"
+"Q0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsaO5S3HWCntZznKWlXWpuTekMwGwPXYshA\n"
+"pqr8ZORK15FTAaggiG6cX0S5y2CBNOxv033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjEN\n"
+"SoYc6+I2KSb12tjE8nVhz36udmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr1\n"
+"8okmAWiDSKIz6MkEkbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2X\n"
+"cEQNtg413OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD\n"
+"u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq4/g7u9xN\n"
+"12TyUb7mqqta6THuBrxzvxNiCp/HuZc=\n"
+"-----END CERTIFICATE-----",.len=1911},
+
+/* T-TeleSec GlobalRoot Class 3 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNV\n"
+"BAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lz\n"
+"dGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNz\n"
+"IDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzAp\n"
+"BgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQt\n"
+"U3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENs\n"
+"YXNzIDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3Z\n"
+"JNW4t/zN8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/\n"
+"RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4hqX2iys5\n"
+"2qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5ZeAsVMhg02YXP+HM\n"
+"VDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltMEnAMbEQgqxHY9Bn20pxSN+f6\n"
+"tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD\n"
+"VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0B\n"
+"AQsFAAOCAQEAVj3vlNW92nOyWL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ\n"
+"85cp4IaH3gRZD/FZ1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/\n"
+"vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT\n"
+"91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuImle9eiPZaG\n"
+"zPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw==\n"
+"-----END CERTIFICATE-----",.len=1363},
+
+/* D-TRUST Root Class 3 CA 2 2009 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRFMRUwEwYD\n"
+"VQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIg\n"
+"MjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NThaME0xCzAJBgNVBAYTAkRFMRUw\n"
+"EwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENB\n"
+"IDIgMjAwOTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/W\n"
+"UEWJNTrGa9v+2wBoqOADER03UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23D\n"
+"EE0NkVJD2IfgXU42tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/\n"
+"RcPHAY9RySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM\n"
+"lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp/hnd2HN8\n"
+"gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8GA1UdEwEB/wQFMAMB\n"
+"Af8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4GA1UdDwEB/wQEAwIBBjCB0wYD\n"
+"VR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRS\n"
+"VVNUJTIwUm9vdCUyMENsYXNzJTIwMyUyMENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21i\n"
+"SCxDPURFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10\n"
+"cnVzdC5uZXQvY3JsL2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZI\n"
+"hvcNAQELBQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni\n"
+"acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0o3/U37CY\n"
+"Aqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4KzCUqNQT4YJEVdT1B\n"
+"/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8PIWmawomDeCTmGCufsYkl4ph\n"
+"X5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3YJohw1+qRzT65ysCQblrGXnRl11z+o+I=\n"
+"-----END CERTIFICATE-----",.len=1513},
+
+/* D-TRUST Root Class 3 CA 2 EV 2009 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRFMRUwEwYD\n"
+"VQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIg\n"
+"RVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUwNDZaMFAxCzAJBgNVBAYTAkRF\n"
+"MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAz\n"
+"IENBIDIgRVYgMjAwOTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3\n"
+"MKCOvXwEz75ivJn9gpfSegpnljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut\n"
+"8Mxk2og+KbgPCdM03TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsT\n"
+"l28So/6ZqQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR\n"
+"p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8HgQ+HKDY\n"
+"D8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQwggEgMA8GA1UdEwEB\n"
+"/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9NteaHNxnMA4GA1UdDwEB/wQEAwIB\n"
+"BjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFwOi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0\n"
+"L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xhc3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89\n"
+"RC1UcnVzdCUyMEdtYkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBo\n"
+"dHRwOi8vd3d3LmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2\n"
+"XzIwMDkuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp\n"
+"3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05nsKtjHEh\n"
+"8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNFCSuGdXzfX2lXANtu\n"
+"2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7naxpeG0ILD5EJt/rDiZE4OJudA\n"
+"NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjm\n"
+"JuVvw9y4AyHqnxbxLFS1\n"
+"-----END CERTIFICATE-----",.len=1534},
+
+/* CA Disig Root R2 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"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\n"
+"-----END CERTIFICATE-----",.len=1931},
+
+/* ACCVRAIZ1 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UEAwwJQUND\n"
+"VlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQswCQYDVQQGEwJFUzAe\n"
+"Fw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQBgNVBAMMCUFDQ1ZSQUlaMTEQ\n"
+"MA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUNDVjELMAkGA1UEBhMCRVMwggIiMA0GCSqG\n"
+"SIb3DQEBAQUAA4ICDwAwggIKAoICAQCbqau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPf\n"
+"mt4ftVTdFXxpNRFvu8gMjmoYHtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM38\n"
+"02/J+Nq2DoLSRYWoG2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkX\n"
+"hBilyNpAlHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr\n"
+"IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/0KIV9VMJ\n"
+"cRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eHk6fGioozl2A3ED6X\n"
+"Pm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/474KElB0iryYl0/wiPgL/AlmXz\n"
+"7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMOm3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE\n"
+"3fj5tItQo05iifCHJPqDQsGH+tUtKSpacXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbh\n"
+"dQrqeWZ2iE9x6wQl1gpaepPluUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQAB\n"
+"o4ICyzCCAscwfQYIKwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3Yu\n"
+"ZXMvZmlsZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG\n"
+"AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2VuqB5TbM\n"
+"jB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeTVfZW6oHlNsyMHj+9\n"
+"MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIGCCsGAQUFBwICMIIBFB6CARAA\n"
+"QQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAA\n"
+"UgBhAO0AegAgAGQAZQAgAGwAYQAgAEEAQwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUA\n"
+"IABUAGUAYwBuAG8AbABvAGcA7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4A\n"
+"IABFAGwAZQBjAHQAcgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUA\n"
+"KQAuACAAQwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA\n"
+"czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2MuaHRtMFUG\n"
+"A1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRtaW4vQXJjaGl2b3Mv\n"
+"Y2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1UdDwEB/wQEAwIBBjAXBgNVHREE\n"
+"EDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEFBQADggIBAJcxAp/n/UNnSEQU5CmH7Uwo\n"
+"ZtCPNdpNYbdKl02125DgBS4OxnnQ8pdpD70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTK\n"
+"FpV8M9xii6g3+CfYCS0b78gUJyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/\n"
+"+QEnWCzI7UiRfD+mAM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA\n"
+"9j9ajepDvV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms\n"
+"tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH7ehVRE2I\n"
+"9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5hI6zppSSMEYCUWqKi\n"
+"uUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szAh1xA2syVP1XgNce4hL60Xc16\n"
+"gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xFd3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew\n"
+"+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2HpPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbR\n"
+"D0tVNEYqi4Y7\n"
+"-----END CERTIFICATE-----",.len=2767},
+
+/* TWCA Global Root CA */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcxEjAQBgNV\n"
+"BAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMTVFdDQSBHbG9iYWwg\n"
+"Um9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5NTlaMFExCzAJBgNVBAYTAlRX\n"
+"MRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0Eg\n"
+"R2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zE\n"
+"booh745NnHEKH1Jw7W2CnJfF10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvV\n"
+"avKOZsTuKwEHktSz0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XT\n"
+"P3VfKfChMBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH\n"
+"zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc46KokWof\n"
+"wpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2yKsi2XMPpfRaAok/\n"
+"T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCilaLOz9qC5wc0GZbpuCGqKX6m\n"
+"OL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYPoA/pyJV/v1WRBXrPPRXAb94JlAGD1zQb\n"
+"zECl8LibZ9WYkTunhHiVJqRaCPgrdLQABDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJr\n"
+"nu+8vyq8W8BQj0FwcYeyTbcEqYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53\n"
+"L46g5pIOBvwFItIm4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/\n"
+"BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL\n"
+"1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsnLhpNgb+E\n"
+"1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WFH6vPNOw/KP4M8VeG\n"
+"TslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNoRI2T9GRwoD2dKAXDOXC4Ynsg\n"
+"/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+nile98FRYB/e2guyLXW3Q0iT5/Z5xoRd\n"
+"gFlglPx4mI88k1HtQJAH32RjJMtOcQWh15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92\n"
+"a6O2JryPA9gK8kxkRr05YuWW6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/Zuepi\n"
+"iI7E8UuDEq3mi4TWnsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZz\n"
+"JBPqpK5jwa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz\n"
+"aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0=\n"
+"-----END CERTIFICATE-----",.len=1878},
+
+/* TeliaSonera Root CA v1 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAwNzEUMBIG\n"
+"A1UECgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJvb3QgQ0EgdjEwHhcN\n"
+"MDcxMDE4MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYDVQQKDAtUZWxpYVNvbmVyYTEf\n"
+"MB0GA1UEAwwWVGVsaWFTb25lcmEgUm9vdCBDQSB2MTCCAiIwDQYJKoZIhvcNAQEBBQADggIP\n"
+"ADCCAgoCggIBAMK+6yfwIaPzaSZVfp3FVRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3\n"
+"t+XmfHnqjLWCi65ItqwA3GV17CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq\n"
+"/t75rH2D+1665I+XZ75Ljo1kB1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1\n"
+"jF3oI7x+/jXh7VB7qTCNGdMJjmhnXb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs\n"
+"81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxHoLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkmdtwoSxAg\n"
+"HNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3F0fUTPHSiXk+TT2YqGHeOh7S+F4D4MHJHIzT\n"
+"jU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJoWjiUIMusDor8zagrC/kb2HCUQk5PotT\n"
+"ubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4pgd7gUY2BIbdeTXHlSw7sKMXNeVzH7Rc\n"
+"We/a6hBle3rQf5+ztCo3O3CLm1u5K7fsslESl1MpWtTwEhDcTwK7EpIvYtQ/aUN8Ddb8WHUB\n"
+"iJ1YFkveupD/RwGJBmr2X7KQarMCpgKIv7NHfirZ1fpoeDVNAgMBAAGjPzA9MA8GA1UdEwEB\n"
+"/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qWDNXr+nuqF+gTEjAN\n"
+"BgkqhkiG9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNmzqjMDfz1mgbl\n"
+"dxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx0GtnLLCo\n"
+"4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1TjTQpgcmLNkQfWpb/I\n"
+"mWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBedY2gea+zDTYa4EzAvXUYNR0PV\n"
+"G6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KF\n"
+"dSpcc41teyWRyu5FrgZLAMzTsVlQ2jqIOylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrE\n"
+"gUy7onOTJsjrDNYmiLbAJM+7vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQ\n"
+"mz1wHiRszYd2qReWt88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfuj\n"
+"uLpwQMcnHL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx\n"
+"SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY=\n"
+"-----END CERTIFICATE-----",.len=1866},
+
+/* E-Tugra Certification Authority */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNVBAYTAlRS\n"
+"MQ8wDQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtu\n"
+"b2xvamlsZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlm\n"
+"aWthc3lvbiBNZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9y\n"
+"aXR5MB4XDTEzMDMwNTEyMDk0OFoXDTIzMDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8w\n"
+"DQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xv\n"
+"amlsZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWth\n"
+"c3lvbiBNZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5\n"
+"MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4vU/kwVRHoViVF56C/UYB4Oufq98\n"
+"99SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vdhQd2h8y/L5VMzH2nPbxHD5hw+IyFHnSOkm0b\n"
+"QNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5KCKpbknSFQ9OArqGIW66z6l7LFpp3RMih\n"
+"9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEoq1+gElIwcxmOj+GMB6LDu0rw6h8VqO4l\n"
+"zKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3Dk14opz8n8Y4e0ypQBaNV2cvnOVPAmJ6MVGKLJrD3\n"
+"fY185MaeZkJVgkfnsliNZvcHfC425lAcP9tDJMW/hkd5s3kc91r0E+xs+D/iWR+V7kI+ua2o\n"
+"MoVJl0b+SzGPWsutdEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gzrt48Ue7LE3wBf4QO\n"
+"XVGUnhMMti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAqjqFGOjGY5RH8\n"
+"zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+LznrFpct1pH\n"
+"XFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUXU8u3Zg5mTPj5dUyQ\n"
+"5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6Jyr+zE7S6E5UMA8GA1UdEwEB\n"
+"/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQD\n"
+"AgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAFNzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd\n"
+"0dCrfOAKkEh47U6YA5n+KGCRHTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/\n"
+"u6Au/U5Mh/jOXKqYGwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1\n"
+"Q9Jauz1c77NCR807VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3\n"
+"+GbHeJAAFS6LrVE1Uweoa2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WKvJUawSg5\n"
+"TB9D0pH0clmKuVb8P7Sd2nCcdlqMQ1DujjByTd//SffGqWfZbawCEeI6FiWnWAjLb1NBnEg4\n"
+"R2gz0dfHj9R0IdTDBZB6/86WiLEVKV0jq9BgoRJP3vQXzTLlyb/IQ639Lo7xr+L0mPoSHyDY\n"
+"wKcMhcWQ9DstliaxLL5Mq+ux0orJ23gTDx4JnW2PAJ8C2sH6H3p6CcRK5ogql5+Ji/03X186\n"
+"zjhZhkuvcQu02PJwT58yE+Owp1fl2tpDy4Q08ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9\n"
+"I2cNE3X7z2VnIp2usAnRCf8dNL/+I5c30jn6PQ0GC7TbO6Orb1wdtn7os4I07QZcJA==\n"
+"-----END CERTIFICATE-----",.len=2239},
+
+/* T-TeleSec GlobalRoot Class 2 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNV\n"
+"BAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lz\n"
+"dGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNz\n"
+"IDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzAp\n"
+"BgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQt\n"
+"U3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENs\n"
+"YXNzIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl\n"
+"82hVYAUdAqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC\n"
+"FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi1hKTXrcx\n"
+"lkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6IavqjnKgP6TeMFvvhk1q\n"
+"lVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZwI18gfNycJ5v/hqO2V81xrJv\n"
+"NHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD\n"
+"VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0B\n"
+"AQsFAAOCAQEAMQOiYQsfdOhyNsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSp\n"
+"p+ghla0+AGIWiPACuvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joi\n"
+"fsFvHZVwIEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6\n"
+"g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN9noHV8ci\n"
+"gwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlPBSeOE6Fuwg==\n"
+"-----END CERTIFICATE-----",.len=1363},
+
+/* Atos TrustedRoot 2011 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UEAwwVQXRv\n"
+"cyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0xMTA3\n"
+"MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3Qg\n"
+"MjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IB\n"
+"DwAwggEKAoIBAQCVhTuXbyo7LjvPpvMpNb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI41\n"
+"9KkM/IL9bcFyYie96mvr54rMVD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+\n"
+"yj5vdHLqqjAqc2K+SZFhyBH+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFs\n"
+"Q/H3NYkQ4J7sVaE3IqKHBAUsR320HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0L\n"
+"cp2AMBYHlT8oDv3FdU9T1nSatCQujgKRz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQieowHAgMB\n"
+"AAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7Rl+lwrrw7GWzbITAPBgNVHRMBAf8EBTADAQH/\n"
+"MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZbNshMBgGA1UdIAQRMA8wDQYLKwYBBAGw\n"
+"LQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4IBAQAmdzTblEiGKkGdLD4G\n"
+"kGDEjKwLVLgfuXvTBznk+j57sj1O7Z8jvZfza1zv7v1Apt+hk6EKhqzvINB5Ab149xnYJDE0\n"
+"BAGmuhWawyfc2E8PzBhj/5kPDpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQP\n"
+"OLXiZNwlz6bb65pcmaHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYV\n"
+"qL2Gns2Dlmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv\n"
+"KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed\n"
+"-----END CERTIFICATE-----",.len=1258},
+
+/* QuoVadis Root CA 1 G3 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQELBQAwSDEL\n"
+"MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1Zh\n"
+"ZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00MjAxMTIxNzI3NDRaMEgxCzAJ\n"
+"BgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRp\n"
+"cyBSb290IENBIDEgRzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjuny\n"
+"bEC0BJyFuTHK3C3kEakEPBtVwedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/\n"
+"gpqG7D0DmVIB0jWerNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOG\n"
+"MAqNF34168Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh\n"
+"4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXpUhtStZI5\n"
+"cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+oabw4m6SkltFi2mnA\n"
+"AZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc3sX5kCLliEVx3ZGZbHqfPT2Y\n"
+"fF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/GKubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVd\n"
+"YdHLiZxfokqRmu8hqkkWCKi9YSgxyXSthfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7\n"
+"a3F98N/ETH3Goy7IlXnLc6KOTk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfn\n"
+"kduebPRq34wGmAOtzCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB\n"
+"/wQEAwIBBjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD\n"
+"ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOCMTaIzen7\n"
+"xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2cDMT/uFPpiN3GPoa\n"
+"jOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUNqXsCHKnQO18LwIE6PWThv6ct\n"
+"Tr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvr\n"
+"iBbP+V04ikkwj+3x6xn0dxoxGE1nVGwvb2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCk\n"
+"eF9OrYMh3jRJjehZrJ3ydlo28hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ\n"
+"+p6Q9pxyz0fawx/kNSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTD\n"
+"vdbJWqNjZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp\n"
+"q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFtnh8GKjwS\n"
+"tIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD\n"
+"-----END CERTIFICATE-----",.len=1919},
+
+/* QuoVadis Root CA 2 G3 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQELBQAwSDEL\n"
+"MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1Zh\n"
+"ZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00MjAxMTIxODU5MzJaMEgxCzAJ\n"
+"BgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRp\n"
+"cyBSb290IENBIDIgRzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjc\n"
+"V4g/Ruv5r+LrI3HimtFhZiFfqq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WV\n"
+"HhLL5hSEBMHfNrMWn4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs\n"
+"+L5u+9ymc5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+\n"
+"O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1o9/NgJ8M\n"
+"SPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0jIaFYAI7D0GoT7RPj\n"
+"EiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKqIcGY5jDjZ1XHm26sGahVpkUG\n"
+"0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIh\n"
+"O4M15zHfeiFuuDIIfR0ykRVKYnLP43ehvNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoos\n"
+"FCT5v0ICvybIxo/gbjh9Uy3l7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4\n"
+"r0jyWbYW8jwNkALGcC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB\n"
+"/wQEAwIBBjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD\n"
+"ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66AarHakE7\n"
+"kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RCroijQ1h5fq7KpVMN\n"
+"qT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0GaW/ZZGYjeVYg3UQt4XAoeo0L9\n"
+"x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4nlv1mNDthcnPxFlxHBlRJAHpYErAK74X9\n"
+"sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDb\n"
+"Il9qxV6XU/IyAgkwo1jwDQHVcsaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+\n"
+"D77vfoRrQ+NwmNtddbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpn\n"
+"IdsPNWNgKCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM\n"
+"HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4WSr2Rz0Z\n"
+"iC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M\n"
+"-----END CERTIFICATE-----",.len=1919},
+
+/* QuoVadis Root CA 3 G3 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQELBQAwSDEL\n"
+"MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1Zh\n"
+"ZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00MjAxMTIyMDI2MzJaMEgxCzAJ\n"
+"BgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRp\n"
+"cyBSb290IENBIDMgRzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47q\n"
+"FJenMioKVjZ/aEzHs286IxSR/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O\n"
+"2YIyC0TeytuMrKNuFoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMB\n"
+"OSBDGzXRU7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c\n"
+"ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERRORFHAGjx+f\n"
+"+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/kA9HvFZcba5DFApCT\n"
+"ZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzweyuxwHApw0BiLTtIadwjPEjr\n"
+"ewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634RylsSqiMd5mBPfAdOhx3v89WcyWJhKLhZVX\n"
+"GqtrdQtEPREoPHtht+KPZ0/l7DxMYIBpVzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUC\n"
+"f+D+IOs15xGsIs5XPd7JMG0QA4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/J\n"
+"xHwc64B+27bQ3RP+ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB\n"
+"/wQEAwIBBjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD\n"
+"ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3pxKGmPc+FS\n"
+"kNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnIFUBhynLWcKzSt/Ac\n"
+"5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5WvvoxXqA/4Ti2Tk08HS6IT7SdEQ\n"
+"TXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFgu/BYpbWcC/ePIlUnwEsBbTuZDdQdm2Nn\n"
+"L9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE\n"
+"8/nxoGibIh6BJpsQBJFxwAYf3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8\n"
+"XgBCH/MyJnmDhPbl8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H\n"
+"6QrG2vd+DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN\n"
+"PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ywaZWWDY\n"
+"WGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0\n"
+"-----END CERTIFICATE-----",.len=1919},
+
+/* DigiCert Assured ID Root G2 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBlMQswCQYD\n"
+"VQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu\n"
+"Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwHhcNMTMwODAxMTIw\n"
+"MDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQg\n"
+"SW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1\n"
+"cmVkIElEIFJvb3QgRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82\n"
+"ckmIkzTz+GoeMVSAn61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxN\n"
+"EP4HteccbiJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp\n"
+"EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lAbx3RvpO7\n"
+"04gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6YuYjOuFgJ3RFrngQo8\n"
+"p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMBAAGjQjBAMA8GA1UdEwEB/wQF\n"
+"MAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTOw0q5mVXyuNtgv6l+vVa1lzan1jAN\n"
+"BgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPIQW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTU\n"
+"iaWxMTeKySHMq2zNixya1r9I0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LR\n"
+"dWLjSisCx1BL4GnilmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70\n"
+"CjTVW0z9B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv\n"
+"ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwoIhNzbM8m\n"
+"9Yop5w==\n"
+"-----END CERTIFICATE-----",.len=1303},
+
+/* DigiCert Assured ID Root G3 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQswCQYDVQQG\n"
+"EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29t\n"
+"MSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwHhcNMTMwODAxMTIwMDAw\n"
+"WhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5j\n"
+"MRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVk\n"
+"IElEIFJvb3QgRzMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0F\n"
+"FfLvC/8QdJ+1YlJfZn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+\n"
+"CW7if17QRSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/\n"
+"BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQDAwNnADBk\n"
+"AjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlYJjZ91eQ0hjkCMHw2\n"
+"U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv6pZjamVFkpUBtA==\n"
+"-----END CERTIFICATE-----",.len=848},
+
+/* DigiCert Global Root G2 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBhMQswCQYD\n"
+"VQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu\n"
+"Y29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMjAeFw0xMzA4MDExMjAwMDBa\n"
+"Fw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx\n"
+"GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBS\n"
+"b290IEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/\n"
+"RrohCgiN9RlUyfuI2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxps\n"
+"MNzaHxmx1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ\n"
+"q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5WztCO7TG1F\n"
+"8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQvIOlCsRnKPZzFBQ9\n"
+"RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G\n"
+"A1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcN\n"
+"AQELBQADggEBAGBnKJRvDkhj6zHd6mcY1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVn\n"
+"NeyIv/iPsGEMNKSuIEyExtv4NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2Z\n"
+"L7tcu7XUIOGZX1NGFdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUT\n"
+"Fy8bJZ918rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe\n"
+"pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTflMrY=\n"
+"-----END CERTIFICATE-----",.len=1290},
+
+/* DigiCert Global Root G3 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQswCQYDVQQG\n"
+"EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29t\n"
+"MSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAeFw0xMzA4MDExMjAwMDBaFw0z\n"
+"ODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAX\n"
+"BgNVBAsTEHd3dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290\n"
+"IEczMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu8\n"
+"0JX28MzQC7phW1FGfp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6I\n"
+"p6FrtUPOZ9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd\n"
+"BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIxAK288mw/\n"
+"EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/oAIwOWZbwmSNuJ5Q\n"
+"3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8sycX\n"
+"-----END CERTIFICATE-----",.len=836},
+
+/* DigiCert Trusted Root G4 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"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\n"
+"6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+\n"
+"-----END CERTIFICATE-----",.len=1984},
+
+/* COMODO RSA Certification Authority */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"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\n"
+"-----END CERTIFICATE-----",.len=2081},
+
+/* USERTrust RSA Certification Authority */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"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\n"
+"-----END CERTIFICATE-----",.len=2089},
+
+/* USERTrust ECC Certification Authority */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDELMAkGA1UE\n"
+"BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYD\n"
+"VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2Vy\n"
+"dGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCB\n"
+"iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBD\n"
+"aXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVz\n"
+"dCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQa\n"
+"rFRaqfloI+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng\n"
+"o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0GA1UdDgQW\n"
+"BBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB\n"
+"/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMBzzuqQhFkoJ2UOQIReVx7Hfpk\n"
+"ue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbWRNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbR\n"
+"qZtNyWHa0V1Xahg=\n"
+"-----END CERTIFICATE-----",.len=946},
+
+/* GlobalSign ECC Root CA - R5 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEkMCIGA1UE\n"
+"CxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMw\n"
+"EQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEk\n"
+"MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxT\n"
+"aWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9\n"
+"Xb/pOdEh+J8LttV7HpI6SFkc8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwz\n"
+"ocWdTaRvQZU4f8kehOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMC\n"
+"AQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI\n"
+"KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg515dTguD\n"
+"nFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnOxwy8p2Fp8fc74SrL\n"
+"+SvzZpA3\n"
+"-----END CERTIFICATE-----",.len=792},
+
+/* IdenTrust Commercial Root CA 1 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBKMQswCQYD\n"
+"VQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVy\n"
+"Y2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQwMTE2MTgxMjIzWjBKMQswCQYD\n"
+"VQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVy\n"
+"Y2lhbCBSb290IENBIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k9\n"
+"1DNG8W9RYYKyqU+PZ4ldhNlT3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1e\n"
+"hm7zCYofWjK9ouuU+ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQf\n"
+"Yo3fw7gpS0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1\n"
+"bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORiT0/Br4sO\n"
+"dBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCLvYf5jysjCiN2O/cz\n"
+"4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjKVsk9+w8YfYs7wRPCTY/JTw43\n"
+"6R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZKdHzVWYfCP04MXFL0PfdSgvHqo6z9STQa\n"
+"KPNBiDoT7uje/5kdX7rL6B7yuVBgwDHTc+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h\n"
+"9tgJ4TNkK2PXMl6f+cB7D3hvl7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHw\n"
+"cz09lCqxC0EOoP5NiGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/\n"
+"BAUwAwEB/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD\n"
+"ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH6oi6mYtQ\n"
+"lNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwtLRvM7Kqas6pgghst\n"
+"O8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93nAbowacYXVKV7cndJZ5t+qnt\n"
+"ozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3+wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gj\n"
+"mmmVYjzlVYA211QC//G5Xc7UI2/YRYRKW2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l\n"
+"2xPE4iUXfeu+h1sXIFRRk0pTAwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lz\n"
+"zY9GvlU47/rokTLql1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2\n"
+"gXjtw+hG4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ\n"
+"mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A7/qxXDgG\n"
+"pRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H\n"
+"-----END CERTIFICATE-----",.len=1919},
+
+/* IdenTrust Public Sector Root CA 1 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBNMQswCQYD\n"
+"VQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGlj\n"
+"IFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcNMzQwMTE2MTc1MzMyWjBNMQsw\n"
+"CQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVi\n"
+"bGljIFNlY3RvciBSb290IENBIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2\n"
+"IpT8pEiv6EdrCvsnduTyP4o7ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2L\n"
+"qEfpYnYeEe4IFNGyRBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1\n"
+"B5+ctMlSbdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF\n"
+"/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R3j6HEDbh\n"
+"uaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vwEUs3oERte8uojHH0\n"
+"1bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy9yLxkA2T26pEUWbMfXYD62qo\n"
+"KjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9VGxyhLrXHFub4qjySjmm2AcG1hp2JDws4\n"
+"lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYP\n"
+"Qxxp+pu10GFqzcpL2UyQRqsVWaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsby\n"
+"VtTdX5Vy7W1f90gDW/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD\n"
+"VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN\n"
+"AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qjt2odIFfl\n"
+"AWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHVDRDtfULAj+7AmgjV\n"
+"QdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9TaDKQGXSc3z1i9kKlT/YPyNt\n"
+"GtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8GlwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S\n"
+"3OFtm6/n6J91eEyrRjuazr8FGF1NFTwWmhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHW\n"
+"chezxQMxNRF4eKLg6TCMf4DfWN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF\n"
+"5PgLZxYWxoK4Mhn5+bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57I\n"
+"cXR5f1GJtshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA\n"
+"GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv8Ue1fXws\n"
+"BOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c\n"
+"-----END CERTIFICATE-----",.len=1927},
+
+/* Entrust Root Certification Authority - G2 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMCVVMxFjAU\n"
+"BgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVn\n"
+"YWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9y\n"
+"aXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0\n"
+"aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcyNTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UE\n"
+"BhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVz\n"
+"dC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBm\n"
+"b3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj\n"
+"YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6\n"
+"hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/TRU4cctZOMvJyig/3\n"
+"gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWNcCG0szLni6LVhjkCsbjSR87k\n"
+"yUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hWwcKUs/Ja5CeanyTXxuzQmyWC48zCxEXF\n"
+"jJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+\n"
+"tVOxMyLlbc9wPBr64ptntoP0jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1Ud\n"
+"DwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2f\n"
+"kBJmqzANBgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/\n"
+"jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZRkfz6/dj\n"
+"wUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v1fN2D807iDginWyT\n"
+"msQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4RnAuknZoh8/CbCzB428Hch0P+\n"
+"vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmHVHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ\n"
+"19xOe4pIb4tF9g==\n"
+"-----END CERTIFICATE-----",.len=1530},
+
+/* Entrust Root Certification Authority - EC1 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkGA1UEBhMC\n"
+"VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5u\n"
+"ZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVudHJ1c3QsIEluYy4gLSBmb3Ig\n"
+"YXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMqRW50cnVzdCBSb290IENlcnRpZmljYXRp\n"
+"b24gQXV0aG9yaXR5IC0gRUMxMB4XDTEyMTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8x\n"
+"CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3\n"
+"LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJ\n"
+"bmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD\n"
+"ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABIQT\n"
+"ydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBtByQnoaD41UcZYUx9\n"
+"ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlHBz7MIKNCMEAwDgYDVR0PAQH/\n"
+"BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFLdj5xrdjekIplWDpOBqUEFlEUJJ\n"
+"MAoGCCqGSM49BAMDA2cAMGQCMGF52OVCR98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHv\n"
+"AvX7td/M/k7//qnmpwIwW5nXhTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZB\n"
+"WyVgrtBIGu4G\n"
+"-----END CERTIFICATE-----",.len=1088},
+
+/* CFCA EV ROOT */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJDTjEwMC4G\n"
+"A1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQD\n"
+"DAxDRkNBIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkxMjMxMDMwNzAxWjBWMQswCQYD\n"
+"VQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y\n"
+"aXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJPT1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK\n"
+"AoICAQDXXWvNED8fBVnVBU03sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCj\n"
+"Z9YMrM8irq93VCpLTIpTUnrD7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3Iv\n"
+"HWOX6Jn5/ZOkVIBMUtRSqy5J35DNuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp\n"
+"7hZZLDRJGqgG16iI0gNyejLi6mhNbiyWZXvKWfry4t3uMCz7zEasxGPrb382KzRzEpR/38wm\n"
+"nvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7xzbh72fROdOXW3NiGUgthxwG+3SYIElz8AXS\n"
+"G7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9fpy25IGvPa931DfSCt/SyZi4QKPaXWnuW\n"
+"Fo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqotaK8KgWU6cMGbrU1tVMoqLUuFG7OA5nBF\n"
+"DWteNfB/O7ic5ARwiRIlk9oKmSJgamNgTnYGmE69g60dWIolhdLHZR4tjsbftsbhf4oEIRUp\n"
+"dPA+nJCdDC7xij5aqgwJHsfVPKPtl8MeNPo4+QgO48BdK4PRVmrJtqhUUy54Mmc9gn900Pvh\n"
+"tgVguXDbjgv5E1hvcWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAfBgNVHSMEGDAWgBTj\n"
+"/i39KNALtbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd\n"
+"BgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIBACXGumvr\n"
+"h8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObTej/tUxPQ4i9qecsA\n"
+"IyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdLjOztUmCypAbqTuv0axn96/Ua\n"
+"4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBSESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9B\n"
+"C2sGE5uPhnEFtC+NiWYzKXZUmhH4J/qyP5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rU\n"
+"QElsgIfXBDrDMlI1Dlb4pd19xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZeP\n"
+"glr4UeWJoBjnaH9dCi77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4R\n"
+"UHlzEhLN5mydLIhyPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe\n"
+"/v5WOaHIz16eGWRGENoXkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+ZAAoACxGV\n"
+"2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ5nbv0CO7O6l5s9UC\n"
+"Kc2Jo5YPSjXnTkLAdc0Hz+Ys63su\n"
+"-----END CERTIFICATE-----",.len=1980},
+
+/* OISTE WISeKey Global Root GB CA */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBtMQswCQYD\n"
+"VQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBF\n"
+"bmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQiBDQTAeFw0x\n"
+"NDEyMDExNTAwMzJaFw0zOTEyMDExNTEwMzFaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdX\n"
+"SVNlS2V5MSIwIAYDVQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9P\n"
+"SVNURSBXSVNlS2V5IEdsb2JhbCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n"
+"MIIBCgKCAQEA2Be3HEokKtaXscriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvY\n"
+"D06fWvGxWuR51jIjK+FTzJlFXHtPrby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX\n"
+"1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNku7dCjmn2\n"
+"1HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4oQnc/nSMbsrY9gBQHTC5P99UKFg29ZkM3fiND\n"
+"ecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvgGUpuuy9rM2RYk61pv48b74JIxwIDAQAB\n"
+"o1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUNQ/INmNe4qPs\n"
+"+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZIhvcNAQELBQADggEBAEBM+4ey\n"
+"mYGQfp3FsLAmzYh7KzKNbrghcViXfa43FK8+5/ea4n32cZiZBKpDdHij40lhPnOMTZTg+XHE\n"
+"thYOU3gf1qKHLwI5gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0VQreUGdNZtGn//3Z\n"
+"wLWoo4rOZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEuiHZeeevJuQHHf\n"
+"aPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02IcNc1MaRVU\n"
+"GpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM=\n"
+"-----END CERTIFICATE-----",.len=1343},
+
+/* SZAFIR ROOT CA2 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQELBQAwUTEL\n"
+"MAkGA1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6ZW5pb3dhIFMuQS4x\n"
+"GDAWBgNVBAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkwNzQzMzBaFw0zNTEwMTkwNzQz\n"
+"MzBaMFExCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9LcmFqb3dhIEl6YmEgUm96bGljemVuaW93\n"
+"YSBTLkEuMRgwFgYDVQQDDA9TWkFGSVIgUk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IB\n"
+"DwAwggEKAoIBAQC3vD5QqEvNQLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj\n"
+"5/QqGJ3a0a4m7utT3PSQ1hNKDJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd\n"
+"3BucPbOw3gAeqDRHu5rr/gsUvTaE2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr6\n"
+"3fE9biCloBK0TXC5ztdyO4mTp4CEHCdJckm1/zuVnsHMyAHs6A6KCpbns6aH5db5BSsNl0Bw\n"
+"PLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwiieDhZNRnvDF5YTy7ykHNXGoAyDw4jlivAgMB\n"
+"AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBQuFqlK\n"
+"GLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsFAAOCAQEAtXP4A9xZWx126aMqe5Aosk3A\n"
+"M0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw8PRBEew/R40/cof5O/2kbytTAOD/OblqBw7rHRz2\n"
+"onKQy4I9EYKL0rufKq8h5mOGnXkZ7/e7DDWQw4rtTw/1zBLZpD67oPwglV9PJi8RI4NOdQcP\n"
+"v5vRtB3pEAT+ymCPoky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul4+vJhaAlIDf7js4M\n"
+"NIThPIGyd05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6+/NNIxuZMzSg\n"
+"LvWpCz/UXeHPhJ/iGcJfitYgHuNztw==\n"
+"-----END CERTIFICATE-----",.len=1254},
+
+/* Certum Trusted Network CA 2 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"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\n"
+"-----END CERTIFICATE-----",.len=2073},
+
+/* Hellenic Academic and Research Institutions RootCA 2015 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1IxDzANBgNV\n"
+"BAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIElu\n"
+"c3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWlj\n"
+"IGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTUwHhcNMTUwNzA3MTAxMTIx\n"
+"WhcNNDAwNjMwMTAxMTIxWjCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIG\n"
+"A1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0\n"
+"LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJ\n"
+"bnN0aXR1dGlvbnMgUm9vdENBIDIwMTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC\n"
+"AQDC+Kk/G4n8PDwEXT2QNrCROnk8ZlrvbTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA4yjsriFB\n"
+"zh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+ehiGsxr/CL0BgzuNtFajT0AoAkKAoCFZVedioN\n"
+"mToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+6PAQZe104S+nfK8nNLspfZu2zwnI5dMK\n"
+"/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06CojXdFPQf/7J31Ycvqm59JCfnxssm5uX+\n"
+"Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV9Cz82XBST3i4vTwri5WY9bPRaM8gFH5MXF/ni+X1\n"
+"NYEZN9cRCLdmvtNKzoNXADrDgfgXy5I2XdGj2HUb4Ysn6npIQf1FGQatJ5lOwXBH3bWfgVMS\n"
+"5bGMSF0xQxfjjMZ6Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2fu/Z8VFRfS0myGlZ\n"
+"YeCsargqNhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9muiNX6hME6wGko\n"
+"LfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZaycBw/qa9wf\n"
+"LgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/\n"
+"MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVdctA4GGqd83EkVAswDQYJKoZI\n"
+"hvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0IXtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg\n"
+"2mF+D1hYc2Ryx+hFjtyp8iY/xnmMsVMIM4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6Hwb\n"
+"ISHTGzrMd/K4kPFox/la/vot9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkN\n"
+"aeJK9E10A/+yd+2VZ5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRG\n"
+"ar9KC/eaj8GsGsVn82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnh\n"
+"X9izjFk0WaSrT2y7HxjbdavYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQl033DlZd\n"
+"wJVqwjbDG2jJ9SrcR5q+ss7FJej6A7na+RZukYT1HCjI/CbM1xyQVqdfbzoEvM14iQuODy+j\n"
+"qk+iGxI9FghAD/FGTNeqewjBCvVtJ94Cj8rDtSvK6evIIVM4pcw72Hc3MKJP2W/R8kCtQXoX\n"
+"xdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGaJI7ZjnHKe7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODf\n"
+"qiAeW2GFZECpkJcNrVPSWh2HagCXZWK0vm9qp/UsQu0yrbYhnr68\n"
+"-----END CERTIFICATE-----",.len=2150},
+
+/* Hellenic Academic and Research Institutions ECC RootCA 2015 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzANBgNVBAcT\n"
+"BkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3Rp\n"
+"dHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hlbGxlbmljIEFjYWRlbWljIGFu\n"
+"ZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgRUNDIFJvb3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcx\n"
+"MloXDTQwMDYzMDEwMzcxMlowgaoxCzAJBgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBC\n"
+"BgNVBAoTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2Vy\n"
+"dC4gQXV0aG9yaXR5MUQwQgYDVQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2gg\n"
+"SW5zdGl0dXRpb25zIEVDQyBSb290Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKg\n"
+"QehLgoRc4vgxEZmGZE4JJS+dQS8KrjVPdJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJajq4onvkt\n"
+"TpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoKVlp8aQuqgAkkbH7BRqNCMEAwDwYDVR0TAQH/\n"
+"BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFLQiC4KZJAEOnLvkDv2/+5cgk5kq\n"
+"MAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaeplSTAGiecMjvAwNW6qef4BENThe5SId6d\n"
+"9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7SofTUwJCA3sS61kFyjndc5FZXIhF8siQQ6ME5g4mlRt\n"
+"m8rifOoCWCKR\n"
+"-----END CERTIFICATE-----",.len=1015},
+
+/* ISRG Root X1 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"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\n"
+"-----END CERTIFICATE-----",.len=1935},
+
+/* AC RAIZ FNMT-RCM */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"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\n"
+"qhOLUTpPSPaLtrM=\n"
+"-----END CERTIFICATE-----",.len=1968},
+
+/* Amazon Root CA 1 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsFADA5MQsw\n"
+"CQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAx\n"
+"MB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNV\n"
+"BAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEB\n"
+"BQADggEPADCCAQoCggEBALJ4gHHKeNXjca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOg\n"
+"Q3pOsqTQNroBvo3bSMgHFzZM9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9\n"
+"tBb6dNqcmzU5L/qwIFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAw\n"
+"hmahRWa6VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L\n"
+"93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQmjgSubJrI\n"
+"qg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYE\n"
+"FIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUAA4IBAQCY8jdaQZChGsV2USgg\n"
+"NiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDIU5PMCCjjmCXPI6T53iHTfIUJrU6adTrC\n"
+"C2qJeHZERxhlbI1Bjjt/msv0tadQ1wUsN+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V\n"
+"8viTO96LXFvKWlJbYK8U90vvo/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJc\n"
+"JmApzyMZFo6IQ6XU5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeX\n"
+"eGADbkpyrqXRfboQnoZsG4q5WTP468SQvvG5\n"
+"-----END CERTIFICATE-----",.len=1185},
+
+/* Amazon Root CA 2 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwFADA5MQsw\n"
+"CQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAy\n"
+"MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNV\n"
+"BAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEB\n"
+"BQADggIPADCCAgoCggIBAK2Wny2cSkxKgXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBv\n"
+"IITplLGbhQPDW9tK6Mj4kHbZW0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZM\n"
+"UnbqQ523BNFQ9lXg1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6\n"
+"PBJTYv9K8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r\n"
+"2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+mez/CiVX18\n"
+"JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR8O70zoan4G7ptGmh\n"
+"32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6ZjmUyl+17vIWR6IF9sZIUVyzfp\n"
+"YgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vn\n"
+"SUeVC06JIglJ4PVhHvG/LopyboBZ/1c6+XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3y\n"
+"VAhU/bQtCSwXVEqY0VThUWcI0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8G\n"
+"A1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPl\n"
+"Uq9LhONmUjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2\n"
+"LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY+gn0oJMs\n"
+"XdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kSk5Nrp+gvU5LEYFiw\n"
+"zAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl7uxMMne0nxrpS10gxdr9HIcW\n"
+"xkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygmbtmlyTrIQRNg91CMFa6ybRoVGld45pIq\n"
+"2WWQgj9sAq+uEjonljYE1x2igGOpm/HlurR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JO\n"
+"cQ3AWEbWaQbLU8uz/mtBzUF+fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn\n"
+"9Kr5v2c69BoVYh63n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG\n"
+"6lzWE7OE76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H\n"
+"9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT4PsJYGw=\n"
+"-----END CERTIFICATE-----",.len=1878},
+
+/* Amazon Root CA 3 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5MQswCQYD\n"
+"VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAzMB4X\n"
+"DTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoT\n"
+"BkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMzBZMBMGByqGSM49AgEGCCqGSM49\n"
+"AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKlui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6A\n"
+"F2hiRVd9RFgdszflZwjrZt6jQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGG\n"
+"MB0GA1UdDgQWBBSrttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWS\n"
+"oxe3jfkrBqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM\n"
+"YyRIHN8wfdVoOw==\n"
+"-----END CERTIFICATE-----",.len=654},
+
+/* Amazon Root CA 4 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5MQswCQYD\n"
+"VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSA0MB4X\n"
+"DTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoT\n"
+"BkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgNDB2MBAGByqGSM49AgEGBSuBBAAi\n"
+"A2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhF\n"
+"XRbb/egQbeOc4OO9X4Ri83BkM6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYD\n"
+"VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc\n"
+"84ZtV+WBMAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw\n"
+"CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW1KyLa2tJ\n"
+"ElMzrdfkviT8tQp21KW8EA==\n"
+"-----END CERTIFICATE-----",.len=735},
+
+/* TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIxGDAWBgNV\n"
+"BAcTD0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxpbXNlbCB2ZSBUZWtu\n"
+"b2xvamlrIEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0wKwYDVQQLEyRLYW11IFNlcnRp\n"
+"ZmlrYXN5b24gTWVya2V6aSAtIEthbXUgU00xNjA0BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBT\n"
+"U0wgS29rIFNlcnRpZmlrYXNpIC0gU3VydW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUw\n"
+"ODI1NTVaMIHSMQswCQYDVQQGEwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYD\n"
+"VQQKEzlUdXJraXllIEJpbGltc2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAt\n"
+"IFRVQklUQUsxLTArBgNVBAsTJEthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBT\n"
+"TTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11IFNNIFNTTCBLb2sgU2VydGlmaWthc2kgLSBTdXJ1\n"
+"bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr3UwM6q7a9OZLBI3hNmNe5eA\n"
+"027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y86Ij5iySrLqP1N+RAjhgleYN1Hzv/bKjF\n"
+"xlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INrN3wcwv61A+xXzry0tcXtAA9TNypN9E8M\n"
+"g/uGz8v+jE69h/mniyFXnHrfA2eJLJ2XYacQuFWQfw4tJzh03+f92k4S400VIgLI4OD8D62K\n"
+"18lUUMw7D8oWgITQUVbDjlZ/iSIzL+aFCr2lqBs23tPcLG07xxO9WSMs5uWk99gL7eqQQESo\n"
+"lbuT1dCANLZGeA4fAJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQUZT/HiobGPN08VFw1\n"
+"+DrtUgxHV8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL\n"
+"BQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifhAHe+SMg1\n"
+"qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPfIPP54+M638yclNhO\n"
+"T8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4lzwDGrpDxpa5RXI4s6ehlj2R\n"
+"e37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X\n"
+"8W0jq5Rm+K37DwhuJi1/FwcJsoz7UMCflo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM=\n"
+"-----END CERTIFICATE-----",.len=1578},
+
+/* GDCA TrustAUTH R5 ROOT */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCQ04x\n"
+"MjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8w\n"
+"HQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0MTEyNjA1MTMxNVoXDTQwMTIz\n"
+"MTE1NTk1OVowYjELMAkGA1UEBhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNB\n"
+"VEUgQVVUSE9SSVRZIENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09U\n"
+"MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92\n"
+"hhJCfVZmPoiC7XJjDp6L3TQsAlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlK\n"
+"CvLriXBjTnnEt1u9ol2x8kECK62pOqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+u\n"
+"KU49tm7srsHwJ5uu4/Ts765/94Y9cnrrpftZTqfrlYwiOXnhLQiPzLyRuEH3FMEjqcOtmkVE\n"
+"s7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ9Cy5WmYqsBebnh52nUpmMUHfP/vFBu8btn4a\n"
+"Rjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQxXABZG12ZuGR224HwGGALrIuL4xwp9E7\n"
+"PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloPzgsMR6flPri9fcebNaBhlzpBdRfMK5Z3\n"
+"KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3GkL30SgLdTMEZeS1SZD2fJpcjyIMGC7J0R38IC+xo7\n"
+"0e0gmu9lZJIQDSri3nDxGGeCjGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4oR24qoAATILnsn8JuLww\n"
+"oC8N9VKejveSswoAHQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx9hoh49pwBiFYFIeF\n"
+"d3mqgnkCAwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlRMA8GA1UdEwEB\n"
+"/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfgp8xoWLoB\n"
+"DysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZmDRd9FBUb1Ov9H5r2\n"
+"XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5COmSdI31R9KrO9b7eGZONn35\n"
+"6ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ryL3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1j\n"
+"PLHd+PwyvzeG5LuOmCd+uh8W4XAR8gPfJWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw\n"
+"9C+df/KQHtZa37dG/OaG+svgIHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3\n"
+"cbK1daFQqUBDF8Io2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrX\n"
+"mKGcjBBV09tL7ECQ8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQ\n"
+"XR4EzzffHqhmsYzmIGrv/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrqT8p+ck0L\n"
+"cIymSLumoRT2+1hEmRSuqguTaaApJUqlyyvdimYHFngVV3Eb7PVHhPOeMTd61X8kreS8/f3M\n"
+"boPoDKi3QWwH3b08hpcv0g==\n"
+"-----END CERTIFICATE-----",.len=1976},
+
+/* SSL.com Root Certification Authority RSA */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMx\n"
+"DjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9y\n"
+"YXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBS\n"
+"U0EwHhcNMTYwMjEyMTczOTM5WhcNNDEwMjEyMTczOTM5WjB8MQswCQYDVQQGEwJVUzEOMAwG\n"
+"A1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlv\n"
+"bjExMC8GA1UEAwwoU1NMLmNvbSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCC\n"
+"AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/Wv\n"
+"pOz6Sl2RxFdHaxh3a3by/ZPkPQ/CFp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aX\n"
+"qhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcCC52GVWH9\n"
+"ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/geoeOy3ZExqysdBP+lSgQ36YWkMyv94tZVNHwZ\n"
+"pEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkpk8zruFvh/l8lqjRYyMEjVJ0bmBHDOJx+\n"
+"PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrFYD3ZfBjVsqnTdXgDciLKOsMf7yzlLqn6\n"
+"niy2UUb9rwPW6mBo6oUWNmuF6R7As93EJNyAKoFBbZQ+yODJgUEAnl6/f8UImKIYLEJAs/lv\n"
+"OCdLToD0PYFH4Ih86hzOtXVcUS4cK38acijnALXRdMbX5J+tB5O2UzU1/Dfkw/ZdFr4hc96S\n"
+"CvigY2q8lpJqPvi8ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi81xtZPCvM8hnIk2sn\n"
+"YxnP/Okm+Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4sbE6x/c+cCbqi\n"
+"M+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNVHRMBAf8E\n"
+"BTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4GA1UdDwEB/wQEAwIB\n"
+"hjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGVcpNxJK1ok1iOMq8bs3AD/CUr\n"
+"dIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBcHadm47GUBwwyOabqG7B52B2ccETjit3E\n"
+"+ZUfijhDPwGFpUenPUayvOUiaPd7nNgsPgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52j\n"
+"vATGGAslu1OJD7OAUN5F7kR/q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3\n"
+"oFRuIIhxdRjqerQ0cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfd\n"
+"SSLBv9jra6x+3uxjMxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90I\n"
+"H37hVZkLId6Tngr75qNJvTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/YK9f1JmzJ\n"
+"BjSWFupwWRoyeXkLtoh/D1JIPb9s2KJELtFOt3JY04kTlf5Eq/jXixtunLwsoFvVagCvXzfh\n"
+"1foQC5ichucmj87w7G6KVwuA406ywKBjYZC6VWg3dGq2ktufoYYitmUnDuy2n0Jg5GfCtdpB\n"
+"C8TTi2EbvPofkSvXRAdeuims2cXp71NIWuuA8ShYIc2wBlX7Jz9TkHCpBB5XJ7k=\n"
+"-----END CERTIFICATE-----",.len=2089},
+
+/* SSL.com Root Certification Authority ECC */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMCVVMxDjAM\n"
+"BgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRp\n"
+"b24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBFQ0Mw\n"
+"HhcNMTYwMjEyMTgxNDAzWhcNNDEwMjEyMTgxNDAzWjB8MQswCQYDVQQGEwJVUzEOMAwGA1UE\n"
+"CAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjEx\n"
+"MC8GA1UEAwwoU1NMLmNvbSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAG\n"
+"ByqGSM49AgEGBSuBBAAiA2IABEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtH\n"
+"llirLZXI7Z4INcgn64mMU1jrYor+8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPg\n"
+"CemB+vNH06NjMGEwHQYDVR0OBBYEFILRhXMw5zUE044CkvvlpNHEIejNMA8GA1UdEwEB/wQF\n"
+"MAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTTjgKS++Wk0cQh6M0wDgYDVR0PAQH/BAQDAgGG\n"
+"MAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCWe+0F+S8Tkdzt5fxQaxFGRrMcIQBiu77D\n"
+"5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+gA0z5Wajs6O7pdWLjwkspl1+4vAHCGht\n"
+"0nxpbl/f5Wpl\n"
+"-----END CERTIFICATE-----",.len=942},
+
+/* SSL.com EV Root Certification Authority RSA R2 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"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\n"
+"Y6GsdqnC+w==\n"
+"-----END CERTIFICATE-----",.len=2110},
+
+/* SSL.com EV Root Certification Authority ECC */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMCVVMxDjAM\n"
+"BgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRp\n"
+"b24xNDAyBgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBF\n"
+"Q0MwHhcNMTYwMjEyMTgxNTIzWhcNNDEwMjEyMTgxNTIzWjB/MQswCQYDVQQGEwJVUzEOMAwG\n"
+"A1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlv\n"
+"bjE0MDIGA1UEAwwrU1NMLmNvbSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVD\n"
+"QzB2MBAGByqGSM49AgEGBSuBBAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjK\n"
+"AMTH6kMAVIbc/R/fALhBYlzccBYy3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1Kthku\n"
+"WnBaBu2+8KGwytAJKaNjMGEwHQYDVR0OBBYEFFvKXuXe0oGqzagtZFG22XKbl+ZPMA8GA1Ud\n"
+"EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe5d7SgarNqC1kUbbZcpuX5k8wDgYDVR0PAQH/\n"
+"BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJN+vp1RPZytRrJPOwPYdGWBrssd9v+1a6\n"
+"cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZgh5Mmm7I1HrrW9zzRHM76JTymGoEVW/MS\n"
+"D2zuZYrJh6j5B+BimoxcSg==\n"
+"-----END CERTIFICATE-----",.len=954},
+
+/* GlobalSign Root CA - R6 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"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\n"
+"1gLXCmeDBVrJpBA=\n"
+"-----END CERTIFICATE-----",.len=1968},
+
+/* OISTE WISeKey Global Root GC CA */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQswCQYDVQQG\n"
+"EwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRv\n"
+"cnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQyBDQTAeFw0xNzA1\n"
+"MDkwOTQ4MzRaFw00MjA1MDkwOTU4MzNaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNl\n"
+"S2V5MSIwIAYDVQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNU\n"
+"RSBXSVNlS2V5IEdsb2JhbCBSb290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQ\n"
+"wMYPchi82PG6s4nieUqjFqdrVCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQ\n"
+"mqJLIX4Wp2OQ0jnUsYd4XxiWD1AbNTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8E\n"
+"BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7TrYy0UGYw\n"
+"EAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0EAwMDaAAwZQIwJsdpW9zV57LnyAyMjMPdeYwb\n"
+"Y9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtkAjEA2zQgMgj/mkkCtojeFK9dbJlxjRo/\n"
+"i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9\n"
+"-----END CERTIFICATE-----",.len=893},
+
+/* UCA Global G2 Root */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFRjCCAy6gAwIBAgIQXd+x2lqj7V2+WmUgZQOQ7zANBgkqhkiG9w0BAQsFADA9MQswCQYD\n"
+"VQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxGzAZBgNVBAMMElVDQSBHbG9iYWwgRzIgUm9v\n"
+"dDAeFw0xNjAzMTEwMDAwMDBaFw00MDEyMzEwMDAwMDBaMD0xCzAJBgNVBAYTAkNOMREwDwYD\n"
+"VQQKDAhVbmlUcnVzdDEbMBkGA1UEAwwSVUNBIEdsb2JhbCBHMiBSb290MIICIjANBgkqhkiG\n"
+"9w0BAQEFAAOCAg8AMIICCgKCAgEAxeYrb3zvJgUno4Ek2m/LAfmZmqkywiKHYUGRO8vDaBsG\n"
+"xUypK8FnFyIdK+35KYmToni9kmugow2ifsqTs6bRjDXVdfkX9s9FxeV67HeToI8jrg4aA3++\n"
+"1NDtLnurRiNb/yzmVHqUwCoV8MmNsHo7JOHXaOIxPAYzRrZUEaalLyJUKlgNAQLx+hVRZ2zA\n"
+"+te2G3/RVogvGjqNO7uCEeBHANBSh6v7hn4PJGtAnTRnvI3HLYZveT6OqTwXS3+wmeOwcWDc\n"
+"C/Vkw85DvG1xudLeJ1uK6NjGruFZfc8oLTW4lVYa8bJYS7cSN8h8s+1LgOGN+jIjtm+3SJUI\n"
+"sUROhYw6AlQgL9+/V087OpAh18EmNVQg7Mc/R+zvWr9LesGtOxdQXGLYD0tK3Cv6brxzks3s\n"
+"x1DoQZbXqX5t2Okdj4q1uViSukqSKwxW/YDrCPBeKW4bHAyvj5OJrdu9o54hyokZ7N+1wxrr\n"
+"Fv54NkzWbtA+FxyQF2smuvt6L78RHBgOLXMDj6DlNaBa4kx1HXHhOThTeEDMg5PXCp6dW4+K\n"
+"5OXgSORIskfNTip1KnvyIvbJvgmRlld6iIis7nCs+dwp4wwcOxJORNanTrAmyPPZGpeRaOrv\n"
+"jUYG0lZFWJo8DA+DuAUlwznPO6Q0ibd5Ei9Hxeepl2n8pndntd978XplFeRhVmUCAwEAAaNC\n"
+"MEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFIHEjMz15DD/\n"
+"pQwIX4wVZyF0Ad/fMA0GCSqGSIb3DQEBCwUAA4ICAQATZSL1jiutROTL/7lo5sOASD0Ee/oj\n"
+"L3rtNtqyzm325p7lX1iPyzcyochltq44PTUbPrw7tgTQvPlJ9Zv3hcU2tsu8+Mg51eRfB70V\n"
+"VJd0ysrtT7q6ZHafgbiERUlMjW+i67HM0cOU2kTC5uLqGOiiHycFutfl1qnN3e92mI0ADs0b\n"
+"+gO3joBYDic/UvuUospeZcnWhNq5NXHzJsBPd+aBJ9J3O5oUb3n09tDh05S60FdRvScFDcH9\n"
+"yBIw7m+NESsIndTUv4BFFJqIRNow6rSn4+7vW4LVPtateJLbXDzz2K36uGt/xDYotgIVilQs\n"
+"nLAXc47QN6MUPJiVAAwpBVueSUmxX8fjy88nZY41F7dXyDDZQVu5FLbowg+UMaeUmMxq67Xh\n"
+"J/UQqAHojhJi6IjMtX9Gl8CbEGY4GjZGXyJoPd/JxhMnq1MGrKI8hgZlb7F+sSlEmqO6SWko\n"
+"aY/X5V+tBIZkbxqgDMUIYs6Ao9Dz7GjevjPHF1t/gMRMTLGmhIrDO7gJzRSBuhjjVFc2/tsv\n"
+"fEehOjPI+Vg7RE+xygKJBJYoaMVLuCaJu9YzL1DV/pqJuhgyklTGW+Cd+V7lDSKb9triyCGy\n"
+"YiGqhkCyLmTTX8jjfhFnRR8F/uOi77Oos/N9j/gMHyIfLXC0uAE0djAA5SN4p1bXUB+K+wb1\n"
+"whnw0A==\n"
+"-----END CERTIFICATE-----",.len=1887},
+
+/* UCA Extended Validation Root */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFWjCCA0KgAwIBAgIQT9Irj/VkyDOeTzRYZiNwYDANBgkqhkiG9w0BAQsFADBHMQswCQYD\n"
+"VQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBFeHRlbmRlZCBWYWxp\n"
+"ZGF0aW9uIFJvb3QwHhcNMTUwMzEzMDAwMDAwWhcNMzgxMjMxMDAwMDAwWjBHMQswCQYDVQQG\n"
+"EwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0\n"
+"aW9uIFJvb3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCpCQcoEwKwmeBkqh5D\n"
+"FnpzsZGgdT6o+uM4AHrsiWogD4vFsJszA1qGxliG1cGFu0/GnEBNyr7uaZa4rYEwmnySBesF\n"
+"K5pI0Lh2PpbIILvSsPGP2KxFRv+qZ2C0d35qHzwaUnoEPQc8hQ2E0B92CvdqFN9y4zR8V05W\n"
+"AT558aopO2z6+I9tTcg1367r3CTueUWnhbYFiN6IXSV8l2RnCdm/WhUFhvMJHuxYMjMR83dk\n"
+"sHYf5BA1FxvyDrFspCqjc/wJHx4yGVMR59mzLC52LqGj3n5qiAno8geK+LLNEOfic0CTuwjR\n"
+"P+H8C5SzJe98ptfRr5//lpr1kXuYC3fUfugH0mK1lTnj8/FtDw5lhIpjVMWAtuCeS31HJqcB\n"
+"CF3RiJ7XwzJE+oJKCmhUfzhTA8ykADNkUVkLo4KRel7sFsLzKuZi2irbWWIQJUoqgQtHB0MG\n"
+"cIfS+pMRKXpITeuUx3BNr2fVUbGAIAEBtHoIppB/TuDvB0GHr2qlXov7z1CymlSvw4m6WC31\n"
+"MJixNnI5fkkE/SmnTHnkBVfblLkWU41Gsx2VYVdWf6/wFlthWG82UBEL2KwrlRYaDh8IzTY0\n"
+"ZRBiZtWAXxQgXy0MoHgKaNYs1+lvK9JKBZP8nm9rZ/+I8U6laUpSNwXqxhaN0sSZ0YIrO7o1\n"
+"dfdRUVjzyAfd5LQDfwIDAQABo0IwQDAdBgNVHQ4EFgQU2XQ65DA9DfcS3H5aBZ8eNJr34RQw\n"
+"DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBADaN\n"
+"l8xCFWQpN5smLNb7rhVpLGsaGvdftvkHTFnq88nIua7Mui563MD1sC3AO6+fcAURap8lTwEp\n"
+"cOPlDOHqWnzcSbvBHiqB9RZLcpHIojG5qtr8nR/zXUACE/xOHAbKsxSQVBcZEhrxH9cMaVr2\n"
+"cXj0lH2RC47skFSOvG+hTKv8dGT9cZr4QQehzZHkPJrgmzI5c6sq1WnIeJEmMX3ixzDx/BR4\n"
+"dxIOE/TdFpS/S2d7cFOFyrC78zhNLJA5wA3CXWvp4uXViI3WLL+rG761KIcSF3Ru/H38j9CH\n"
+"JrAb+7lsq+KePRXBOy5nAliRn+/4Qh8st2j1da3Ptfb/EX3C8CSlrdP6oDyp+l3cpaDvRKS+\n"
+"1ujl5BOWF3sGPjLtx7dCvHaj2GU4Kzg1USEODm8uNBNA4StnDG1KQTAYI1oyVZnJF+A83vbs\n"
+"ea0rWBmirSwiGpWOvpaQXUJXxPkUAzUrHC1RVwinOt4/5Mi0A3PCwSaAuwtCH60NryZy2sy+\n"
+"s6ODWA2CxR9GUeOcGMyNm43sSet1UNWMKFnKdDTajAshqx7qG+XH/RU+wBeq+yNuJkbL+vmx\n"
+"cmtpzyKEC2IPrNkZAJSidjzULZrtBJ4tBmIQN1IchXIbJ+XMxjHsN+xjWZsLHXbMfjKaiJUI\n"
+"NlK73nZfdklJrX+9ZSCyycErdhh2n1ax\n"
+"-----END CERTIFICATE-----",.len=1911},
+
+/* Certigna Root CA */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"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\n"
+"2k+KYTlerj1KjL0=\n"
+"-----END CERTIFICATE-----",.len=2260},
+
+/* emSign Root CA - G1 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYDVQQGEwJJ\n"
+"TjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9sb2dpZXMg\n"
+"TGltaXRlZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBHMTAeFw0xODAyMTgxODMwMDBa\n"
+"Fw00MzAyMTgxODMwMDBaMGcxCzAJBgNVBAYTAklOMRMwEQYDVQQLEwplbVNpZ24gUEtJMSUw\n"
+"IwYDVQQKExxlTXVkaHJhIFRlY2hub2xvZ2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24g\n"
+"Um9vdCBDQSAtIEcxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0u76WaK7p1b\n"
+"1TST0Bsew+eeuGQzf2N4aLTNLnF115sgxk0pvLZoYIr3IZpWNVrzdr3YzZr/k1ZLpVkGoZM0\n"
+"Kd0WNHVO8oG0x5ZOrRkVUkr+PHB1cM2vK6sVmjM8qrOLqs1D/fXqcP/tzxE7lM5OMhbTI0Aq\n"
+"d7OvPAEsbO2ZLIvZTmmYsvePQbAyeGHWDV/D+qJAkh1cF+ZwPjXnorfCYuKrpDhMtTk1b+oD\n"
+"afo6VGiFbdbyL0NVHpENDtjVaqSW0RM8LHhQ6DqS0hdW5TUaQBw+jSztOd9C4INBdN+jzcKG\n"
+"YEho42kLVACL5HZpIQ15TjQIXhTCzLG3rdd8cIrHhQIDAQABo0IwQDAdBgNVHQ4EFgQU++8N\n"
+"hp6w492pufEhF38+/PB3KxowDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJ\n"
+"KoZIhvcNAQELBQADggEBAFn/8oz1h31xPaOfG1vR2vjTnGs2vZupYeveFix0PZ7mddrXuqe8\n"
+"QhfnPZHr5X3dPpzxz5KsbEjMwiI/aTvFthUvozXGaCocV685743QNcMYDHsAVhzNixl03r4P\n"
+"EuDQqqE/AjSxcM6dGNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q+Mri/Tm3R7nrft8E\n"
+"I6/6nAYH6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeihU80Bv2noWgby\n"
+"RQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxxiN66zB+A\n"
+"fko=\n"
+"-----END CERTIFICATE-----",.len=1299},
+
+/* emSign ECC Root CA - G3 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQGEwJJTjET\n"
+"MBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGlt\n"
+"aXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMwHhcNMTgwMjE4MTgzMDAw\n"
+"WhcNNDMwMjE4MTgzMDAwWjBrMQswCQYDVQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTEl\n"
+"MCMGA1UEChMcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWdu\n"
+"IEVDQyBSb290IENBIC0gRzMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3i\n"
+"AhKAnjlfSU2fySU0WXTsuwYc58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4P\n"
+"PwT01xySfvalY8L1X44uT6EYGQIrMgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuB\n"
+"zhccLikenEhjQjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQD\n"
+"AwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+DCBeQyh+KTOgNG3qxrdWBCUfvO6wIBHxcmbHt\n"
+"RwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7jHvrZQnD+JbNR6iC8hZVdyR+EhCVBCyj\n"
+"-----END CERTIFICATE-----",.len=856},
+
+/* emSign Root CA - C1 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIDczCCAlugAwIBAgILAK7PALrEzzL4Q7IwDQYJKoZIhvcNAQELBQAwVjELMAkGA1UEBhMC\n"
+"VVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQD\n"
+"ExNlbVNpZ24gUm9vdCBDQSAtIEMxMB4XDTE4MDIxODE4MzAwMFoXDTQzMDIxODE4MzAwMFow\n"
+"VjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEg\n"
+"SW5jMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEMxMIIBIjANBgkqhkiG9w0BAQEFAAOC\n"
+"AQ8AMIIBCgKCAQEAz+upufGZBczYKCFK83M0UYRWEPWgTywS4/oTmifQz/l5GnRfHXk5/Fv4\n"
+"cI7gklL35CX5VIPZHdPIWoU/Xse2B+4+wM6ar6xWQio5JXDWv7V7Nq2s9nPczdcdioOl+yuQ\n"
+"FTdrHCZH3DspVpNqs8FqOp099cGXOFgFixwR4+S0uF2FHYP+eF8LRWgYSKVGczQ7/g/IdrvH\n"
+"GPMF0Ybzhe3nudkyrVWIzqa2kbBPrH4VI5b2P/AgNBbeCsbEBEV5f6f9vtKppa+cxSMq9zwh\n"
+"bL2vj07FOrLzNBL834AaSaTUqZX3noleoomslMuoaJuvimUnzYnu3Yy1aylwQ6BpC+S5DwID\n"
+"AQABo0IwQDAdBgNVHQ4EFgQU/qHgcB4qAzlSWkK+XJGFehiqTbUwDgYDVR0PAQH/BAQDAgEG\n"
+"MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAMJKVvoVIXsoounlHfv4LcQ5\n"
+"lkFMOycsxGwYFYDGrK9HWS8mC+M2sO87/kOXSTKZEhVb3xEp/6tT+LvBeA+snFOvV71ojD1p\n"
+"M/CjoCNjO2RnIkSt1XHLVip4kqNPEjE2NuLe/gDEo2APJ62gsIq1NnpSob0n9CAnYuhNlCQT\n"
+"5AoE6TyrLshDCUrGYQTlSTR+08TI9Q/Aqum6VF7zYytPT1DU/rl7mYw9wC68AivTxEDkigcx\n"
+"HpvOJpkT+xHqmiIMERnHXhuBUDDIlhJu58tBf5E7oke3VIAb3ADMmpDqw8NQBmIMMMAVSKeo\n"
+"WXzhriKi4gp6D/piq1JM4fHfyr6DDUI=\n"
+"-----END CERTIFICATE-----",.len=1254},
+
+/* emSign ECC Root CA - C3 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIICKzCCAbGgAwIBAgIKe3G2gla4EnycqDAKBggqhkjOPQQDAzBaMQswCQYDVQQGEwJVUzET\n"
+"MBEGA1UECxMKZW1TaWduIFBLSTEUMBIGA1UEChMLZU11ZGhyYSBJbmMxIDAeBgNVBAMTF2Vt\n"
+"U2lnbiBFQ0MgUm9vdCBDQSAtIEMzMB4XDTE4MDIxODE4MzAwMFoXDTQzMDIxODE4MzAwMFow\n"
+"WjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEg\n"
+"SW5jMSAwHgYDVQQDExdlbVNpZ24gRUNDIFJvb3QgQ0EgLSBDMzB2MBAGByqGSM49AgEGBSuB\n"
+"BAAiA2IABP2lYa57JhAd6bciMK4G9IGzsUJxlTm801Ljr6/58pc1kjZGDoeVjbk5Wum739D+\n"
+"yAdBPLtVb4OjavtisIGJAnB9SMVK4+kiVCJNk7tCDK93nCOmfddhEc5lx/h//vXyqaNCMEAw\n"
+"HQYDVR0OBBYEFPtaSNCAIEDyqOkAB2kZd6fmw/TPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB\n"
+"Af8EBTADAQH/MAoGCCqGSM49BAMDA2gAMGUCMQC02C8Cif22TGK6Q04ThHK1rt0c3ta13FaP\n"
+"WEBaLd4gTCKDypOofu4SQMfWh0/434UCMBwUZOR8loMRnLDRWmFLpg9J0wD8ofzkpf9/rdcw\n"
+"0Md3f76BB1UwUCAU9Vc4CqgxUQ==\n"
+"-----END CERTIFICATE-----",.len=812},
+
+/* Hongkong Post Root CA 3 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"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\n"
+"-----END CERTIFICATE-----",.len=2069},
+
+/* Entrust Root Certification Authority - G4 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIGSzCCBDOgAwIBAgIRANm1Q3+vqTkPAAAAAFVlrVgwDQYJKoZIhvcNAQELBQAwgb4xCzAJ\n"
+"BgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVu\n"
+"dHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJbmMu\n"
+"IC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0\n"
+"aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0MB4XDTE1MDUyNzExMTExNloXDTM3MTIyNzExNDEx\n"
+"Nlowgb4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9T\n"
+"ZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRy\n"
+"dXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3Qg\n"
+"Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0MIICIjANBgkqhkiG9w0BAQEFAAOC\n"
+"Ag8AMIICCgKCAgEAsewsQu7i0TD/pZJH4i3DumSXbcr3DbVZwbPLqGgZ2K+EbTBwXX7zLtJT\n"
+"meH+H17ZSK9dE43b/2MzTdMAArzE+NEGCJR5WIoV3imz/f3ET+iq4qA7ec2/a0My3dl0ELn3\n"
+"9GjUu9CH1apLiipvKgS1sqbHoHrmSKvS0VnM1n4j5pds8ELl3FFLFUHtSUrJ3hCX1nbB76W1\n"
+"NhSXNdh4IjVS70O92yfbYVaCNNzLiGAMC1rlLAHGVK/XqsEQe9IFWrhAnoanw5CGAlZSCXqc\n"
+"0ieCU0plUmr1POeo8pyvi73TDtTUXm6Hnmo9RR3RXRv06QqsYJn7ibT/mCzPfB3pAqoEmh64\n"
+"3IhuJbNsZvc8kPNXwbMv9W3y+8qh+CmdRouzavbmZwe+LGcKKh9asj5XxNMhIWNlUpEbsZmO\n"
+"eX7m640A2Vqq6nPopIICR5b+W45UYaPrL0swsIsjdXJ8ITzI9vF01Bx7owVV7rtNOzK+mndm\n"
+"nqxpkCIHH2E6lr7lmk/MBTwoWdPBDFSoWWG9yHJM6Nyfh3+9nEg2XpWjDrk4JFX8dWbrAuMI\n"
+"NClKxuMrLzOg2qOGpRKX/YAr2hRC45K9PvJdXmd0LhyIRyk0X+IyqJwlN4y6mACXi0mWHv0l\n"
+"iqzc2thddG5msP9E36EYxr5ILzeUePiVSj9/E15dWf10hkNjc0kCAwEAAaNCMEAwDwYDVR0T\n"
+"AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJ84xFYjwznooHFs6FRM5Og6\n"
+"sb9nMA0GCSqGSIb3DQEBCwUAA4ICAQAS5UKme4sPDORGpbZgQIeMJX6tuGguW8ZAdjwD+MlZ\n"
+"9POrYs4QjbRaZIxowLByQzTSGwv2LFPSypBLhmb8qoMi9IsabyZIrHZ3CL/FmFz0Jomee8O5\n"
+"ZDIBf9PD3Vht7LGrhFV0d4QEJ1JrhkzO3bll/9bGXp+aEJlLdWr+aumXIOTkdnrG0CSqkM0g\n"
+"kLpHZPt/B7NTeLUKYvJzQ85BK4FqLoUWlFPUa19yIqtRLULVAJyZv967lDtX/Zr1hstWO1uI\n"
+"AeV8KEsD+UmDfLJ/fOPtjqF/YFOOVZ1QNBIPt5d7bIdKROf1beyAN/BYGW5KaHbwH5Lk6rWS\n"
+"02FREAutp9lfx1/cH6NcjKF+m7ee01ZvZl4HliDtC3T7Zk6LERXpgUl+b7DUUH8i119lAg2m\n"
+"9IUe2K4GS0qn0jFmwvjO5QimpAKWRGhXxNUzzxkvFMSUHHuk2fCfDrGA4tGeEWSpiBE6doLl\n"
+"YsKA2KSD7ZPvfC+QsDJMlhVoSFLUmQjAJOgc47OlIQ6SwJAfzyBfyjs4x7dtOvPmRLgOMWuI\n"
+"jnDrnBdSqEGULoe256YSxXXfW8AKbnuk5F6G+TaU33fD6Q3AOfF5u0aOq0NZJ7cguyPpVkAh\n"
+"7DE9ZapD8j3fcEThuk0mEDuYn/PIjhs4ViFqUZPTkcpG2om3PVODLAgfi49T3f+sHw==\n"
+"-----END CERTIFICATE-----",.len=2239},
+
+/* Microsoft ECC Root Certificate Authority 2017 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQswCQYDVQQG\n"
+"EwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNyb3Nv\n"
+"ZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwHhcNMTkxMjE4MjMwNjQ1\n"
+"WhcNNDIwNzE4MjMxNjA0WjBlMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENv\n"
+"cnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0\n"
+"aG9yaXR5IDIwMTcwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATUvD0CQnVBEyPNgASGAlEvaqiB\n"
+"YgtlzPbKnR5vSmZRogPZnZH6thaxjG7efM3beaYvzrvOcS/lpaso7GMEZpn4+vKTEAXhgShC\n"
+"48Zo9OYbhGBKia/teQ87zvH2RPUBeMCjVDBSMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8E\n"
+"BTADAQH/MB0GA1UdDgQWBBTIy5lycFIM+Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3FQEEAwIB\n"
+"ADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlfXu5gKcs68tvWMoQZP3zVL8KxzJOuULsJMsbG\n"
+"7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaReNtUjGUBiudQZsIxtzm6uBoiB078a1QW\n"
+"IP8rtedMDE2mT3M=\n"
+"-----END CERTIFICATE-----",.len=873},
+
+/* Microsoft RSA Root Certificate Authority 2017 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBlMQswCQYD\n"
+"VQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNy\n"
+"b3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwHhcNMTkxMjE4MjI1\n"
+"MTIyWhcNNDIwNzE4MjMwMDIzWjBlMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0\n"
+"IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUg\n"
+"QXV0aG9yaXR5IDIwMTcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKW76UM4wp\n"
+"lZEWCpW9R2LBifOZNt9GkMml7Xhqb0eRaPgnZ1AzHaGm++DlQ6OEAlcBXZxIQIJTELy/xzto\n"
+"kLaCLeX0ZdDMbRnMlfl7rEqUrQ7eS0MdhweSE5CAg2Q1OQT85elss7YfUJQ4ZVBcF0a5toW1\n"
+"HLUX6NZFndiyJrDKxHBKrmCk3bPZ7Pw71VdyvD/IybLeS2v4I2wDwAW9lcfNcztmgGTjGqwu\n"
+"+UcF8ga2m3P1eDNbx6H7JyqhtJqRjJHTOoI+dkC0zVJhUXAoP8XFWvLJjEm7FFtNyP9nTUwS\n"
+"lq31/niol4fX/V4ggNyhSyL71Imtus5Hl0dVe49FyGcohJUcaDDv70ngNXtk55iwlNpNhTs+\n"
+"VcQor1fznhPbRiefHqJeRIOkpcrVE7NLP8TjwuaGYaRSMLl6IE9vDzhTyzMMEyuP1pq9Ksgt\n"
+"sRx9S1HKR9FIJ3Jdh+vVReZIZZ2vUpC6W6IYZVcSn2i51BVrlMRpIpj0M+Dt+VGOQVDJNE92\n"
+"kKz8OMHY4Xu54+OU4UZpyw4KUGsTuqwPN1q3ErWQgR5WrlcihtnJ0tHXUeOrO8ZV/R4O03QK\n"
+"0dqq6mm4lyiPSMQH+FJDOvTKVTUssKZqwJz58oHhEmrARdlns87/I6KJClTUFLkqqNfs+avN\n"
+"JVgyeY+QW5g5xAgGwax/Dj0ApQIDAQABo1QwUjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/\n"
+"BAUwAwEB/zAdBgNVHQ4EFgQUCctZf4aycI8awznjwNnpv7tNsiMwEAYJKwYBBAGCNxUBBAMC\n"
+"AQAwDQYJKoZIhvcNAQEMBQADggIBAKyvPl3CEZaJjqPnktaXFbgToqZCLgLNFgVZJ8og6Lq4\n"
+"6BrsTaiXVq5lQ7GPAJtSzVXNUzltYkyLDVt8LkS/gxCP81OCgMNPOsduET/m4xaRhPtthH80\n"
+"dK2Jp86519efhGSSvpWhrQlTM93uCupKUY5vVau6tZRGrox/2KJQJWVggEbbMwSubLWYdFQl\n"
+"3JPk+ONVFT24bcMKpBLBaYVu32TxU5nhSnUgnZUP5NbcA/FZGOhHibJXWpS2qdgXKxdJ5XbL\n"
+"wVaZOjex/2kskZGT4d9Mozd2TaGf+G0eHdP67Pv0RR0Tbc/3WeUiJ3IrhvNXuzDtJE3cfVa7\n"
+"o7P4NHmJweDyAmH3pvwPuxwXC65B2Xy9J6P9LjrRk5Sxcx0ki69bIImtt2dmefU6xqaWM/5T\n"
+"kshGsRGRxpl/j8nWZjEgQRCHLQzWwa80mMpkg/sTV9HB8Dx6jKXB/ZUhoHHBk2dxEuqPiApp\n"
+"GWSZI1b7rCoucL5mxAyE7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9dOLITzNy\n"
+"4ZQ5bb0Sr74MTnB8G2+NszKTc0QWbej09+CVgI+WXTik9KveCjCHk9hNAHFiRSdLOkKEW39l\n"
+"t2c0Ui2cFmuqqNh7o0JMcccMyj6D5KbvtwEwXlGjefVwaaZBRA+GsCyRxj3qrg+E\n"
+"-----END CERTIFICATE-----",.len=2016},
+
+/* e-Szigno Root CA 2017 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNVBAYTAkhV\n"
+"MREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRkLjEXMBUGA1UEYQwO\n"
+"VkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJvb3QgQ0EgMjAxNzAeFw0xNzA4\n"
+"MjIxMjA3MDZaFw00MjA4MjIxMjA3MDZaMHExCzAJBgNVBAYTAkhVMREwDwYDVQQHDAhCdWRh\n"
+"cGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcx\n"
+"HjAcBgNVBAMMFWUtU3ppZ25vIFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEH\n"
+"A0IABJbcPYrYsHtvxie+RJCxs1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9o\n"
+"MrWGQd+HWyx7xf58etqjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G\n"
+"A1UdDgQWBBSHERUI0arBeAyxr87GyZDvvzAEwDAfBgNVHSMEGDAWgBSHERUI0arBeAyxr87G\n"
+"yZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEAtVfd14pVCzbhhkT61NlojbjcI4qKDdQvfepz\n"
+"7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxOsvxyqltZ+efcMQ==\n"
+"-----END CERTIFICATE-----",.len=840},
+
+/* certSIGN Root CA G2 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFRzCCAy+gAwIBAgIJEQA0tk7GNi02MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNVBAYTAlJP\n"
+"MRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04gUk9PVCBDQSBHMjAe\n"
+"Fw0xNzAyMDYwOTI3MzVaFw00MjAyMDYwOTI3MzVaMEExCzAJBgNVBAYTAlJPMRQwEgYDVQQK\n"
+"EwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04gUk9PVCBDQSBHMjCCAiIwDQYJKoZI\n"
+"hvcNAQEBBQADggIPADCCAgoCggIBAMDFdRmRfUR0dIf+DjuW3NgBFszuY5HnC2/OOwppGnzC\n"
+"46+CjobXXo9X69MhWf05N0IwvlDqtg+piNguLWkh59E3GE59kdUWX2tbAMI5Qw02hVK5U2UP\n"
+"HULlj88F0+7cDBrZuIt4ImfkabBoxTzkbFpG583H+u/E7Eu9aqSs/cwoUe+StCmrqzWaTOTE\n"
+"CMYmzPhpn+Sc8CnTXPnGFiWeI8MgwT0PPzhAsP6CRDiqWhqKa2NYOLQV07YRaXseVO6MGiKs\n"
+"cpc/I1mbySKEwQdPzH/iV8oScLumZfNpdWO9lfsbl83kqK/20U6o2YpxJM02PbyWxPFsqa7l\n"
+"zw1uKA2wDrXKUXt4FMMgL3/7FFXhEZn91QqhngLjYl/rNUssuHLoPj1PrCy7Lobio3aP5ZMq\n"
+"z6WryFyNSwb/EkaseMsUBzXgqd+L6a8VTxaJW732jcZZroiFDsGJ6x9nxUWO/203Nit4ZoOR\n"
+"USs9/1F3dmKh7Gc+PoGD4FapUB8fepmrY7+EF3fxDTvf95xhszWYijqy7DwaNz9+j5LP2RIU\n"
+"ZNoQAhVB/0/E6xyjyfqZ90bp4RjZsbgyLcsUDFDYg2WD7rlcz8sFWkz6GZdr1l0T08JcVLwy\n"
+"c6B49fFtHsufpaafItzRUZ6CeWRgKRM+o/1Pcmqr4tTluCRVLERLiohEnMqE0yo7AgMBAAGj\n"
+"QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSCIS1mxteg\n"
+"4BXrzkwJd8RgnlRuAzANBgkqhkiG9w0BAQsFAAOCAgEAYN4auOfyYILVAzOBywaK8SJJ6ejq\n"
+"kX/GM15oGQOGO0MBzwdw5AgeZYWR5hEit/UCI46uuR59H35s5r0l1ZUa8gWmr4UCb6741jH/\n"
+"JclKyMeKqdmfS0mbEVeZkkMR3rYzpMzXjWR91M08KCy0mpbqTfXERMQlqiCA2ClV9+BB/AYm\n"
+"/7k29UMUA2Z44RGx2iBfRgB4ACGlHgAoYXhvqAEBj500mv/0OJD7uNGzcgbJceaBxXntC6Z5\n"
+"8hMLnPddDnskk7RI24Zf3lCGeOdA5jGokHZwYa+cNywRtYK3qq4kNFtyDGkNzVmf9nGvnAvR\n"
+"Cjj5BiKDUyUM/FHE5r7iOZULJK2v0ZXkltd0ZGtxTgI8qoXzIKNDOXZbbFD+mpwUHmUUihW9\n"
+"o4JFWklWatKcsWMy5WHgUyIOpwpJ6st+H6jiYoD2EEVSmAYY3qXNL3+q1Ok+CHLsIwMCPKaq\n"
+"2LxndD0UF/tUSxfj03k9bWtJySgOLnRQvwzZRjoQhsmnP+mg7H/rpXdYaXHmgwo38oZJar55\n"
+"CJD2AhZkPuXaTH4MNMn5X7azKFGnpyuqSfqNZSlO42sTp5SjLVFteAxEy9/eCG/Oo2Sr05WE\n"
+"1LlSVHJ7liXMvGnjSG4N0MedJ5qq+BOS3R7fY581qRY27Iy4g/Q9iY/NtBde17MXQRBdJ3Ng\n"
+"hVdJIgc=\n"
+"-----END CERTIFICATE-----",.len=1887},
+
+/* Trustwave Global Certification Authority */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIF2jCCA8KgAwIBAgIMBfcOhtpJ80Y1LrqyMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQG\n"
+"EwJVUzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRy\n"
+"dXN0d2F2ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0\n"
+"aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMxOTM0MTJaFw00MjA4MjMxOTM0MTJaMIGI\n"
+"MQswCQYDVQQGEwJVUzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAf\n"
+"BgNVBAoMGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEds\n"
+"b2JhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC\n"
+"AgoCggIBALldUShLPDeS0YLOvR29zd24q88KPuFd5dyqCblXAj7mY2Hf8g+CY66j96xz0Xzn\n"
+"swuvCAAJWX/NKSqIk4cXGIDtiLK0thAfLdZfVaITXdHG6wZWiYj+rDKd/VzDBcdu7oaJuogD\n"
+"nXIhhpCujwOl3J+IKMujkkkP7NAP4m1ET4BqstTnoApTAbqOl5F2brz81Ws25kCI1nsvXwXo\n"
+"LG0R8+eyvpJETNKXpP7ScoFDB5zpET71ixpZfR9oWN0EACyW80OzfpgZdNmcc9kYvkHHNHnZ\n"
+"9GLCQ7mzJ7Aiy/k9UscwR7PJPrhq4ufogXBeQotPJqX+OsIgbrv4Fo7NDKm0G2x2EOFYeUY+\n"
+"VM6AqFcJNykbmROPDMjWLBz7BegIlT1lRtzuzWniTY+HKE40Cz7PFNm73bZQmq131BnW2hqI\n"
+"yE4bJ3XYsgjxroMwuREOzYfwhI0Vcnyh78zyiGG69Gm7DIwLdVcEuE4qFC49DxweMqZiNu5m\n"
+"4iK4BUBjECLzMx10coos9TkpoNPnG4CELcU9402x/RpvumUHO1jsQkUm+9jaJXLE9gCxInm9\n"
+"43xZYkqcBW89zubWR2OZxiRvchLIrH+QtAuRcOi35hYQcRfO3gZPSEF9NUqjifLJS3tBEW1n\n"
+"twiYTOURGa5CgNz7kAXU+FDKvuStx8KU1xad5hePrzb7AgMBAAGjQjBAMA8GA1UdEwEB/wQF\n"
+"MAMBAf8wHQYDVR0OBBYEFJngGWcNYtt2s9o9uFvo/ULSMQ6HMA4GA1UdDwEB/wQEAwIBBjAN\n"
+"BgkqhkiG9w0BAQsFAAOCAgEAmHNw4rDT7TnsTGDZqRKGFx6W0OhUKDtkLSGm+J1WE2pIPU/H\n"
+"PinbbViDVD2HfSMF1OQc3Og4ZYbFdada2zUFvXfeuyk3QAUHw5RSn8pk3fEbK9xGChACMf1K\n"
+"aA0HZJDmHvUqoai7PF35owgLEQzxPy0QlG/+4jSHg9bP5Rs1bdID4bANqKCqRieCNqcVtgim\n"
+"QlRXtpla4gt5kNdXElE1GYhBaCXUNxeEFfsBctyV3lImIJgm4nb1J2/6ADtKYdkNy1GTKv0W\n"
+"BpanI5ojSP5RvbbEsLFUzt5sQa0WZ37b/TjNuThOssFgy50X31ieemKyJo90lZvkWx3SD92Y\n"
+"HJtZuSPTMaCm/zjdzyBP6VhWOmfD0faZmZ26NraAL4hHT4a/RDqA5Dccprrql5gR0IRiR2Qe\n"
+"qu5AvzSxnI9O4fKSTx+O856X3vOmeWqJcU9LJxdI/uz0UA9PSX3MReO9ekDFQdxhVicGaeVy\n"
+"QYHTtgGJoC86cnn+OjC/QezHYj6RS8fZMXZC+fc8Y+wmjHMMfRod6qh8h6jCJ3zhM0EPz8/8\n"
+"AKAigJ5Kp28AsEFFtyLKaEjFQqKu3R3y4G5OBVixwJAWKqQ9EEC+j2Jjg6mcgn0tAumDMHzL\n"
+"J8n9HmYAsC7TIS+OMxZsmO0QqAfWzJPP29FpHOTKyeC2nOnOcXHebD8WpHk=\n"
+"-----END CERTIFICATE-----",.len=2085},
+
+/* Trustwave Global ECC P256 Certification Authority */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIICYDCCAgegAwIBAgIMDWpfCD8oXD5Rld9dMAoGCCqGSM49BAMCMIGRMQswCQYDVQQGEwJV\n"
+"UzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0\n"
+"d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDI1\n"
+"NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMxOTM1MTBaFw00MjA4MjMxOTM1\n"
+"MTBaMIGRMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNh\n"
+"Z28xITAfBgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3\n"
+"YXZlIEdsb2JhbCBFQ0MgUDI1NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTBZMBMGByqGSM49\n"
+"AgEGCCqGSM49AwEHA0IABH77bOYj43MyCMpg5lOcunSNGLB4kFKA3TjASh3RqMyTpJcGOMoN\n"
+"FWLGjgEqZZ2q3zSRLoHB5DOSMcT9CTqmP62jQzBBMA8GA1UdEwEB/wQFMAMBAf8wDwYDVR0P\n"
+"AQH/BAUDAwcGADAdBgNVHQ4EFgQUo0EGrJBt0UrrdaVKEJmzsaGLSvcwCgYIKoZIzj0EAwID\n"
+"RwAwRAIgB+ZU2g6gWrKuEZ+Hxbb/ad4lvvigtwjzRM4q3wghDDcCIC0mA6AFvWvR9lz4ZcyG\n"
+"bbOcNEhjhAnFjXca4syc4XR7\n"
+"-----END CERTIFICATE-----",.len=881},
+
+/* Trustwave Global ECC P384 Certification Authority */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIICnTCCAiSgAwIBAgIMCL2Fl2yZJ6SAaEc7MAoGCCqGSM49BAMDMIGRMQswCQYDVQQGEwJV\n"
+"UzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0\n"
+"d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDM4\n"
+"NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMxOTM2NDNaFw00MjA4MjMxOTM2\n"
+"NDNaMIGRMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNh\n"
+"Z28xITAfBgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3\n"
+"YXZlIEdsb2JhbCBFQ0MgUDM4NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTB2MBAGByqGSM49\n"
+"AgEGBSuBBAAiA2IABGvaDXU1CDFHBa5FmVXxERMuSvgQMSOjfoPTfygIOiYaOs+Xgh+AtycJ\n"
+"j9GOMMQKmw6sWASr9zZ9lCOkmwqKi6vr/TklZvFe/oyujUF5nQlgziip04pt89ZF1PKYhDhl\n"
+"oKNDMEEwDwYDVR0TAQH/BAUwAwEB/zAPBgNVHQ8BAf8EBQMDBwYAMB0GA1UdDgQWBBRVqYSJ\n"
+"0sEyvRjLbKYHTsjnnb6CkDAKBggqhkjOPQQDAwNnADBkAjA3AZKXRRJ+oPM+rRk6ct30UJMD\n"
+"Er5E0k9BpIycnR+j9sKS50gU/k6bpZFXrsY3crsCMGclCrEMXu6pY5Jv5ZAL/mYiykf9ijH3\n"
+"g/56vxC+GCsej/YpHpRZ744hN8tRmKVuSw==\n"
+"-----END CERTIFICATE-----",.len=966},
+
+/* NAVER Global Root Certification Authority */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFojCCA4qgAwIBAgIUAZQwHqIL3fXFMyqxQ0Rx+NZQTQ0wDQYJKoZIhvcNAQEMBQAwaTEL\n"
+"MAkGA1UEBhMCS1IxJjAkBgNVBAoMHU5BVkVSIEJVU0lORVNTIFBMQVRGT1JNIENvcnAuMTIw\n"
+"MAYDVQQDDClOQVZFUiBHbG9iYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0x\n"
+"NzA4MTgwODU4NDJaFw0zNzA4MTgyMzU5NTlaMGkxCzAJBgNVBAYTAktSMSYwJAYDVQQKDB1O\n"
+"QVZFUiBCVVNJTkVTUyBQTEFURk9STSBDb3JwLjEyMDAGA1UEAwwpTkFWRVIgR2xvYmFsIFJv\n"
+"b3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK\n"
+"AoICAQC21PGTXLVAiQqrDZBbUGOukJR0F0Vy1ntlWilLp1agS7gvQnXp2XskWjFlqxcX0TM6\n"
+"2RHcQDaH38dq6SZeWYp34+hInDEW+j6RscrJo+KfziFTowI2MMtSAuXaMl3Dxeb57hHHi8lE\n"
+"HoSTGEq0n+USZGnQJoViAbbJAh2+g1G7XNr4rRVqmfeSVPc0W+m/6imBEtRTkZazkVrd/pBz\n"
+"KPswRrXKCAfHcXLJZtM0l/aM9BhK4dA9WkW2aacp+yPOiNgSnABIqKYPszuSjXEOdMWLyEz5\n"
+"9JuOuDxp7W87UC9Y7cSw0BwbagzivESq2M0UXZR4Yb8ObtoqvC8MC3GmsxY/nOb5zJ9TNeID\n"
+"oKAYv7vxvvTWjIcNQvcGufFt7QSUqP620wbGQGHfnZ3zVHbOUzoBppJB7ASjjw2i1QnK1sua\n"
+"8e9DXcCrpUHPXFNwcMmIpi3Ua2FzUCaGYQ5fG8Ir4ozVu53BA0K6lNpfqbDKzE0K70dpAy8i\n"
+"+/Eozr9dUGWokG2zdLAIx6yo0es+nPxdGoMuK8u180SdOqcXYZaicdNwlhVNt0xz7hlcxVs+\n"
+"Qf6sdWA7G2POAN3aCJBitOUt7kinaxeZVL6HSuOpXgRM6xBtVNbv8ejyYhbLgGvtPe31HzCl\n"
+"rkvJE+2KAQHJuFFYwGY6sWZLxNUxAmLpdIQM201GLQIDAQABo0IwQDAdBgNVHQ4EFgQU0p+I\n"
+"36HNLL3s9TsBAZMzJ7LrYEswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJ\n"
+"KoZIhvcNAQEMBQADggIBADLKgLOdPVQG3dLSLvCkASELZ0jKbY7gyKoNqo0hV4/GPnrK21HU\n"
+"UrPUloSlWGB/5QuOH/XcChWB5Tu2tyIvCZwTFrFsDDUIbatjcu3cvuzHV+YwIHHW1xDBE1UB\n"
+"jCpD5EHxzzp6U5LOogMFDTjfArsQLtk70pt6wKGm+LUx5vR1yblTmXVHIloUFcd4G7ad6Qz4\n"
+"G3bxhYTeodoS76TiEJd6eN4MUZeoIUCLhr0N8F5OSza7OyAfikJW4Qsav3vQIkMsRIz75Sq0\n"
+"bBwcupTgE34h5prCy8VCZLQelHsIJchxzIdFV4XTnyliIoNRlwAYl3dqmJLJfGBs32x9SuRw\n"
+"TMKeuB330DTHD8z7p/8Dvq1wkNoL3chtl1+afwkyQf3NosxabUzyqkn+Zvjp2DXrDige7kgv\n"
+"OtB5CTh8piKCk5XQA76+AqAF3SAi428diDRgxuYKuQl1C/AH6GmWNcf7I4GOODm4RStDeKLR\n"
+"LBT/DShycpWbXgnbiUSYqqFJu3FS8r/2/yehNq+4tneI3TqkbZs0kNwUXTC/t+sX5Ie3cdCh\n"
+"13cV1ELX8vMxmV2b3RZtP+oGI/hGoiLtk/bdmuYqh7GYVPEi92tF4+KOdh2ajcQGjTa3FPOd\n"
+"VGm3jjzVpG2Tgbet9r1ke8LJaDmgkpzNNIaRkPpkUZ3+/uul9XXeifdy\n"
+"-----END CERTIFICATE-----",.len=2008},
+
+/* AC RAIZ FNMT-RCM SERVIDORES SEGUROS */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIICbjCCAfOgAwIBAgIQYvYybOXE42hcG2LdnC6dlTAKBggqhkjOPQQDAzB4MQswCQYDVQQG\n"
+"EwJFUzERMA8GA1UECgwIRk5NVC1SQ00xDjAMBgNVBAsMBUNlcmVzMRgwFgYDVQRhDA9WQVRF\n"
+"Uy1RMjgyNjAwNEoxLDAqBgNVBAMMI0FDIFJBSVogRk5NVC1SQ00gU0VSVklET1JFUyBTRUdV\n"
+"Uk9TMB4XDTE4MTIyMDA5MzczM1oXDTQzMTIyMDA5MzczM1oweDELMAkGA1UEBhMCRVMxETAP\n"
+"BgNVBAoMCEZOTVQtUkNNMQ4wDAYDVQQLDAVDZXJlczEYMBYGA1UEYQwPVkFURVMtUTI4MjYw\n"
+"MDRKMSwwKgYDVQQDDCNBQyBSQUlaIEZOTVQtUkNNIFNFUlZJRE9SRVMgU0VHVVJPUzB2MBAG\n"
+"ByqGSM49AgEGBSuBBAAiA2IABPa6V1PIyqvfNkpSIeSX0oNnnvBlUdBeh8dHsVnyV0ebAAKT\n"
+"RBdp20LHsbI6GA60XYyzZl2hNPk2LEnb80b8s0RpRBNm/dfF/a82Tc4DTQdxz69qBdKiQ1oK\n"
+"Um8BA06Oi6NCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE\n"
+"FAG5L++/EYZg8k/QQW6rcx/n0m5JMAoGCCqGSM49BAMDA2kAMGYCMQCuSuMrQMN0EfKVrRYj\n"
+"3k4MGuZdpSRea0R7/DjiT8ucRRcRTBQnJlU5dUoDzBOQn5ICMQD6SmxgiHPz7riYYqnOK8LZ\n"
+"iqZwMR2vsJRM60/G49HzYqc8/5MuB1xJAWdpEgJyv+c=\n"
+"-----END CERTIFICATE-----",.len=901},
+
+/* GlobalSign Root R46 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUAMEYxCzAJ\n"
+"BgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQDExNHbG9iYWxT\n"
+"aWduIFJvb3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAwMDAwMFowRjELMAkGA1UE\n"
+"BhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24g\n"
+"Um9vdCBSNDYwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYd\n"
+"pokNRbopiLKkHWPd08EsCVeJOaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+m\n"
+"sLZ4j5lUlghYruQGvGIFAha/r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTq\n"
+"a1VbkNud316HCkD7rRlr+/fKYIje2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/OrffGFqfUo\n"
+"0q3v84RLHIf8E6M6cqJaESvWJ3En7YEtbWaBkoe0G1h6zD8K+kZPTXhc+CtI4wSEy132tGqz\n"
+"ZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvjK8Cd+RTyG/FWaha/LIWFzXg4mutCagI0GIMX\n"
+"TpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0NXfeD412lPFzYE+cCQYDdF3uYM2HSNrpyibXRd\n"
+"Qr4G9dlkbgIQrImwTDsHTUB+JMWKmIJ5jqSngiCNI/onccnfxkF0oE32kRbcRoxfKWMxWXEM\n"
+"2G/CtjJ9++ZdU6Z+Ffy7dXxd7Pj2Fxzsx2sZy/N78CsHpdlseVR2bJ0cpm4O6XkMqCNqo98b\n"
+"MDGfsVR7/mrLZqrcZdCinkqaByFrgY/bxFn63iLABJzjqls2k+g9vXqhnQt2sQvHnf3PmKgG\n"
+"wvgqo6GDoLclcqUC4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB\n"
+"/zAdBgNVHQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIhvcNAQEMBQADggIBAHx4\n"
+"7PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048p9gkUbJUHJNOxO97k4VgJuoJSOD1\n"
+"u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63bEIaZHU1VNaL8FpO7XJqti2kM3S+LGteWygxk6\n"
+"x9PbTZ4IevPuzz5i+6zoYMzRx6Fcg0XERczzF2sUyQQCPtIkpnnpHs6i58FZFZ8d4kuaPp92\n"
+"CC1r2LpXFNqD6v6MVenQTqnMdzGxRBF6XLE+0xRFFRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J\n"
+"3pZmOUdkLG5NrmJ7v2B0GbhWrJKsFjLtrWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSyB\n"
+"Q7N0H3qqJZ4d16GLuc1CLgSkZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7fXwgNNgyYMq\n"
+"IgXQBztSvwyeqiv5u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTS\n"
+"yxTDYWnP4vkYxboznxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6\n"
+"N3ec592kD3ZDZopD8p/7DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3vouXsXgx\n"
+"T7PntgMTzlSdriVZzH81Xwj3QEUxeCp6\n"
+"-----END CERTIFICATE-----",.len=1911},
+
+/* GlobalSign Root E46 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDMEYxCzAJBgNV\n"
+"BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQDExNHbG9iYWxTaWdu\n"
+"IFJvb3QgRTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAwMDAwMFowRjELMAkGA1UEBhMC\n"
+"QkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9v\n"
+"dCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAScDrHPt+ieUnd1NPqlRqetMhkytAepJ8qU\n"
+"uwzSChDH2omwlwxwEwkBjtjqR+q+soArzfwoDdusvKSGN+1wCAB16pMLey5SnCNoIwZD7JIv\n"
+"U4Tb+0cUB+hflGddyXqBPCCjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/\n"
+"MB0GA1UdDgQWBBQxCpCPtsad0kRLgLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ\n"
+"7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZkvLtoURMMA/cVi4RguYv/Uo7njLwcAjA8+RHUjE7A\n"
+"wWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+CAezNIm8BZ/3Hobui3A=\n"
+"-----END CERTIFICATE-----",.len=767},
+
+/* GLOBALTRUST 2020 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"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\n"
+"vTmR9W0Nv3vXkg==\n"
+"-----END CERTIFICATE-----",.len=1968},
+
+/* ANF Secure Server Root CA */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"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\n"
+"hKd9OyMYkomgjaw=\n"
+"-----END CERTIFICATE-----",.len=2114},
+
+/* Certum EC-384 CA */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIICZTCCAeugAwIBAgIQeI8nXIESUiClBNAt3bpz9DAKBggqhkjOPQQDAzB0MQswCQYDVQQG\n"
+"EwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0\n"
+"dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAXBgNVBAMTEENlcnR1bSBFQy0zODQgQ0Ew\n"
+"HhcNMTgwMzI2MDcyNDU0WhcNNDMwMzI2MDcyNDU0WjB0MQswCQYDVQQGEwJQTDEhMB8GA1UE\n"
+"ChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNh\n"
+"dGlvbiBBdXRob3JpdHkxGTAXBgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwdjAQBgcqhkjOPQIB\n"
+"BgUrgQQAIgNiAATEKI6rGFtqvm5kN2PkzeyrOvfMobgOgknXhimfoZTy42B4mIF4Bk3y7JoO\n"
+"V2CDn7TmFy8as10CW4kjPMIRBSqniBMY81CE1700LCeJVf/OTOffph8oxPBUw7l8t1Ot68Kj\n"
+"QjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI0GZnQkdjrzife81r1HfS+8EF9LMA4G\n"
+"A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNoADBlAjADVS2m5hjEfO/JUG7BJw+ch69u1RsI\n"
+"GL2SKcHvlJF40jocVYli5RsJHrpka/F2tNQCMQC0QoSZ/6vnnvuRlydd3LBbMHHOXjgaatkl\n"
+"5+r3YZJW+OraNsKHZZYuciUvf9/DE8k=\n"
+"-----END CERTIFICATE-----",.len=889},
+
+/* Certum Trusted Root CA */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFwDCCA6igAwIBAgIQHr9ZULjJgDdMBvfrVU+17TANBgkqhkiG9w0BAQ0FADB6MQswCQYD\n"
+"VQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5D\n"
+"ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVk\n"
+"IFJvb3QgQ0EwHhcNMTgwMzE2MTIxMDEzWhcNNDMwMzE2MTIxMDEzWjB6MQswCQYDVQQGEwJQ\n"
+"TDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0g\n"
+"Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3Qg\n"
+"Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDRLY67tzbqbTeRn06TpwXkKQMl\n"
+"zhyC93yZn0EGze2jusDbCSzBfN8pfktlL5On1AFrAygYo9idBcEq2EXxkd7fO9CAAozPOA/q\n"
+"p1x4EaTByIVcJdPTsuclzxFUl6s1wB52HO8AU5853BSlLCIls3Jy/I2z5T4IHhQqNwuIPMqw\n"
+"9MjCoa68wb4pZ1Xi/K1ZXP69VyywkI3C7Te2fJmItdUDmj0VDT06qKhF8JVOJVkdzZhpu9PM\n"
+"MsmN74H+rX2Ju7pgE8pllWeg8xn2A1bUatMn4qGtg/BKEiJ3HAVz4hlxQsDsdUaakFjgao4r\n"
+"pUYwBI4Zshfjvqm6f1bxJAPXsiEodg42MEx51UGamqi4NboMOvJEGyCI98Ul1z3G4z5D3Yf+\n"
+"xOr1Uz5MZf87Sst4WmsXXw3Hw09Omiqi7VdNIuJGmj8PkTQkfVXjjJU30xrwCSss0smNtA0A\n"
+"q2cpKNgB9RkEth2+dv5yXMSFytKAQd8FqKPVhJBPC/PgP5sZ0jeJP/J7UhyM9uH3PAeXjA6i\n"
+"WYEMspA90+NZRu0PqafegGtaqge2Gcu8V/OXIXoMsSt0Puvap2ctTMSYnjYJdmZm/Bo/6khU\n"
+"HL4wvYBQv3y1zgD2DGHZ5yQD4OMBgQ692IU0iL2yNqh7XAjlRICMb/gv1SHKHRzQ+8S1h9E6\n"
+"Tsd2tTVItQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSM+xx1vALTn04u\n"
+"SNn5YFSqxLNP+jAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQENBQADggIBAEii1QALLtA/\n"
+"vBzVtVRJHlpr9OTy4EA34MwUe7nJ+jW1dReTagVphZzNTxl4WxmB82M+w85bj/UvXgF2Ez8s\n"
+"ALnNllI5SW0ETsXpD4YN4fqzX4IS8TrOZgYkNCvozMrnadyHncI013nR03e4qllY/p0m+jiG\n"
+"Pp2Kh2RX5Rc64vmNueMzeMGQ2Ljdt4NR5MTMI9UGfOZR0800McD2RrsLrfw9EAUqO0qRJe6M\n"
+"1ISHgCq8CYyqOhNf6DR5UMEQGfnTKB7U0VEwKbOukGfWHwpjscWpxkIxYxeU72nLL/qMFH3E\n"
+"QxiJ2fAyQOaA4kZf5ePBAFmo+eggvIksDkc0C+pXwlM2/KfUrzHN/gLldfq5Jwn58/U7yn2f\n"
+"qSLLiMmq0Uc9NneoWWRrJ8/vJ8HjJLWG965+Mk2weWjROeiQWMODvA8s1pfrzgzhIMfatz7D\n"
+"P78v3DSk+yshzWePS/Tj6tQ/50+6uaWTRRxmHyH6ZF5v4HaUMst19W7l9o/HuKTMqJZ9ZPsk\n"
+"WkoDbGs4xugDQ5r3V7mzKWmTOPQD8rv7gmsHINFSH5pkAnuYZttcTVoP0ISVoDwUQwbKytu4\n"
+"QTbaakRnh6+v40URFWkIsr4WOZckbxJF0WddCajJFdr60qZfE2Efv4WstK2tBZQIgx51F9Nx\n"
+"O5NQI1mg7TyRVJ12AMXDuDjb\n"
+"-----END CERTIFICATE-----",.len=2049},
+
+/* TunTrust Root CA */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFszCCA5ugAwIBAgIUEwLV4kBMkkaGFmddtLu7sms+/BMwDQYJKoZIhvcNAQELBQAwYTEL\n"
+"MAkGA1UEBhMCVE4xNzA1BgNVBAoMLkFnZW5jZSBOYXRpb25hbGUgZGUgQ2VydGlmaWNhdGlv\n"
+"biBFbGVjdHJvbmlxdWUxGTAXBgNVBAMMEFR1blRydXN0IFJvb3QgQ0EwHhcNMTkwNDI2MDg1\n"
+"NzU2WhcNNDQwNDI2MDg1NzU2WjBhMQswCQYDVQQGEwJUTjE3MDUGA1UECgwuQWdlbmNlIE5h\n"
+"dGlvbmFsZSBkZSBDZXJ0aWZpY2F0aW9uIEVsZWN0cm9uaXF1ZTEZMBcGA1UEAwwQVHVuVHJ1\n"
+"c3QgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMPN0/y9BFPdDCA6\n"
+"1YguBUtB9YOCfvdZn56eY+hz2vYGqU8ftPkLHzmMmiDQfgbU7DTZhrx1W4eI8NLZ1KMKsmwb\n"
+"60ksPqxd2JQDoOw05TDENX37Jk0bbjBU2PWARZw5rZzJJQRNmpA+TkBuimvNKWfGzC3gdOgF\n"
+"VwpIUPp6Q9p+7FuaDmJ2/uqdHYVy7BG7NegfJ7/Boce7SBbdVtfMTqDhuazb1YMZGoXRlJfX\n"
+"yqNlC/M4+QKu3fZnz8k/9YosRxqZbwUN/dAdgjH8KcwAWJeRTIAAHDOFli/LQcKLEITDCSSJ\n"
+"H7UP2dl3RxiSlGBcx5kDPP73lad9UKGAwqmDrViWVSHbhlnUr8a83YFuB9tgYv7sEG7aaAH0\n"
+"gxupPqJbI9dkxt/con3YS7qC0lH4Zr8GRuR5KiY2eY8fTpkdso8MDhz/yV3A/ZAQprE38806\n"
+"JG60hZC/gLkMjNWb1sjxVj8agIl6qeIbMlEsPvLfe/ZdeikZjuXIvTZxi11Mwh0/rViizz1w\n"
+"TaZQmCXcI/m4WEEIcb9PuISgjwBUFfyRbVinljvrS5YnzWuioYasDXxU5mZMZl+QviGaAkYt\n"
+"5IPCgLnPSz7ofzwB7I9ezX/SKEIBlYrilz0QIX32nRzFNKHsLA4KUiwSVXAkPcvCFDVDXSdO\n"
+"vsC9qnyW5/yeYa1E0wCXAgMBAAGjYzBhMB0GA1UdDgQWBBQGmpsfU33x9aTI04Y+oXNZtPdE\n"
+"ITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFAaamx9TffH1pMjThj6hc1m090QhMA4G\n"
+"A1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAqgVutt0Vyb+zxiD2BkewhpMl0425\n"
+"yAA/l/VSJ4hxyXT968pk21vvHl26v9Hr7lxpuhbI87mP0zYuQEkHDVneixCwSQXi/5E/S7fd\n"
+"Ao74gShczNxtr18UnH1YeA32gAm56Q6XKRm4t+v4FstVEuTGfbvE7Pi1HE4+Z7/FXxttbUco\n"
+"qgRYYdZ2vyJ/0Adqp2RT8JeNnYA/u8EH22Wv5psymsNUk8QcCMNE+3tjEUPRahphanltkE8p\n"
+"jkcFwRJpadbGNjHh/PqAulxPxOu3Mqz4dWEX1xAZufHSCe96Qp1bWgvUxpVOKs7/B9dPfhgG\n"
+"iPEZtdmYu65xxBzndFlY7wyJz4sfdZMaBBSSSFCp61cpABbjNhzI+L/wM9VBD8TMPN3pM0MB\n"
+"kRArHtG5Xc0yGYuPjCB31yLEQtyEFpslbei0VXF/sHyz03FJuc9SpAQ/3D2gu68zngowYI7b\n"
+"nV2UqL1g52KAdoGDDIzMMEZJ4gzSqK/rYXHv5yJiqfdcZGyfFoxnNidF9Ql7v/YQCvGwjVRD\n"
+"jAS6oz/v4jXH+XTgbzRB0L9zZVcg+ZtnemZoJE6AZb0QmQZZ8mWvuMZHu/2QeItBcy6vVR/c\n"
+"O5JyboTT0GFMDcx2V+IthSIVNg3rAZ3r2OvEhJn7wAzMMujjd9qDRIueVSjAi1jTkD5OGwDx\n"
+"Fa2DK5o=\n"
+"-----END CERTIFICATE-----",.len=2033},
+
+/* HARICA TLS RSA Root CA 2021 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"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\n"
+"-----END CERTIFICATE-----",.len=2012},
+
+/* HARICA TLS ECC Root CA 2021 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIICVDCCAdugAwIBAgIQZ3SdjXfYO2rbIvT/WeK/zjAKBggqhkjOPQQDAzBsMQswCQYDVQQG\n"
+"EwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0\n"
+"aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBFQ0MgUm9vdCBDQSAyMDIxMB4XDTIxMDIx\n"
+"OTExMDExMFoXDTQ1MDIxMzExMDEwOVowbDELMAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxl\n"
+"bmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hB\n"
+"UklDQSBUTFMgRUNDIFJvb3QgQ0EgMjAyMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDgI/rGg\n"
+"ltJ6rK9JOtDA4MM7KKrxcm1lAEeIhPyaJmuqS7psBAqIXhfyVYf8MLA04jRYVxqEU+kw2any\n"
+"lnTDUR9YSTHMmE5gEYd103KUkE+bECUqqHgtvpBBWJAVcqeht6NCMEAwDwYDVR0TAQH/BAUw\n"
+"AwEB/zAdBgNVHQ4EFgQUyRtTgRL+BNUW0aq8mm+3oJUZbsowDgYDVR0PAQH/BAQDAgGGMAoG\n"
+"CCqGSM49BAMDA2cAMGQCMBHervjcToiwqfAircJRQO9gcS3ujwLEXQNwSaSS6sUUiHCm0w2w\n"
+"qsosQJz76YJumgIwK0eaB8bRwoF8yguWGEEbo/QwCZ61IygNnxS2PFOiTAZpffpskcYqSUXm\n"
+"7LcT4Tps\n"
+"-----END CERTIFICATE-----",.len=865},
+
+/* Autoridad de Certificacion Firmaprofesional CIF A62634068 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIGFDCCA/ygAwIBAgIIG3Dp0v+ubHEwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCRVMx\n"
+"QjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwg\n"
+"Q0lGIEE2MjYzNDA2ODAeFw0xNDA5MjMxNTIyMDdaFw0zNjA1MDUxNTIyMDdaMFExCzAJBgNV\n"
+"BAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zl\n"
+"c2lvbmFsIENJRiBBNjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDK\n"
+"lmuO6vj78aI14H9M2uDDUtd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOS\n"
+"L/UR5GLXMnE42QQMcas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9\n"
+"qFD0sefGL9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i\n"
+"NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2\n"
+"f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44\n"
+"I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCyZ/QYFpM6/EfY0XiWMR+6Kwxf\n"
+"XZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy\n"
+"9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF\n"
+"8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mV\n"
+"BngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMB0GA1UdDgQWBBRlzeurNR4A\n"
+"Pn7VdMActHNHDhpkLzASBgNVHRMBAf8ECDAGAQH/AgEBMIGmBgNVHSAEgZ4wgZswgZgGBFUd\n"
+"IAAwgY8wLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuZmlybWFwcm9mZXNpb25hbC5jb20vY3Bz\n"
+"MFwGCCsGAQUFBwICMFAeTgBQAGEAcwBlAG8AIABkAGUAIABsAGEAIABCAG8AbgBhAG4AbwB2\n"
+"AGEAIAA0ADcAIABCAGEAcgBjAGUAbABvAG4AYQAgADAAOAAwADEANzAOBgNVHQ8BAf8EBAMC\n"
+"AQYwDQYJKoZIhvcNAQELBQADggIBAHSHKAIrdx9miWTtj3QuRhy7qPj4Cx2Dtjqn6EWKB7fg\n"
+"PiDL4QjbEwj4KKE1soCzC1HA01aajTNFSa9J8OA9B3pFE1r/yJfY0xgsfZb43aJlQ3CTkBW6\n"
+"kN/oGbDbLIpgD7dvlAceHabJhfa9NPhAeGIQcDq+fUs5gakQ1JZBu/hfHAsdCPKxsIl68veg\n"
+"4MSPi3i1O1ilI45PVf42O+AMt8oqMEEgtIDNrvx2ZnOorm7hfNoD6JQg5iKj0B+QXSBTFCZX\n"
+"2lSX3xZEEAEeiGaPcjiT3SC3NL7X8e5jjkd5KAb881lFJWAiMxujX6i6KtoaPc1A6ozuBRWV\n"
+"1aUsIC+nmCjuRfzxuIgALI9C2lHVnOUTaHFFQ4ueCyE8S1wF3BqfmI7avSKecs2tCsvMo2eb\n"
+"KHTEm9caPARYpoKdrcd7b/+Alun4jWq9GJAd/0kakFI3ky88Al2CdgtR5xbHV/g4+afNmyJU\n"
+"72OwFW1TZQNKXkqgsqeOSQBZONXH9IBk9W6VULgRfhVwOEqwf9DEMnDAGf/JOC0ULGb0QkTm\n"
+"VXYbgBVX/8Cnp6o5qtjTcNAuuuuUavpfNIbnYrX9ivAwhZTJryQCL2/W3Wf+47BVTwSYT6RB\n"
+"VuKT0Gro1vP7ZeDOdcQxWQzugsgMYDNKGbqEZycPvEJdvSRUDewdcAZfpLz6IHxV\n"
+"-----END CERTIFICATE-----",.len=2162},
+
+/* vTrus ECC Root CA */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIICDzCCAZWgAwIBAgIUbmq8WapTvpg5Z6LSa6Q75m0c1towCgYIKoZIzj0EAwMwRzELMAkG\n"
+"A1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAYBgNVBAMTEXZUcnVz\n"
+"IEVDQyBSb290IENBMB4XDTE4MDczMTA3MjY0NFoXDTQzMDczMTA3MjY0NFowRzELMAkGA1UE\n"
+"BhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAYBgNVBAMTEXZUcnVzIEVD\n"
+"QyBSb290IENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEZVBKrox5lkqqHAjDo6LN/llWQXf9\n"
+"JpRCux3NCNtzslt188+cToL0v/hhJoVs1oVbcnDS/dtitN9Ti72xRFhiQgnH+n9bEOf+QP3A\n"
+"2MMrMudwpremIFUde4BdS49nTPEQo0IwQDAdBgNVHQ4EFgQUmDnNvtiyjPeyq+GtJK97fKHb\n"
+"H88wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwMDaAAwZQIw\n"
+"V53dVvHH4+m4SVBrm2nDb+zDfSXkV5UTQJtS0zvzQBm8JsctBp61ezaf9SXUY2sAAjEA6dPG\n"
+"nlaaKsyh2j/IZivTWJwghfqrkYpwcBE4YGQLYgmRWAD5Tfs0aNoJrSEGGJTO\n"
+"-----END CERTIFICATE-----",.len=771},
+
+/* vTrus Root CA */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFVjCCAz6gAwIBAgIUQ+NxE9izWRRdt86M/TX9b7wFjUUwDQYJKoZIhvcNAQELBQAwQzEL\n"
+"MAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xFjAUBgNVBAMTDXZU\n"
+"cnVzIFJvb3QgQ0EwHhcNMTgwNzMxMDcyNDA1WhcNNDMwNzMxMDcyNDA1WjBDMQswCQYDVQQG\n"
+"EwJDTjEcMBoGA1UEChMTaVRydXNDaGluYSBDby4sTHRkLjEWMBQGA1UEAxMNdlRydXMgUm9v\n"
+"dCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL1VfGHTuB0EYgWgrmy3cLRB\n"
+"6ksDXhA/kFocizuwZotsSKYcIrrVQJLuM7IjWcmOvFjai57QGfIvWcaMY1q6n6MLsLOaXLoR\n"
+"uBLpDLvPbmyAhykUAyyNJJrIZIO1aqwTLDPxn9wsYTwaP3BVm60AUn/PBLn+NvqcwBauYv6W\n"
+"TEN+VRS+GrPSbcKvdmaVayqwlHeFXgQPYh1jdfdr58tbmnDsPmcF8P4HCIDPKNsFxhQnL4Z9\n"
+"8Cfe/+Z+M0jnCx5Y0ScrUw5XSmXX+6KAYPxMvDVTAWqXcoKv8R1w6Jz1717CbMdHflqUhSZN\n"
+"O7rrTOiwCcJlwp2dCZtOtZcFrPUGoPc2BX70kLJrxLT5ZOrpGgrIDajtJ8nU57O5q4IikCc9\n"
+"Kuh8kO+8T/3iCiSn3mUkpF3qwHYw03dQ+A0Em5Q2AXPKBlim0zvc+gRGE1WKyURHuFE5Gi7o\n"
+"NOJ5y1lKCn+8pu8fA2dqWSslYpPZUxlmPCdiKYZNpGvu/9ROutW04o5IWgAZCfEF2c6Rsffr\n"
+"6TlP9m8EQ5pV9T4FFL2/s1m02I4zhKOQUqqzApVg+QxMaPnu1RcN+HFXtSXkKe5lXa/R7jwX\n"
+"C1pDxaWG6iSe4gUH3DRCEpHWOXSuTEGC2/KmSNGzm/MzqvOmwMVO9fSddmPmAsYiS8GVP1Bk\n"
+"LFTltvA8Kc9XAgMBAAGjQjBAMB0GA1UdDgQWBBRUYnBj8XWEQ1iO0RYgscasGrz2iTAPBgNV\n"
+"HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAKbqSSaet\n"
+"8PFww+SX8J+pJdVrnjT+5hpk9jprUrIQeBqfTNqK2uwcN1LgQkv7bHbKJAs5EhWdnxEt/Hlk\n"
+"3ODg9d3gV8mlsnZwUKT+twpw1aA08XXXTUm6EdGz2OyC/+sOxL9kLX1jbhd47F18iMjrjld2\n"
+"2VkE+rxSH0Ws8HqA7Oxvdq6R2xCOBNyS36D25q5J08FsEhvMKar5CKXiNxTKsbhm7xqC5PD4\n"
+"8acWabfbqWE8n/Uxy+QARsIvdLGx14HuqCaVvIivTDUHKgLKeBRtRytAVunLKmChZwOgzoy8\n"
+"sHJnxDHO2zTlJQNgJXtxmOTAGytfdELSS8VZCAeHvsXDf+eW2eHcKJfWjwXj9ZtOyh1QRwVT\n"
+"sMo554WgicEFOwE30z9J4nfrI8iIZjs9OXYhRvHsXyO466JmdXTBQPfYaJqT4i2pLr0cox7I\n"
+"dMakLXogqzu4sEb9b91fUlV1YvCXoHzXOP0l382gmxDPi7g4Xl7FtKYCNqEeXxzP4padKar9\n"
+"mK5S4fNBUvupLnKWnyfjqnN9+BojZns7q2WwMgFLFT49ok8MKzWixtlnEjUwzXYuFrOZnk1P\n"
+"Ti07NEPhmg4NpGaXutIcSkwsKouLgU9xGqndXHt7CMUADTdA43x7VF8vhV929vensBxXVsFy\n"
+"6K2ir40zSbofitzmdHxghm+Hl3s=\n"
+"-----END CERTIFICATE-----",.len=1907},
+
+/* ISRG Root X2 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQswCQYDVQQG\n"
+"EwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNV\n"
+"BAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00MDA5MTcxNjAwMDBaME8xCzAJ\n"
+"BgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBTZWN1cml0eSBSZXNlYXJjaCBHcm91cDEV\n"
+"MBMGA1UEAxMMSVNSRyBSb290IFgyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJ\n"
+"SvMWSj5cz3es3mcFDR0HttwW+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvX\n"
+"RdgKam7mAHf7AlF9ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYw\n"
+"DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI\n"
+"zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdWtL4ndQav\n"
+"Ei51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1/q4AaOeMSQ+2b1tb\n"
+"FfLn\n"
+"-----END CERTIFICATE-----",.len=788},
+
+/* HiPKI Root CA - G1 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"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\n"
+"-----END CERTIFICATE-----",.len=1935},
+
+/* GlobalSign ECC Root CA - R4 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIB3DCCAYOgAwIBAgINAgPlfvU/k/2lCSGypjAKBggqhkjOPQQDAjBQMSQwIgYDVQQLExtH\n"
+"bG9iYWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNV\n"
+"BAMTCkdsb2JhbFNpZ24wHhcNMTIxMTEzMDAwMDAwWhcNMzgwMTE5MDMxNDA3WjBQMSQwIgYD\n"
+"VQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2JhbFNpZ24x\n"
+"EzARBgNVBAMTCkdsb2JhbFNpZ24wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS4xnnTj2wl\n"
+"Dp8uORkcA6SumuU5BwkWymOxuYb4ilfBV85C+nOh92VC/x7BALJucw7/xyHlGKSq2XE/qNS5\n"
+"zowdo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVLB7\n"
+"rUW44kB/+wpu+74zyTyjhNUwCgYIKoZIzj0EAwIDRwAwRAIgIk90crlgr/HmnKAWBVBfw147\n"
+"bmF0774BxL4YSFlhgjICICadVGNA3jdgUM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm\n"
+"-----END CERTIFICATE-----",.len=702},
+
+/* GTS Root R1 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQG\n"
+"EwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RT\n"
+"IFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJV\n"
+"UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJv\n"
+"b3QgUjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreV\n"
+"p0A8of2C+X0yBoJx9vaMf/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4\n"
+"IyfLpLGcY9vXmX7wCl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/S\n"
+"CYe7zUjwTcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl\n"
+"qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaHszVsrBhQ\n"
+"f4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8Y/kB+Xj9e1x3+naH\n"
+"+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmkMiVOKvFlRNACzqrOSbTqn3yD\n"
+"sEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97\n"
+"PtbfkSIS5r762DL8EGMUUXLeXdYWk70paDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmx\n"
+"lbCluQ0WEdrHbEg8QOB+DVrNVjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACY\n"
+"PTrIrnqYNxgFlQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAd\n"
+"BgNVHQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb\n"
+"C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEeQkEzCzc9\n"
+"zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuyh6f88/qBVRRiClmp\n"
+"IgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM47HLwEXWdyzRSjeZ2axfG34ar\n"
+"J45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8JZgfIPxz88NtFMN9iiMG1D53Dn0reWVlH\n"
+"xYciNuaCp+0KueIHoI17eko8cdLiA6EfMgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/b\n"
+"al8xa5meLMFrUKTX5hgUvYU/Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAb\n"
+"d03OIozUhfJFfbdT6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLs\n"
+"QBqvFAnZ0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm\n"
+"2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bbbP6MvPJw\n"
+"NQzcmRk13NfIRmPVNnGuV/u3gm3c\n"
+"-----END CERTIFICATE-----",.len=1907},
+
+/* GTS Root R2 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFVzCCAz+gAwIBAgINAgPlrsWNBCUaqxElqjANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQG\n"
+"EwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RT\n"
+"IFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJV\n"
+"UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJv\n"
+"b3QgUjIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3\n"
+"GTXd98GdVarTzTukk3LvCvptnfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfg\n"
+"LFuv5AS/T3KgGjSY6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/B\n"
+"W9BuXvAuMC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7k\n"
+"RXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXuPuWgf9RhD1FL\n"
+"PD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1mKPV+3PBV2HdKFZ1E66H\n"
+"jucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K8YzodDqs5xoic4DSMPclQsciOzsS\n"
+"rZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RWIr9qS34BIbIjMt/kmkRtWVtd9QCgHJvG\n"
+"eJeNkP+byKq0rxFROV7Z+2et1VsRnTKaG73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9Om\n"
+"TN6SpdTi3/UGVN4unUu0kzCqgc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGF\n"
+"PP0UJAOyh9OktwIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAd\n"
+"BgNVHQ4EFgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBAB/Kzt3H\n"
+"vqGf2SdMC9wXmBFqiN495nFWcrKeGk6c1SuYJF2ba3uwM4IJvd8lRuqYnrYb/oM80mJhwQTt\n"
+"zuDFycgTE1XnqGOtjHsB/ncw4c5omwX4Eu55MaBBRTUoCnGkJE+M3DyCB19m3H0Q/gxhswWV\n"
+"7uGugQ+o+MePTagjAiZrHYNSVc61LwDKgEDg4XSsYPWHgJ2uNmSRXbBoGOqKYcl3qJfEycel\n"
+"/FVL8/B/uWU9J2jQzGv6U53hkRrJXRqWbTKH7QMgyALOWr7Z6v2yTcQvG99fevX4i8buMTol\n"
+"UVVnjWQye+mew4K6Ki3pHrTgSAai/GevHyICc/sgCq+dVEuhzf9gR7A/Xe8bVr2XIZYtCtFe\n"
+"nTgCR2y59PYjJbigapordwj6xLEokCZYCDzifqrXPW+6MYgKBesntaFJ7qBFVHvmJ2WZICGo\n"
+"o7z7GJa7Um8M7YNRTOlZ4iBgxcJlkoKM8xAfDoqXvneCbT+PHV28SSe9zE8P4c52hgQjxcCM\n"
+"Elv924SgJPFI/2R80L5cFtHvma3AH/vLrrw4IgYmZNralw4/KBVEqE8AyvCazM90arQ+POuV\n"
+"7LXTWtiBmelDGDfrs7vRWGJB82bSj6p4lVQgw1oudCvV0b4YacCs1aTPObpRhANl6WLAYv7Y\n"
+"TVWW4tAR+kg0Eeye7QUd5MjWHYbL\n"
+"-----END CERTIFICATE-----",.len=1907},
+
+/* GTS Root R3 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIICCTCCAY6gAwIBAgINAgPluILrIPglJ209ZjAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJV\n"
+"UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJv\n"
+"b3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEi\n"
+"MCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg\n"
+"UjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/\n"
+"JlFyb+Kf1qPKzEUURout736GjOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyR\n"
+"RK2EE46ajA2ADDL24CejQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0G\n"
+"A1UdDgQWBBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEA9uEglRR7\n"
+"VKOQFhG/hMjqb2sXnh5GmCCbn9MN2azTL818+FsuVbu/3ZL3pAzcMeGiAjEA/JdmZuVDFhOD\n"
+"3cffL74UOO0BzrEXGhF16b0DjyZ+hOXJYKaV11RZt+cRLInUue4X\n"
+"-----END CERTIFICATE-----",.len=763},
+
+/* GTS Root R4 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJV\n"
+"UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJv\n"
+"b3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEi\n"
+"MCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg\n"
+"UjQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvV\n"
+"YRg1rkDdc/eJkTBa6zzuhXyiQHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHc\n"
+"i8nHc8iMai/lxKvRHYqjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0G\n"
+"A1UdDgQWBBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D\n"
+"9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/Cr8deVl5c1RxYIigL9zC2L7F8AjEA8GE8p/SgguMh\n"
+"1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh4rsUecrNIdSUtUlD\n"
+"-----END CERTIFICATE-----",.len=763},
+
+/* Telia Root CA v2 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"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\n"
+"-----END CERTIFICATE-----",.len=1947},
+
+/* D-TRUST BR Root CA 1 2020 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIC2zCCAmCgAwIBAgIQfMmPK4TX3+oPyWWa00tNljAKBggqhkjOPQQDAzBIMQswCQYDVQQG\n"
+"EwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRSVVNUIEJSIFJvb3Qg\n"
+"Q0EgMSAyMDIwMB4XDTIwMDIxMTA5NDUwMFoXDTM1MDIxMTA5NDQ1OVowSDELMAkGA1UEBhMC\n"
+"REUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAGA1UEAxMZRC1UUlVTVCBCUiBSb290IENB\n"
+"IDEgMjAyMDB2MBAGByqGSM49AgEGBSuBBAAiA2IABMbLxyjR+4T1mu9CFCDhQ2tuda38KwOE\n"
+"1HaTJddZO0Flax7mNCq7dPYSzuht56vkPE4/RAiLzRZxy7+SmfSk1zxQVFKQhYN4lGdnoxwJ\n"
+"GT11NIXe7WB9xwy0QVK5buXuQqOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE\n"
+"FHOREKv/VbNafAkl1bK6CKBrqx9tMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g\n"
+"PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2JyX3Jvb3RfY2FfMV8y\n"
+"MDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNU\n"
+"JTIwQlIlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1c3QlMjBHbWJILEM9REU/Y2Vy\n"
+"dGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjOPQQDAwNpADBmAjEAlJAtE/rhY/hhY+it\n"
+"hXhUkZy4kzg+GkHaQBZTQgjKL47xPoFWwKrY7RjEsK70PvomAjEA8yjixtsrmfu3Ubgko6SU\n"
+"eho/5jbiA1czijDLgsfWFBHVdWNbFJWcHwHP2NVypw87\n"
+"-----END CERTIFICATE-----",.len=1047},
+
+/* D-TRUST EV Root CA 1 2020 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIC2zCCAmCgAwIBAgIQXwJB13qHfEwDo6yWjfv/0DAKBggqhkjOPQQDAzBIMQswCQYDVQQG\n"
+"EwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRSVVNUIEVWIFJvb3Qg\n"
+"Q0EgMSAyMDIwMB4XDTIwMDIxMTEwMDAwMFoXDTM1MDIxMTA5NTk1OVowSDELMAkGA1UEBhMC\n"
+"REUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAGA1UEAxMZRC1UUlVTVCBFViBSb290IENB\n"
+"IDEgMjAyMDB2MBAGByqGSM49AgEGBSuBBAAiA2IABPEL3YZDIBnfl4XoIkqbz52Yv7QFJsnL\n"
+"46bSj8WeeHsxiamJrSc8ZRCC/N/DnU7wMyPE0jL1HLDfMxddxfCxivnvubcUyilKwg+pf3Vl\n"
+"SSowZ/Rk99Yad9rDwpdhQntJraOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE\n"
+"FH8QARY3OqQo5FD4pPfsazK2/umLMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g\n"
+"PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2V2X3Jvb3RfY2FfMV8y\n"
+"MDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNU\n"
+"JTIwRVYlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1c3QlMjBHbWJILEM9REU/Y2Vy\n"
+"dGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjOPQQDAwNpADBmAjEAyjzGKnXCXnViOTYA\n"
+"YFqLwZOZzNnbQTs7h5kXO9XMT8oi96CAy/m0sRtW9XLS/BnRAjEAkfcwkz8QRitxpNA7RJvA\n"
+"KQIFskF3UfN5Wp6OFKBOQtJbgfM0agPnIjhQW+0ZT0MW\n"
+"-----END CERTIFICATE-----",.len=1047},
+
+/* DigiCert TLS ECC P384 Root G5 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQswCQYDVQQG\n"
+"EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURpZ2lDZXJ0IFRMUyBF\n"
+"Q0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2MDExNDIzNTk1OVowTjELMAkG\n"
+"A1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBU\n"
+"TFMgRUNDIFAzODQgUm9vdCBHNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOC\n"
+"NQfN0MsYndLxf3c1TzvdlHJS7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cK\n"
+"PGEQQil8pQgO4CLp0zVozptjn4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7n\n"
+"LFr6ICISB4CIfBFqMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49\n"
+"BAMDA2gAMGUCMQCJao1H5+z8blUD2WdsJk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQLgGheQaR\n"
+"nUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIxAJSdYsiJvRmEFOml+wG4DXZDjC5Ty3zfDBeW\n"
+"UA==\n"
+"-----END CERTIFICATE-----",.len=788},
+
+/* DigiCert TLS RSA4096 Root G5 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFZjCCA06gAwIBAgIQCPm0eKj6ftpqMzeJ3nzPijANBgkqhkiG9w0BAQwFADBNMQswCQYD\n"
+"VQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRM\n"
+"UyBSU0E0MDk2IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcNNDYwMTE0MjM1OTU5WjBNMQsw\n"
+"CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0\n"
+"IFRMUyBSU0E0MDk2IFJvb3QgRzUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz\n"
+"0PTJeRGd/fxmgefM1eS87IE+ajWOLrfn3q/5B03PMJ3qCQuZvWxX2hhKuHisOjmopkisLnLl\n"
+"vevxGs3npAOpPxG02C+JFvuUAT27L/gTBaF4HI4o4EXgg/RZG5Wzrn4DReW+wkL+7vI8toUT\n"
+"mDKdFqgpwgscONyfMXdcvyej/Cestyu9dJsXLfKB2l2w4SMXPohKEiPQ6s+d3gMXsUJKoBZM\n"
+"pG2T6T867jp8nVid9E6P/DsjyG244gXazOvswzH016cpVIDPRFtMbzCe88zdH5RDnU1/cHAN\n"
+"1DrRN/BsnZvAFJNY781BOHW8EwOVfH/jXOnVDdXifBBiqmvwPXbzP6PosMH976pXTayGpxi0\n"
+"KcEsDr9kvimM2AItzVwv8n/vFfQMFawKsPHTDU9qTXeXAaDxZre3zu/O7Oyldcqs4+Fj97ih\n"
+"BMi8ez9dLRYiVu1ISf6nL3kwJZu6ay0/nTvEF+cdLvvyz6b84xQslpghjLSR6Rlgg/IwKwZz\n"
+"UNWYOwbpx4oMYIwo+FKbbuH2TbsGJJvXKyY//SovcfXWJL5/MZ4PbeiPT02jP/816t9JXkGP\n"
+"hvnxd3lLG7SjXi/7RgLQZhNeXoVPzthwiHvOAbWWl9fNff2C+MIkwcoBOU+NosEUQB+cZtUM\n"
+"CUbW8tDRSHZWOkPLtgoRObqME2wGtZ7P6wIDAQABo0IwQDAdBgNVHQ4EFgQUUTMc7TZArxfT\n"
+"Jc1paPKvTiM+s0EwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcN\n"
+"AQEMBQADggIBAGCmr1tfV9qJ20tQqcQjNSH/0GEwhJG3PxDPJY7Jv0Y02cEhJhxwGXIeo8mH\n"
+"/qlDZJY6yFMECrZBu8RHANmfGBg7sg7zNOok992vIGCukihfNudd5N7HPNtQOa27PShNlnx2\n"
+"xlv0wdsUpasZYgcYQF+Xkdycx6u1UQ3maVNVzDl92sURVXLFO4uJ+DQtpBflF+aZfTCIITfN\n"
+"MBc9uPK8qHWgQ9w+iUuQrm0D4ByjoJYJu32jtyoQREtGBzRj7TG5BO6jm5qu5jF49OokYTur\n"
+"WGT/u4cnYiWB39yhL/btp/96j1EuMPikAdKFOV8BmZZvWltwGUb+hmA+rYAQCd05JS9Yf7vS\n"
+"dPD3Rh9GOUrYU9DzLjtxpdRv/PNn5AeP3SYZ4Y1b+qOTEZvpyDrDVWiakuFSdjjo4bq9+0/V\n"
+"77PnSIMx8IIh47a+p6tv75/fTM8BuGJqIz3nCU2AG3swpMPdB380vqQmsvZB6Akd4yCYqjdP\n"
+"//fx4ilwMUc/dNAUFvohigLVigmUdy7yWSiLfFCSCmZ4OIN1xLVaqBHG5cGdZlXPU8Sv13WF\n"
+"qUITVuwhd4GTWgzqltlJyqEI8pc7bZsEGCREjnwB8twl2F6GmrE52/WRMmrRpnCKovfepEWF\n"
+"JqgejF0pW8hL2JpqA15w8oVPbEtoL8pU9ozaMv7Da4M/OMZ+\n"
+"-----END CERTIFICATE-----",.len=1927},
+
+/* Certainly Root R1 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIFRzCCAy+gAwIBAgIRAI4P+UuQcWhlM1T01EQ5t+AwDQYJKoZIhvcNAQELBQAwPTELMAkG\n"
+"A1UEBhMCVVMxEjAQBgNVBAoTCUNlcnRhaW5seTEaMBgGA1UEAxMRQ2VydGFpbmx5IFJvb3Qg\n"
+"UjEwHhcNMjEwNDAxMDAwMDAwWhcNNDYwNDAxMDAwMDAwWjA9MQswCQYDVQQGEwJVUzESMBAG\n"
+"A1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0YWlubHkgUm9vdCBSMTCCAiIwDQYJKoZI\n"
+"hvcNAQEBBQADggIPADCCAgoCggIBANA21B/q3avk0bbm+yLA3RMNansiExyXPGhjZjKcA7WN\n"
+"pIGD2ngwEc/csiu+kr+O5MQTvqRoTNoCaBZ0vrLdBORrKt03H2As2/X3oXyVtwxwhi7xOu9S\n"
+"98zTm/mLvg7fMbedaFySpvXl8wo0tf97ouSHocavFwDvA5HtqRxOcT3Si2yJ9HiG5mpJoM61\n"
+"0rCrm/b01C7jcvk2xusVtyWMOvwlDbMicyF0yEqWYZL1LwsYpfSt4u5BvQF5+paMjRcCMLT5\n"
+"r3gajLQ2EBAHBXDQ9DGQilHFhiZ5shGIXsXwClTNSaa/ApzSRKft43jvRl5tcdF5cBxGX1Hp\n"
+"yTfcX35pe0HfNEXgO4T0oYoKNp43zGJS4YkNKPl6I7ENPT2a/Z2B7yyQwHtETrtJ4A5KVpK8\n"
+"y7XdeReJkd5hiXSSqOMyhb5OhaRLWcsrxXiOcVTQAjeZjOVJ6uBUcqQRBi8LjMFbvrWhsFNu\n"
+"nLhgkR9Za/kt9JQKl7XsxXYDVBtlUrpMklZRNaBA2CnbrlJ2Oy0wQJuK0EJWtLeIAaSHO1OW\n"
+"zaMWj/Nmqhexx2DgwUMFDO6bW2BvBlyHWyf5QBGenDPBt+U1VwV/J84XIIwc/PH72jEpSe31\n"
+"C4SnT8H2TsIonPru4K8H+zMReiFPCyEQtkA6qyI6BJyLm4SGcprSp6XEtHWRqSsjAgMBAAGj\n"
+"QjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTgqj8ljZ9E\n"
+"XME66C6ud0yEPmcM9DANBgkqhkiG9w0BAQsFAAOCAgEAuVevuBLaV4OPaAszHQNTVfSVcOQr\n"
+"PbA56/qJYv331hgELyE03fFo8NWWWt7CgKPBjcZq91l3rhVkz1t5BXdm6ozTaw3d8VkswTOl\n"
+"MIAVRQdFGjEitpIAq5lNOo93r6kiyi9jyhXWx8bwPWz8HA2YEGGeEaIi1wrykXprOQ4vMMM2\n"
+"SZ/g6Q8CRFA3lFV96p/2O7qUpUzpvD5RtOjKkjZUbVwlKNrdrRT90+7iIgXr0PK3aBLXWopB\n"
+"GsaSpVo7Y0VPv+E6dyIvXL9G+VoDhRNCX8reU9ditaY1BMJH/5n9hN9czulegChB8n3nHpDY\n"
+"T3Y+gjwN/KUD+nsa2UUeYNrEjvn8K8l7lcUq/6qJ34IxD3L/DCfXCh5WAFAeDJDBlrXYFIW7\n"
+"pw0WwfgHJBu6haEaBQmAupVjyTrsJZ9/nbqkRxWbRHDxakvWOF5D8xh+UG7pWijmZeZ3Gzr9\n"
+"Hb4DJqPb1OG7fpYnKx3upPvaJVQTA945xsMfTZDsjxtK0hzthZU4UHlG1sGQUDGpXJpuHfUz\n"
+"VounmdLyyCwzk5Iwx06MZTMQZBf9JBeW0Y3COmor6xOLRPIh80oat3df1+2IpHLlOR+Vnb5n\n"
+"wXARPbv0+Em34yaXOp/SX3z7wJl8OSngex2/DaeP0ik0biQVy96QXr8axGbqwua6OV+KmalB\n"
+"WQewLK8=\n"
+"-----END CERTIFICATE-----",.len=1887},
+
+/* Certainly Root E1 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIB9zCCAX2gAwIBAgIQBiUzsUcDMydc+Y2aub/M+DAKBggqhkjOPQQDAzA9MQswCQYDVQQG\n"
+"EwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0YWlubHkgUm9vdCBFMTAe\n"
+"Fw0yMTA0MDEwMDAwMDBaFw00NjA0MDEwMDAwMDBaMD0xCzAJBgNVBAYTAlVTMRIwEAYDVQQK\n"
+"EwlDZXJ0YWlubHkxGjAYBgNVBAMTEUNlcnRhaW5seSBSb290IEUxMHYwEAYHKoZIzj0CAQYF\n"
+"K4EEACIDYgAE3m/4fxzf7flHh4axpMCK+IKXgOqPyEpeKn2IaKcBYhSRJHpcnqMXfYqGITQY\n"
+"UBsQ3tA3SybHGWCA6TS9YBk2QNYphwk8kXr2vBMj3VlOBF7PyAIcGFPBMdjaIOlEjeR2o0Iw\n"
+"QDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ygYy2R17ikq\n"
+"6+2uI1g4hevIIgcwCgYIKoZIzj0EAwMDaAAwZQIxALGOWiDDshliTd6wT99u0nCK8Z9+aozm\n"
+"ut6Dacpps6kFtZaSF4fC0urQe87YQVt8rgIwRt7qy12a7DLCZRawTDBcMPPaTnOGBtjOiQRI\n"
+"Nzf43TNRnXCve1XYAS59BWQOhriR\n"
+"-----END CERTIFICATE-----",.len=739},
+
+/* E-Tugra Global Root CA RSA v3 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIIF8zCCA9ugAwIBAgIUDU3FzRYilZYIfrgLfxUGNPt5EDQwDQYJKoZIhvcNAQELBQAwgYAx\n"
+"CzAJBgNVBAYTAlRSMQ8wDQYDVQQHEwZBbmthcmExGTAXBgNVBAoTEEUtVHVncmEgRUJHIEEu\n"
+"Uy4xHTAbBgNVBAsTFEUtVHVncmEgVHJ1c3QgQ2VudGVyMSYwJAYDVQQDEx1FLVR1Z3JhIEds\n"
+"b2JhbCBSb290IENBIFJTQSB2MzAeFw0yMDAzMTgwOTA3MTdaFw00NTAzMTIwOTA3MTdaMIGA\n"
+"MQswCQYDVQQGEwJUUjEPMA0GA1UEBxMGQW5rYXJhMRkwFwYDVQQKExBFLVR1Z3JhIEVCRyBB\n"
+"LlMuMR0wGwYDVQQLExRFLVR1Z3JhIFRydXN0IENlbnRlcjEmMCQGA1UEAxMdRS1UdWdyYSBH\n"
+"bG9iYWwgUm9vdCBDQSBSU0EgdjMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCi\n"
+"ZvCJt3J77gnJY9LTQ91ew6aEOErxjYG7FL1H6EAX8z3DeEVypi6Q3po61CBxyryfHUuXCscx\n"
+"uj7X/iWpKo429NEvx7epXTPcMHD4QGxLsqYxYdE0PD0xesevxKenhOGXpOhL9hd87jwH7eKK\n"
+"V9y2+/hDJVDqJ4GohryPUkqWOmAalrv9c/SF/YP9f4RtNGx/ardLAQO/rWm31zLZ9Vdq6YaC\n"
+"PqVmMbMWPcLzJmAy01IesGykNz709a/r4d+ABs8qQedmCeFLl+d3vSFtKbZnwy1+7dZ5ZdHP\n"
+"OrbRsV5WYVB6Ws5OUDGAA5hH5+QYfERaxqSzO8bGwzrwbMOLyKSRBfP12baqBqG3q+Sx6iEU\n"
+"XIOk/P+2UNOMEiaZdnDpwA+mdPy70Bt4znKS4iicvObpCdg604nmvi533wEKb5b25Y08TVJ2\n"
+"Glbhc34XrD2tbKNSEhhw5oBOM/J+JjKsBY04pOZ2PJ8QaQ5tndLBeSBrW88zjdGUdjXnXVXH\n"
+"t6woq0bM5zshtQoK5EpZ3IE1S0SVEgpnpaH/WwAH0sDM+T/8nzPyAPiMbIedBi3x7+PmBvrF\n"
+"ZhNb/FAHnnGGstpvdDDPk1Po3CLW3iAfYY2jLqN4MpBs3KwytQXk9TwzDdbgh3cXTJ2w2Amo\n"
+"DVf3RIXwyAS+XF1a4xeOVGNpf0l0ZAWMowIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MB8G\n"
+"A1UdIwQYMBaAFLK0ruYt9ybVqnUtdkvAG1Mh0EjvMB0GA1UdDgQWBBSytK7mLfcm1ap1LXZL\n"
+"wBtTIdBI7zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIBAImocn+M684uGMQQ\n"
+"gC0QDP/7FM0E4BQ8Tpr7nym/Ip5XuYJzEmMmtcyQ6dIqKe6cLcwsmb5FJ+Sxce3kOJUxQfJ9\n"
+"emN438o2Fi+CiJ+8EUdPdk3ILY7r3y18Tjvarvbj2l0Upq7ohUSdBm6O++96SmotKygY/r+Q\n"
+"LHUWnw/qln0F7psTpURs+APQ3SPh/QMSEgj0GDSz4DcLdxEBSL9htLX4GdnLTeqjjO/98Aa1\n"
+"bZL0SmFQhO3sSdPkvmjmLuMxC1QLGpLWgti2omU8ZgT5Vdps+9u1FGZNlIM7zR6mK7L+d0CG\n"
+"q+ffCsn99t2HVhjYsCxVYJb6CH5SkPVLpi6HfMsg2wY+oF0Dd32iPBMbKaITVaA9FCKvb7jQ\n"
+"mhty3QUBjYZgv6Rn7rWlDdF/5horYmbDB7rnoEgcOMPpRfunf/ztAmgayncSd6YAVSgU7NbH\n"
+"EqIbZULpkejLPoeJVF3Zr52XnGnnCv8PWniLYypMfUeUP95L6VPQMPHF9p5J3zugkaOj/s1Y\n"
+"zOrfr28oO6Bpm4/srK4rVJ2bBLFHIK+WEj5jlB0E5y67hscMmoi/dkfv97ALl2bSRM9gUgfh\n"
+"1SxKOidhd8rXj+eHDjD/DLsE4mHDosiXYY60MGo8bcIHX0pzLz/5FooBZu+6kcpSV3uu1OYP\n"
+"3Qt6f4ueJiDPO++BcYNZ\n"
+"-----END CERTIFICATE-----",.len=2118},
+
+/* E-Tugra Global Root CA ECC v3 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIICpTCCAiqgAwIBAgIUJkYZdzHhT28oNt45UYbm1JeIIsEwCgYIKoZIzj0EAwMwgYAxCzAJ\n"
+"BgNVBAYTAlRSMQ8wDQYDVQQHEwZBbmthcmExGTAXBgNVBAoTEEUtVHVncmEgRUJHIEEuUy4x\n"
+"HTAbBgNVBAsTFEUtVHVncmEgVHJ1c3QgQ2VudGVyMSYwJAYDVQQDEx1FLVR1Z3JhIEdsb2Jh\n"
+"bCBSb290IENBIEVDQyB2MzAeFw0yMDAzMTgwOTQ2NThaFw00NTAzMTIwOTQ2NThaMIGAMQsw\n"
+"CQYDVQQGEwJUUjEPMA0GA1UEBxMGQW5rYXJhMRkwFwYDVQQKExBFLVR1Z3JhIEVCRyBBLlMu\n"
+"MR0wGwYDVQQLExRFLVR1Z3JhIFRydXN0IENlbnRlcjEmMCQGA1UEAxMdRS1UdWdyYSBHbG9i\n"
+"YWwgUm9vdCBDQSBFQ0MgdjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASOmCm/xxAeJ9urA8wo\n"
+"LNheSBkQKczLWYHMjLiSF4mDKpL2w6QdTGLVn9agRtwcvHbB40fQWxPa56WzZkjnIZpKT4YK\n"
+"fWzqTTKACrJ6CZtpS5iB4i7sAnCWH/31Rs7K3IKjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYD\n"
+"VR0jBBgwFoAU/4Ixcj75xGZsrTie0bBRiKWQzPUwHQYDVR0OBBYEFP+CMXI++cRmbK04ntGw\n"
+"UYilkMz1MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNpADBmAjEA5gVYaWHlLcoNy/EZ\n"
+"CL3W/VGSGn5jVASQkZo1kTmZ+gepZpO6yGjUij/67W4WAie3AjEA3VoXK3YdZUKWpqxdinlW\n"
+"2Iob35reX8dQj7FbcQwm32pAAOwzkSFxvmjkI6TZraE3\n"
+"-----END CERTIFICATE-----",.len=974},
+
+/* Security Communication RootCA3 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"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\n"
+"16/9M51NZg==\n"
+"-----END CERTIFICATE-----",.len=1964},
+
+/* Security Communication ECC RootCA1 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIICODCCAb6gAwIBAgIJANZdm7N4gS7rMAoGCCqGSM49BAMDMGExCzAJBgNVBAYTAkpQMSUw\n"
+"IwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMSswKQYDVQQDEyJTZWN1cml0\n"
+"eSBDb21tdW5pY2F0aW9uIEVDQyBSb290Q0ExMB4XDTE2MDYxNjA1MTUyOFoXDTM4MDExODA1\n"
+"MTUyOFowYTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08u\n"
+"LExURC4xKzApBgNVBAMTIlNlY3VyaXR5IENvbW11bmljYXRpb24gRUNDIFJvb3RDQTEwdjAQ\n"
+"BgcqhkjOPQIBBgUrgQQAIgNiAASkpW9gAwPDvTH00xecK4R1rOX9PVdu12O/5gSJko6BnOPp\n"
+"R27KkBLIE+CnnfdldB9sELLo5OnvbYUymUSxXv3MdhDYW72ixvnWQuRXdtyQwjWpS4g8Ekdt\n"
+"XP9JTxpKULGjQjBAMB0GA1UdDgQWBBSGHOf+LaVKiwj+KBH6vqNm+GBZLzAOBgNVHQ8BAf8E\n"
+"BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjAVXUI9/Lbu9zuxNuie\n"
+"9sRGKEkz0FhDKmMpzE2xtHqiuQ04pV1IKv3LsnNdo4gIxwwCMQDAqy0Obe0YottT6SXbVQjg\n"
+"UMzfRGEWgqtJsLKB7HOHeLRMsmIbEvoWTSVLY70eN9k=\n"
+"-----END CERTIFICATE-----",.len=828},
+
+/* BJCA Global Root CA1 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"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\n"
+"-----END CERTIFICATE-----",.len=1947},
+
+/* BJCA Global Root CA2 */
+{.str="-----BEGIN CERTIFICATE-----\n"
+"MIICJTCCAaugAwIBAgIQLBcIfWQqwP6FGFkGz7RK6zAKBggqhkjOPQQDAzBUMQswCQYDVQQG\n"
+"EwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRIT1JJVFkxHTAbBgNVBAMM\n"
+"FEJKQ0EgR2xvYmFsIFJvb3QgQ0EyMB4XDTE5MTIxOTAzMTgyMVoXDTQ0MTIxMjAzMTgyMVow\n"
+"VDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJFSUpJTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ\n"
+"MR0wGwYDVQQDDBRCSkNBIEdsb2JhbCBSb290IENBMjB2MBAGByqGSM49AgEGBSuBBAAiA2IA\n"
+"BJ3LgJGNU2e1uVCxA/jlSR9BIgmwUVJY1is0j8USRhTFiy8shP8sbqjV8QnjAyEUxEM9fMEs\n"
+"xEtqSs3ph+B99iK++kpRuDCK/eHeGBIK9ke35xe/J4rUQUyWPGCWwf0VHKNCMEAwHQYDVR0O\n"
+"BBYEFNJKsVF/BvDRgh9Obl+rg/xI1LCRMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD\n"
+"AgEGMAoGCCqGSM49BAMDA2gAMGUCMBq8W9f+qdJUDkpd0m2xQNz0Q9XSSpkZElaA94M04TVO\n"
+"SG0ED1cxMDAtsaqdAzjbBgIxAMvMh1PLet8gUXOQwKhbYdDFUDn9hf7B43j4ptZLvZuHjw/l\n"
+"1lOWqzzIQNph91Oj9w==\n"
+"-----END CERTIFICATE-----",.len=804},
+};
diff --git a/packages/bun-usockets/src/crypto/sni_tree.cpp b/packages/bun-usockets/src/crypto/sni_tree.cpp
new file mode 100644
index 000000000..fbe0d38d7
--- /dev/null
+++ b/packages/bun-usockets/src/crypto/sni_tree.cpp
@@ -0,0 +1,218 @@
+/*
+ * Authored by Alex Hultman, 2018-2020.
+ * Intellectual property of third-party.
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This Server Name Indication hostname tree is written in C++ but could be ported to C.
+ * Overall it looks like crap, but has no memory allocations in fast path and is O(log n). */
+
+#ifndef SNI_TREE_H
+#define SNI_TREE_H
+
+#ifndef LIBUS_NO_SSL
+
+#include <map>
+#include <memory>
+#include <string_view>
+#include <cstring>
+#include <cstdlib>
+#include <algorithm>
+
+/* We only handle a maximum of 10 labels per hostname */
+#define MAX_LABELS 10
+
+/* This cannot be shared */
+thread_local void (*sni_free_cb)(void *);
+
+struct sni_node {
+ /* Empty nodes must always hold null */
+ void *user = nullptr;
+ std::map<std::string_view, std::unique_ptr<sni_node>> children;
+
+ ~sni_node() {
+ for (auto &p : children) {
+ /* The data of our string_views are managed by malloc */
+ free((void *) p.first.data());
+
+ /* Call destructor passed to sni_free only if we hold data.
+ * This is important since sni_remove does not have sni_free_cb set */
+ if (p.second.get()->user) {
+ sni_free_cb(p.second.get()->user);
+ }
+ }
+ }
+};
+
+// this can only delete ONE single node, but may cull "empty nodes with null as data"
+void *removeUser(struct sni_node *root, unsigned int label, std::string_view *labels, unsigned int numLabels) {
+
+ /* If we are in the bottom (past bottom by one), there is nothing to remove */
+ if (label == numLabels) {
+ void *user = root->user;
+ /* Mark us for culling on the way up */
+ root->user = nullptr;
+ return user;
+ }
+
+ /* Is this label a child of root? */
+ auto it = root->children.find(labels[label]);
+ if (it == root->children.end()) {
+ /* We cannot continue */
+ return nullptr;
+ }
+
+ void *removedUser = removeUser(it->second.get(), label + 1, labels, numLabels);
+
+ /* On the way back up, we may cull empty nodes with no children.
+ * This ends up being where we remove all nodes */
+ if (it->second.get()->children.empty() && it->second.get()->user == nullptr) {
+
+ /* The data of our string_views are managed by malloc */
+ free((void *) it->first.data());
+
+ /* This can only happen with user set to null, otherwise we use sni_free_cb which is unset by sni_remove */
+ root->children.erase(it);
+ }
+
+ return removedUser;
+}
+
+void *getUser(struct sni_node *root, unsigned int label, std::string_view *labels, unsigned int numLabels) {
+
+ /* Do we have labels to match? Otherwise, return where we stand */
+ if (label == numLabels) {
+ return root->user;
+ }
+
+ /* Try and match by our label */
+ auto it = root->children.find(labels[label]);
+ if (it != root->children.end()) {
+ void *user = getUser(it->second.get(), label + 1, labels, numLabels);
+ if (user) {
+ return user;
+ }
+ }
+
+ /* Try and match by wildcard */
+ it = root->children.find("*");
+ if (it == root->children.end()) {
+ /* Matching has failed for both label and wildcard */
+ return nullptr;
+ }
+
+ /* We matched by wildcard */
+ return getUser(it->second.get(), label + 1, labels, numLabels);
+}
+
+extern "C" {
+
+ void *sni_new() {
+ return new sni_node;
+ }
+
+ void sni_free(void *sni, void (*cb)(void *)) {
+ /* We want to run this callback for every remaining name */
+ sni_free_cb = cb;
+
+ delete (sni_node *) sni;
+ }
+
+ /* Returns non-null if this name already exists */
+ int sni_add(void *sni, const char *hostname, void *user) {
+ struct sni_node *root = (struct sni_node *) sni;
+
+ /* Traverse all labels in hostname */
+ for (std::string_view view(hostname, strlen(hostname)), label;
+ view.length(); view.remove_prefix(std::min(view.length(), label.length() + 1))) {
+ /* Label is the token separated by dot */
+ label = view.substr(0, view.find('.', 0));
+
+ auto it = root->children.find(label);
+ if (it == root->children.end()) {
+ /* Duplicate this label for our kept string_view of it */
+ void *labelString = malloc(label.length());
+ memcpy(labelString, label.data(), label.length());
+
+ it = root->children.emplace(std::string_view((char *) labelString, label.length()),
+ std::make_unique<sni_node>()).first;
+ }
+
+ root = it->second.get();
+ }
+
+ /* We must never add multiple contexts for the same name, as that would overwrite and leak */
+ if (root->user) {
+ return 1;
+ }
+
+ root->user = user;
+
+ return 0;
+ }
+
+ /* Removes the exact match. Wildcards are treated as the verbatim asterisk char, not as an actual wildcard */
+ void *sni_remove(void *sni, const char *hostname) {
+ struct sni_node *root = (struct sni_node *) sni;
+
+ /* I guess 10 labels is an okay limit */
+ std::string_view labels[10];
+ unsigned int numLabels = 0;
+
+ /* We traverse all labels first of all */
+ for (std::string_view view(hostname, strlen(hostname)), label;
+ view.length(); view.remove_prefix(std::min(view.length(), label.length() + 1))) {
+ /* Label is the token separated by dot */
+ label = view.substr(0, view.find('.', 0));
+
+ /* Anything longer than 10 labels is forbidden */
+ if (numLabels == 10) {
+ return nullptr;
+ }
+
+ labels[numLabels++] = label;
+ }
+
+ return removeUser(root, 0, labels, numLabels);
+ }
+
+ void *sni_find(void *sni, const char *hostname) {
+ struct sni_node *root = (struct sni_node *) sni;
+
+ /* I guess 10 labels is an okay limit */
+ std::string_view labels[10];
+ unsigned int numLabels = 0;
+
+ /* We traverse all labels first of all */
+ for (std::string_view view(hostname, strlen(hostname)), label;
+ view.length(); view.remove_prefix(std::min(view.length(), label.length() + 1))) {
+ /* Label is the token separated by dot */
+ label = view.substr(0, view.find('.', 0));
+
+ /* Anything longer than 10 labels is forbidden */
+ if (numLabels == 10) {
+ return nullptr;
+ }
+
+ labels[numLabels++] = label;
+ }
+
+ return getUser(root, 0, labels, numLabels);
+ }
+
+}
+
+#endif
+
+#endif \ No newline at end of file
diff --git a/packages/bun-usockets/src/eventing/epoll_kqueue.c b/packages/bun-usockets/src/eventing/epoll_kqueue.c
new file mode 100644
index 000000000..0e2c1f92b
--- /dev/null
+++ b/packages/bun-usockets/src/eventing/epoll_kqueue.c
@@ -0,0 +1,568 @@
+/*
+ * Authored by Alex Hultman, 2018-2019.
+ * Intellectual property of third-party.
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libusockets.h"
+#include "internal/internal.h"
+#include <stdlib.h>
+
+#if defined(LIBUS_USE_EPOLL) || defined(LIBUS_USE_KQUEUE)
+
+void Bun__internal_dispatch_ready_poll(void* loop, void* poll);
+// void Bun__internal_dispatch_ready_poll(void* loop, void* poll) {}
+
+void us_loop_run_bun_tick(struct us_loop_t *loop);
+
+/* Cannot include this one on Windows */
+#include <unistd.h>
+
+/* Pointer tags are used to indicate a Bun pointer versus a uSockets pointer */
+#define UNSET_BITS_49_UNTIL_64 0x0000FFFFFFFFFFFF
+#define CLEAR_POINTER_TAG(p) ((void *) ((uintptr_t) (p) & UNSET_BITS_49_UNTIL_64))
+#define LIKELY(cond) __builtin_expect((uint64_t)(void*)cond, 1)
+#define UNLIKELY(cond) __builtin_expect((uint64_t)(void*)cond, 0)
+
+#ifdef LIBUS_USE_EPOLL
+#define GET_READY_POLL(loop, index) (struct us_poll_t *) loop->ready_polls[index].data.ptr
+#define SET_READY_POLL(loop, index, poll) loop->ready_polls[index].data.ptr = (void*)poll
+#else
+#define GET_READY_POLL(loop, index) (struct us_poll_t *) loop->ready_polls[index].udata
+#define SET_READY_POLL(loop, index, poll) loop->ready_polls[index].udata = (uint64_t)poll
+#endif
+
+/* Loop */
+void us_loop_free(struct us_loop_t *loop) {
+ us_internal_loop_data_free(loop);
+ close(loop->fd);
+ us_free(loop);
+}
+
+/* Poll */
+struct us_poll_t *us_create_poll(struct us_loop_t *loop, int fallthrough, unsigned int ext_size) {
+ if (!fallthrough) {
+ loop->num_polls++;
+ }
+ return CLEAR_POINTER_TAG(us_malloc(sizeof(struct us_poll_t) + ext_size));
+}
+
+/* Todo: this one should be us_internal_poll_free */
+void us_poll_free(struct us_poll_t *p, struct us_loop_t *loop) {
+ loop->num_polls--;
+ us_free(p);
+}
+
+void *us_poll_ext(struct us_poll_t *p) {
+ return p + 1;
+}
+
+/* Todo: why have us_poll_create AND us_poll_init!? libuv legacy! */
+void us_poll_init(struct us_poll_t *p, LIBUS_SOCKET_DESCRIPTOR fd, int poll_type) {
+ p->state.fd = fd;
+ p->state.poll_type = poll_type;
+}
+
+int us_poll_events(struct us_poll_t *p) {
+ return ((p->state.poll_type & POLL_TYPE_POLLING_IN) ? LIBUS_SOCKET_READABLE : 0) | ((p->state.poll_type & POLL_TYPE_POLLING_OUT) ? LIBUS_SOCKET_WRITABLE : 0);
+}
+
+LIBUS_SOCKET_DESCRIPTOR us_poll_fd(struct us_poll_t *p) {
+ return p->state.fd;
+}
+
+/* Returns any of listen socket, socket, shut down socket or callback */
+int us_internal_poll_type(struct us_poll_t *p) {
+ return p->state.poll_type & 3;
+}
+
+/* Bug: doesn't really SET, rather read and change, so needs to be inited first! */
+void us_internal_poll_set_type(struct us_poll_t *p, int poll_type) {
+ p->state.poll_type = poll_type | (p->state.poll_type & 12);
+}
+
+/* Timer */
+void *us_timer_ext(struct us_timer_t *timer) {
+ return ((struct us_internal_callback_t *) timer) + 1;
+}
+
+struct us_loop_t *us_timer_loop(struct us_timer_t *t) {
+ struct us_internal_callback_t *internal_cb = (struct us_internal_callback_t *) t;
+
+ return internal_cb->loop;
+}
+
+/* Loop */
+struct us_loop_t *us_create_loop(void *hint, void (*wakeup_cb)(struct us_loop_t *loop), void (*pre_cb)(struct us_loop_t *loop), void (*post_cb)(struct us_loop_t *loop), unsigned int ext_size) {
+ struct us_loop_t *loop = (struct us_loop_t *) us_malloc(sizeof(struct us_loop_t) + ext_size);
+ loop->num_polls = 0;
+ /* These could be accessed if we close a poll before starting the loop */
+ loop->num_ready_polls = 0;
+ loop->current_ready_poll = 0;
+
+ loop->bun_polls = 0;
+
+#ifdef LIBUS_USE_EPOLL
+ loop->fd = epoll_create1(EPOLL_CLOEXEC);
+#else
+ loop->fd = kqueue();
+#endif
+
+ us_internal_loop_data_init(loop, wakeup_cb, pre_cb, post_cb);
+ return loop;
+}
+
+void us_loop_run(struct us_loop_t *loop) {
+ us_loop_integrate(loop);
+
+ /* While we have non-fallthrough polls we shouldn't fall through */
+ while (loop->num_polls) {
+ /* Emit pre callback */
+ us_internal_loop_pre(loop);
+
+ /* Fetch ready polls */
+#ifdef LIBUS_USE_EPOLL
+ loop->num_ready_polls = epoll_wait(loop->fd, loop->ready_polls, 1024, -1);
+#else
+ loop->num_ready_polls = kevent64(loop->fd, NULL, 0, loop->ready_polls, 1024, 0, NULL);
+#endif
+
+ /* Iterate ready polls, dispatching them by type */
+ for (loop->current_ready_poll = 0; loop->current_ready_poll < loop->num_ready_polls; loop->current_ready_poll++) {
+ struct us_poll_t *poll = GET_READY_POLL(loop, loop->current_ready_poll);
+ /* Any ready poll marked with nullptr will be ignored */
+ if (LIKELY(poll)) {
+ if (CLEAR_POINTER_TAG(poll) != poll) {
+ Bun__internal_dispatch_ready_poll(loop, poll);
+ continue;
+ }
+#ifdef LIBUS_USE_EPOLL
+ int events = loop->ready_polls[loop->current_ready_poll].events;
+ int error = loop->ready_polls[loop->current_ready_poll].events & (EPOLLERR | EPOLLHUP);
+#else
+ /* EVFILT_READ, EVFILT_TIME, EVFILT_USER are all mapped to LIBUS_SOCKET_READABLE */
+ int events = LIBUS_SOCKET_READABLE;
+ if (loop->ready_polls[loop->current_ready_poll].filter == EVFILT_WRITE) {
+ events = LIBUS_SOCKET_WRITABLE;
+ }
+ int error = loop->ready_polls[loop->current_ready_poll].flags & (EV_ERROR | EV_EOF);
+#endif
+ /* Always filter all polls by what they actually poll for (callback polls always poll for readable) */
+ events &= us_poll_events(poll);
+ if (events || error) {
+ us_internal_dispatch_ready_poll(poll, error, events);
+ }
+ }
+ }
+
+ /* Emit post callback */
+ us_internal_loop_post(loop);
+ }
+}
+
+
+void us_loop_run_bun_tick(struct us_loop_t *loop) {
+ us_loop_integrate(loop);
+
+ if (loop->num_polls == 0)
+ return;
+
+ /* Emit pre callback */
+ us_internal_loop_pre(loop);
+
+ /* Fetch ready polls */
+#ifdef LIBUS_USE_EPOLL
+ loop->num_ready_polls = epoll_wait(loop->fd, loop->ready_polls, 1024, -1);
+#else
+ struct timespec ts = {0, 0};
+ loop->num_ready_polls = kevent64(loop->fd, NULL, 0, loop->ready_polls, 1024, 0, NULL);
+#endif
+
+ /* Iterate ready polls, dispatching them by type */
+ for (loop->current_ready_poll = 0; loop->current_ready_poll < loop->num_ready_polls; loop->current_ready_poll++) {
+ struct us_poll_t *poll = GET_READY_POLL(loop, loop->current_ready_poll);
+ /* Any ready poll marked with nullptr will be ignored */
+ if (LIKELY(poll)) {
+ if (CLEAR_POINTER_TAG(poll) != poll) {
+ Bun__internal_dispatch_ready_poll(loop, poll);
+ continue;
+ }
+#ifdef LIBUS_USE_EPOLL
+ int events = loop->ready_polls[loop->current_ready_poll].events;
+ int error = loop->ready_polls[loop->current_ready_poll].events & (EPOLLERR | EPOLLHUP);
+#else
+ /* EVFILT_READ, EVFILT_TIME, EVFILT_USER are all mapped to LIBUS_SOCKET_READABLE */
+ int events = LIBUS_SOCKET_READABLE;
+ if (loop->ready_polls[loop->current_ready_poll].filter == EVFILT_WRITE) {
+ events = LIBUS_SOCKET_WRITABLE;
+ }
+ int error = loop->ready_polls[loop->current_ready_poll].flags & (EV_ERROR | EV_EOF);
+#endif
+ /* Always filter all polls by what they actually poll for (callback polls always poll for readable) */
+ events &= us_poll_events(poll);
+ if (events || error) {
+ us_internal_dispatch_ready_poll(poll, error, events);
+ }
+ }
+ }
+
+ /* Emit post callback */
+ us_internal_loop_post(loop);
+}
+
+void us_internal_loop_update_pending_ready_polls(struct us_loop_t *loop, struct us_poll_t *old_poll, struct us_poll_t *new_poll, int old_events, int new_events) {
+#ifdef LIBUS_USE_EPOLL
+ /* Epoll only has one ready poll per poll */
+ int num_entries_possibly_remaining = 1;
+#else
+ /* Ready polls may contain same poll twice under kqueue, as one poll may hold two filters */
+ int num_entries_possibly_remaining = 2;//((old_events & LIBUS_SOCKET_READABLE) ? 1 : 0) + ((old_events & LIBUS_SOCKET_WRITABLE) ? 1 : 0);
+#endif
+
+ /* Todo: for kqueue if we track things in us_change_poll it is possible to have a fast path with no seeking in cases of:
+ * current poll being us AND we only poll for one thing */
+
+ for (int i = loop->current_ready_poll; i < loop->num_ready_polls && num_entries_possibly_remaining; i++) {
+ if (GET_READY_POLL(loop, i) == old_poll) {
+
+ // if new events does not contain the ready events of this poll then remove (no we filter that out later on)
+ SET_READY_POLL(loop, i, new_poll);
+
+ num_entries_possibly_remaining--;
+ }
+ }
+}
+
+/* Poll */
+
+#ifdef LIBUS_USE_KQUEUE
+/* Helper function for setting or updating EVFILT_READ and EVFILT_WRITE */
+int kqueue_change(int kqfd, int fd, int old_events, int new_events, void *user_data) {
+ struct kevent64_s change_list[2];
+ int change_length = 0;
+
+ /* Do they differ in readable? */
+ if ((new_events & LIBUS_SOCKET_READABLE) != (old_events & LIBUS_SOCKET_READABLE)) {
+ EV_SET64(&change_list[change_length++], fd, EVFILT_READ, (new_events & LIBUS_SOCKET_READABLE) ? EV_ADD : EV_DELETE, 0, 0, (uint64_t)(void*)user_data, 0, 0);
+ }
+
+ /* Do they differ in writable? */
+ if ((new_events & LIBUS_SOCKET_WRITABLE) != (old_events & LIBUS_SOCKET_WRITABLE)) {
+ EV_SET64(&change_list[change_length++], fd, EVFILT_WRITE, (new_events & LIBUS_SOCKET_WRITABLE) ? EV_ADD : EV_DELETE, 0, 0, (uint64_t)(void*)user_data, 0, 0);
+ }
+
+ int ret = kevent64(kqfd, change_list, change_length, NULL, 0, 0, NULL);
+
+ // ret should be 0 in most cases (not guaranteed when removing async)
+
+ return ret;
+}
+#endif
+
+struct us_poll_t *us_poll_resize(struct us_poll_t *p, struct us_loop_t *loop, unsigned int ext_size) {
+ int events = us_poll_events(p);
+
+ struct us_poll_t *new_p = us_realloc(p, sizeof(struct us_poll_t) + ext_size);
+ if (p != new_p && events) {
+#ifdef LIBUS_USE_EPOLL
+ /* Hack: forcefully update poll by stripping away already set events */
+ new_p->state.poll_type = us_internal_poll_type(new_p);
+ us_poll_change(new_p, loop, events);
+#else
+ /* Forcefully update poll by resetting them with new_p as user data */
+ kqueue_change(loop->fd, new_p->state.fd, 0, events, new_p);
+#endif
+
+ /* This is needed for epoll also (us_change_poll doesn't update the old poll) */
+ us_internal_loop_update_pending_ready_polls(loop, p, new_p, events, events);
+ }
+
+ return new_p;
+}
+
+void us_poll_start(struct us_poll_t *p, struct us_loop_t *loop, int events) {
+ p->state.poll_type = us_internal_poll_type(p) | ((events & LIBUS_SOCKET_READABLE) ? POLL_TYPE_POLLING_IN : 0) | ((events & LIBUS_SOCKET_WRITABLE) ? POLL_TYPE_POLLING_OUT : 0);
+
+#ifdef LIBUS_USE_EPOLL
+ struct epoll_event event;
+ event.events = events;
+ event.data.ptr = p;
+ epoll_ctl(loop->fd, EPOLL_CTL_ADD, p->state.fd, &event);
+#else
+ kqueue_change(loop->fd, p->state.fd, 0, events, p);
+#endif
+}
+
+void us_poll_change(struct us_poll_t *p, struct us_loop_t *loop, int events) {
+ int old_events = us_poll_events(p);
+ if (old_events != events) {
+
+ p->state.poll_type = us_internal_poll_type(p) | ((events & LIBUS_SOCKET_READABLE) ? POLL_TYPE_POLLING_IN : 0) | ((events & LIBUS_SOCKET_WRITABLE) ? POLL_TYPE_POLLING_OUT : 0);
+
+#ifdef LIBUS_USE_EPOLL
+ struct epoll_event event;
+ event.events = events;
+ event.data.ptr = p;
+ epoll_ctl(loop->fd, EPOLL_CTL_MOD, p->state.fd, &event);
+#else
+ kqueue_change(loop->fd, p->state.fd, old_events, events, p);
+#endif
+ /* Set all removed events to null-polls in pending ready poll list */
+ //us_internal_loop_update_pending_ready_polls(loop, p, p, old_events, events);
+ }
+}
+
+void us_poll_stop(struct us_poll_t *p, struct us_loop_t *loop) {
+ int old_events = us_poll_events(p);
+ int new_events = 0;
+#ifdef LIBUS_USE_EPOLL
+ struct epoll_event event;
+ epoll_ctl(loop->fd, EPOLL_CTL_DEL, p->state.fd, &event);
+#else
+ if (old_events) {
+ kqueue_change(loop->fd, p->state.fd, old_events, new_events, NULL);
+ }
+#endif
+
+ /* Disable any instance of us in the pending ready poll list */
+ us_internal_loop_update_pending_ready_polls(loop, p, 0, old_events, new_events);
+}
+
+unsigned int us_internal_accept_poll_event(struct us_poll_t *p) {
+#ifdef LIBUS_USE_EPOLL
+ int fd = us_poll_fd(p);
+ uint64_t buf;
+ int read_length = read(fd, &buf, 8);
+ (void)read_length;
+ return buf;
+#else
+ /* Kqueue has no underlying FD for timers or user events */
+ return 0;
+#endif
+}
+
+/* Timer */
+#ifdef LIBUS_USE_EPOLL
+struct us_timer_t *us_create_timer(struct us_loop_t *loop, int fallthrough, unsigned int ext_size) {
+ struct us_poll_t *p = us_create_poll(loop, fallthrough, sizeof(struct us_internal_callback_t) + ext_size);
+ int timerfd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC);
+ if (timerfd == -1) {
+ return NULL;
+ }
+ us_poll_init(p, timerfd, POLL_TYPE_CALLBACK);
+
+ struct us_internal_callback_t *cb = (struct us_internal_callback_t *) p;
+ cb->loop = loop;
+ cb->cb_expects_the_loop = 0;
+ cb->leave_poll_ready = 0;
+
+ return (struct us_timer_t *) cb;
+}
+#else
+struct us_timer_t *us_create_timer(struct us_loop_t *loop, int fallthrough, unsigned int ext_size) {
+ struct us_internal_callback_t *cb = us_malloc(sizeof(struct us_internal_callback_t) + ext_size);
+
+ cb->loop = loop;
+ cb->cb_expects_the_loop = 0;
+ cb->leave_poll_ready = 0;
+
+ /* Bug: us_internal_poll_set_type does not SET the type, it only CHANGES it */
+ cb->p.state.poll_type = POLL_TYPE_POLLING_IN;
+ us_internal_poll_set_type((struct us_poll_t *) cb, POLL_TYPE_CALLBACK);
+
+ if (!fallthrough) {
+ loop->num_polls++;
+ }
+
+ return (struct us_timer_t *) cb;
+}
+#endif
+
+#ifdef LIBUS_USE_EPOLL
+void us_timer_close(struct us_timer_t *timer) {
+ struct us_internal_callback_t *cb = (struct us_internal_callback_t *) timer;
+
+ us_poll_stop(&cb->p, cb->loop);
+ close(us_poll_fd(&cb->p));
+
+ /* (regular) sockets are the only polls which are not freed immediately */
+ us_poll_free((struct us_poll_t *) timer, cb->loop);
+}
+
+void us_timer_set(struct us_timer_t *t, void (*cb)(struct us_timer_t *t), int ms, int repeat_ms) {
+ struct us_internal_callback_t *internal_cb = (struct us_internal_callback_t *) t;
+
+ internal_cb->cb = (void (*)(struct us_internal_callback_t *)) cb;
+
+ struct itimerspec timer_spec = {
+ {repeat_ms / 1000, (long) (repeat_ms % 1000) * (long) 1000000},
+ {ms / 1000, (long) (ms % 1000) * (long) 1000000}
+ };
+
+ timerfd_settime(us_poll_fd((struct us_poll_t *) t), 0, &timer_spec, NULL);
+ us_poll_start((struct us_poll_t *) t, internal_cb->loop, LIBUS_SOCKET_READABLE);
+}
+#else
+void us_timer_close(struct us_timer_t *timer) {
+ struct us_internal_callback_t *internal_cb = (struct us_internal_callback_t *) timer;
+
+ struct kevent64_s event;
+ EV_SET64(&event, (uint64_t) (void*) internal_cb, EVFILT_TIMER, EV_DELETE, 0, 0, (uint64_t)internal_cb, 0, 0);
+ kevent64(internal_cb->loop->fd, &event, 1, NULL, 0, 0, NULL);
+
+ /* (regular) sockets are the only polls which are not freed immediately */
+ us_poll_free((struct us_poll_t *) timer, internal_cb->loop);
+}
+
+void us_timer_set(struct us_timer_t *t, void (*cb)(struct us_timer_t *t), int ms, int repeat_ms) {
+ struct us_internal_callback_t *internal_cb = (struct us_internal_callback_t *) t;
+
+ internal_cb->cb = (void (*)(struct us_internal_callback_t *)) cb;
+
+ /* Bug: repeat_ms must be the same as ms, or 0 */
+ struct kevent64_s event;
+ uint64_t ptr = (uint64_t)(void*)internal_cb;
+ EV_SET64(&event, ptr, EVFILT_TIMER, EV_ADD | (repeat_ms ? 0 : EV_ONESHOT), 0, ms, (uint64_t)internal_cb, 0, 0);
+ kevent64(internal_cb->loop->fd, &event, 1, NULL, 0, 0, NULL);
+}
+#endif
+
+/* Async (internal helper for loop's wakeup feature) */
+#ifdef LIBUS_USE_EPOLL
+struct us_internal_async *us_internal_create_async(struct us_loop_t *loop, int fallthrough, unsigned int ext_size) {
+ struct us_poll_t *p = us_create_poll(loop, fallthrough, sizeof(struct us_internal_callback_t) + ext_size);
+ us_poll_init(p, eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC), POLL_TYPE_CALLBACK);
+
+ struct us_internal_callback_t *cb = (struct us_internal_callback_t *) p;
+ cb->loop = loop;
+ cb->cb_expects_the_loop = 1;
+ cb->leave_poll_ready = 0;
+
+ return (struct us_internal_async *) cb;
+}
+
+// identical code as for timer, make it shared for "callback types"
+void us_internal_async_close(struct us_internal_async *a) {
+ struct us_internal_callback_t *cb = (struct us_internal_callback_t *) a;
+
+ us_poll_stop(&cb->p, cb->loop);
+ close(us_poll_fd(&cb->p));
+
+ /* (regular) sockets are the only polls which are not freed immediately */
+ us_poll_free((struct us_poll_t *) a, cb->loop);
+}
+
+void us_internal_async_set(struct us_internal_async *a, void (*cb)(struct us_internal_async *)) {
+ struct us_internal_callback_t *internal_cb = (struct us_internal_callback_t *) a;
+
+ internal_cb->cb = (void (*)(struct us_internal_callback_t *)) cb;
+
+ us_poll_start((struct us_poll_t *) a, internal_cb->loop, LIBUS_SOCKET_READABLE);
+}
+
+void us_internal_async_wakeup(struct us_internal_async *a) {
+ uint64_t one = 1;
+ int written = write(us_poll_fd((struct us_poll_t *) a), &one, 8);
+ (void)written;
+}
+#else
+
+#define MACHPORT_BUF_LEN 1024
+
+struct us_internal_async *us_internal_create_async(struct us_loop_t *loop, int fallthrough, unsigned int ext_size) {
+ struct us_internal_callback_t *cb = us_malloc(sizeof(struct us_internal_callback_t) + ext_size);
+
+ cb->loop = loop;
+ cb->cb_expects_the_loop = 1;
+ cb->leave_poll_ready = 0;
+
+ /* Bug: us_internal_poll_set_type does not SET the type, it only CHANGES it */
+ cb->p.state.poll_type = POLL_TYPE_POLLING_IN;
+ us_internal_poll_set_type((struct us_poll_t *) cb, POLL_TYPE_CALLBACK);
+
+ if (!fallthrough) {
+ loop->num_polls++;
+ }
+
+ cb->machport_buf = us_malloc(MACHPORT_BUF_LEN);
+ kern_return_t kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &cb->port);
+
+ if (UNLIKELY(kr != KERN_SUCCESS)) {
+ return NULL;
+ }
+
+ return (struct us_internal_async *) cb;
+}
+
+// identical code as for timer, make it shared for "callback types"
+void us_internal_async_close(struct us_internal_async *a) {
+ struct us_internal_callback_t *internal_cb = (struct us_internal_callback_t *) a;
+
+ struct kevent64_s event;
+ uint64_t ptr = (uint64_t)(void*)internal_cb;
+ EV_SET64(&event, ptr, EVFILT_MACHPORT, EV_DELETE, 0, 0, (uint64_t)(void*)internal_cb, 0,0);
+ kevent64(internal_cb->loop->fd, &event, 1, NULL, 0, 0, NULL);
+
+ mach_port_deallocate(mach_task_self(), internal_cb->port);
+ us_free(internal_cb->machport_buf);
+
+ /* (regular) sockets are the only polls which are not freed immediately */
+ us_poll_free((struct us_poll_t *) a, internal_cb->loop);
+}
+
+void us_internal_async_set(struct us_internal_async *a, void (*cb)(struct us_internal_async *)) {
+ struct us_internal_callback_t *internal_cb = (struct us_internal_callback_t *) a;
+
+ internal_cb->cb = (void (*)(struct us_internal_callback_t *)) cb;
+
+ // EVFILT_MACHPORT benchmarks faster than EVFILT_USER when using multiple threads
+ // Very old versions of macOS required them to be portsets instead of ports
+ // but that is no longer the case
+ // There are not many examples on the internet of using machports this way
+ // you can find one in Chromium's codebase.
+ struct kevent64_s event;
+ event.ident = internal_cb->port;
+ event.filter = EVFILT_MACHPORT;
+ event.flags = EV_ADD | EV_ENABLE;
+ event.fflags = MACH_RCV_MSG | MACH_RCV_OVERWRITE;
+ event.ext[0] = (uint64_t)(void*)internal_cb->machport_buf;
+ event.ext[1] = MACHPORT_BUF_LEN;
+ event.udata = (uint64_t)(void*)internal_cb;
+
+ int ret = kevent64(internal_cb->loop->fd, &event, 1, NULL, 0, 0, NULL);
+
+ if (UNLIKELY(ret == -1)) {
+ abort();
+ }
+}
+
+void us_internal_async_wakeup(struct us_internal_async *a) {
+ struct us_internal_callback_t *internal_cb = (struct us_internal_callback_t *) a;
+ mach_msg_empty_send_t message;
+ memset(&message, 0, sizeof(message));
+ message.header.msgh_size = sizeof(message);
+ message.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MAKE_SEND_ONCE);
+ message.header.msgh_remote_port = internal_cb->port;
+ kern_return_t kr = mach_msg_send(&message.header);
+ if (kr != KERN_SUCCESS) {
+ // If us_internal_async_wakeup is being called by other threads faster
+ // than the pump can dispatch work, the kernel message queue for the wakeup
+ // port can fill The kernel does return a SEND_ONCE right in the case of
+ // failure, which must be destroyed to avoid leaking.
+ mach_msg_destroy(&message.header);
+ }
+}
+#endif
+
+#endif
diff --git a/packages/bun-usockets/src/internal/eventing/epoll_kqueue.h b/packages/bun-usockets/src/internal/eventing/epoll_kqueue.h
new file mode 100644
index 000000000..ae46e91e7
--- /dev/null
+++ b/packages/bun-usockets/src/internal/eventing/epoll_kqueue.h
@@ -0,0 +1,72 @@
+/*
+ * Authored by Alex Hultman, 2018-2019.
+ * Intellectual property of third-party.
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef EPOLL_KQUEUE_H
+#define EPOLL_KQUEUE_H
+
+#include "internal/loop_data.h"
+
+#ifdef LIBUS_USE_EPOLL
+#include <sys/epoll.h>
+#include <sys/timerfd.h>
+#include <sys/eventfd.h>
+#define LIBUS_SOCKET_READABLE EPOLLIN
+#define LIBUS_SOCKET_WRITABLE EPOLLOUT
+#else
+#include <sys/event.h>
+/* Kqueue's EVFILT_ is NOT a bitfield, you cannot OR together them.
+ * We therefore have our own bitfield we then translate in every call */
+#define LIBUS_SOCKET_READABLE 1
+#define LIBUS_SOCKET_WRITABLE 2
+
+#include <mach/mach.h>
+#endif
+
+struct us_loop_t {
+ alignas(LIBUS_EXT_ALIGNMENT) struct us_internal_loop_data_t data;
+
+ /* Number of non-fallthrough polls in the loop */
+ int num_polls;
+
+ /* Number of ready polls this iteration */
+ int num_ready_polls;
+
+ /* Current index in list of ready polls */
+ int current_ready_poll;
+
+ /* Loop's own file descriptor */
+ int fd;
+
+ /* Number of polls owned by bun */
+ unsigned int bun_polls;
+
+ /* The list of ready polls */
+#ifdef LIBUS_USE_EPOLL
+ alignas(LIBUS_EXT_ALIGNMENT) struct epoll_event ready_polls[1024];
+#else
+ alignas(LIBUS_EXT_ALIGNMENT) struct kevent64_s ready_polls[1024];
+#endif
+};
+
+struct us_poll_t {
+ alignas(LIBUS_EXT_ALIGNMENT) struct {
+ signed int fd : 28; // we could have this unsigned if we wanted to, -1 should never be used
+ unsigned int poll_type : 4;
+ } state;
+};
+
+#endif // EPOLL_KQUEUE_H
diff --git a/packages/bun-usockets/src/internal/internal.h b/packages/bun-usockets/src/internal/internal.h
new file mode 100644
index 000000000..2d4719571
--- /dev/null
+++ b/packages/bun-usockets/src/internal/internal.h
@@ -0,0 +1,245 @@
+/*
+ * Authored by Alex Hultman, 2018-2019.
+ * Intellectual property of third-party.
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INTERNAL_H
+#define INTERNAL_H
+
+
+#if defined(_MSC_VER)
+#define alignas(x) __declspec(align(x))
+#else
+#include <stdalign.h>
+#endif
+
+#if defined(LIBUS_USE_KQUEUE)
+#include <mach/mach.h>
+#endif
+
+#if defined(LIBUS_USE_EPOLL) || defined(LIBUS_USE_KQUEUE)
+#define LIBUS_MAX_READY_POLLS 1024
+
+void us_internal_loop_update_pending_ready_polls(struct us_loop_t *loop, struct us_poll_t *old_poll, struct us_poll_t *new_poll, int old_events, int new_events);
+#endif
+
+/* We only have one networking implementation so far */
+#include "internal/networking/bsd.h"
+
+/* We have many different eventing implementations */
+#if defined(LIBUS_USE_EPOLL) || defined(LIBUS_USE_KQUEUE)
+#include "internal/eventing/epoll_kqueue.h"
+#endif
+
+/* Poll type and what it polls for */
+enum {
+ /* Two first bits */
+ POLL_TYPE_SOCKET = 0,
+ POLL_TYPE_SOCKET_SHUT_DOWN = 1,
+ POLL_TYPE_SEMI_SOCKET = 2,
+ POLL_TYPE_CALLBACK = 3,
+
+ /* Two last bits */
+ POLL_TYPE_POLLING_OUT = 4,
+ POLL_TYPE_POLLING_IN = 8
+};
+
+/* Loop related */
+void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int events);
+void us_internal_timer_sweep(struct us_loop_t *loop);
+void us_internal_free_closed_sockets(struct us_loop_t *loop);
+void us_internal_loop_link(struct us_loop_t *loop, struct us_socket_context_t *context);
+void us_internal_loop_unlink(struct us_loop_t *loop, struct us_socket_context_t *context);
+void us_internal_loop_data_init(struct us_loop_t *loop, void (*wakeup_cb)(struct us_loop_t *loop),
+ void (*pre_cb)(struct us_loop_t *loop), void (*post_cb)(struct us_loop_t *loop));
+void us_internal_loop_data_free(struct us_loop_t *loop);
+void us_internal_loop_pre(struct us_loop_t *loop);
+void us_internal_loop_post(struct us_loop_t *loop);
+
+/* Asyncs (old) */
+struct us_internal_async *us_internal_create_async(struct us_loop_t *loop, int fallthrough, unsigned int ext_size);
+void us_internal_async_close(struct us_internal_async *a);
+void us_internal_async_set(struct us_internal_async *a, void (*cb)(struct us_internal_async *));
+void us_internal_async_wakeup(struct us_internal_async *a);
+
+/* Eventing related */
+unsigned int us_internal_accept_poll_event(struct us_poll_t *p);
+int us_internal_poll_type(struct us_poll_t *p);
+void us_internal_poll_set_type(struct us_poll_t *p, int poll_type);
+
+/* SSL loop data */
+void us_internal_init_loop_ssl_data(struct us_loop_t *loop);
+void us_internal_free_loop_ssl_data(struct us_loop_t *loop);
+
+/* Socket context related */
+void us_internal_socket_context_link_socket(struct us_socket_context_t *context, struct us_socket_t *s);
+void us_internal_socket_context_unlink_socket(struct us_socket_context_t *context, struct us_socket_t *s);
+
+/* Sockets are polls */
+struct us_socket_t {
+ alignas(LIBUS_EXT_ALIGNMENT) struct us_poll_t p; // 4 bytes
+ unsigned char timeout; // 1 byte
+ unsigned char long_timeout; // 1 byte
+ unsigned short low_prio_state; /* 0 = not in low-prio queue, 1 = is in low-prio queue, 2 = was in low-prio queue in this iteration */
+ struct us_socket_context_t *context;
+ struct us_socket_t *prev, *next;
+};
+
+struct us_wrapped_socket_context_t {
+ struct us_socket_events_t events;
+ struct us_socket_events_t old_events;
+};
+
+#if defined(LIBUS_USE_KQUEUE)
+/* Internal callback types are polls just like sockets */
+struct us_internal_callback_t {
+ alignas(LIBUS_EXT_ALIGNMENT) struct us_poll_t p;
+ struct us_loop_t *loop;
+ int cb_expects_the_loop;
+ int leave_poll_ready;
+ void (*cb)(struct us_internal_callback_t *cb);
+ mach_port_t port;
+ void* machport_buf;
+};
+
+#else
+
+struct us_internal_callback_t {
+ alignas(LIBUS_EXT_ALIGNMENT) struct us_poll_t p;
+ struct us_loop_t *loop;
+ int cb_expects_the_loop;
+ int leave_poll_ready;
+ void (*cb)(struct us_internal_callback_t *cb);
+};
+
+#endif
+
+/* Listen sockets are sockets */
+struct us_listen_socket_t {
+ alignas(LIBUS_EXT_ALIGNMENT) struct us_socket_t s;
+ unsigned int socket_ext_size;
+};
+
+/* Listen sockets are keps in their own list */
+void us_internal_socket_context_link_listen_socket(struct us_socket_context_t *context, struct us_listen_socket_t *s);
+void us_internal_socket_context_unlink_listen_socket(struct us_socket_context_t *context, struct us_listen_socket_t *s);
+
+struct us_socket_context_t {
+ alignas(LIBUS_EXT_ALIGNMENT) struct us_loop_t *loop;
+ uint32_t global_tick;
+ unsigned char timestamp;
+ unsigned char long_timestamp;
+ struct us_socket_t *head_sockets;
+ struct us_listen_socket_t *head_listen_sockets;
+ struct us_socket_t *iterator;
+ struct us_socket_context_t *prev, *next;
+
+ struct us_socket_t *(*on_open)(struct us_socket_t *, int is_client, char *ip, int ip_length);
+ struct us_socket_t *(*on_data)(struct us_socket_t *, char *data, int length);
+ struct us_socket_t *(*on_writable)(struct us_socket_t *);
+ struct us_socket_t *(*on_close)(struct us_socket_t *, int code, void *reason);
+ //void (*on_timeout)(struct us_socket_context *);
+ struct us_socket_t *(*on_socket_timeout)(struct us_socket_t *);
+ struct us_socket_t *(*on_socket_long_timeout)(struct us_socket_t *);
+ struct us_socket_t *(*on_end)(struct us_socket_t *);
+ struct us_socket_t *(*on_connect_error)(struct us_socket_t *, int code);
+ int (*is_low_prio)(struct us_socket_t *);
+};
+
+/* Internal SSL interface */
+#ifndef LIBUS_NO_SSL
+
+struct us_internal_ssl_socket_context_t;
+struct us_internal_ssl_socket_t;
+
+/* SNI functions */
+void us_internal_ssl_socket_context_add_server_name(struct us_internal_ssl_socket_context_t *context, const char *hostname_pattern, struct us_socket_context_options_t options, void *user);
+void us_bun_internal_ssl_socket_context_add_server_name(struct us_internal_ssl_socket_context_t *context, const char *hostname_pattern, struct us_bun_socket_context_options_t options, void *user);
+void us_internal_ssl_socket_context_remove_server_name(struct us_internal_ssl_socket_context_t *context, const char *hostname_pattern);
+void us_internal_ssl_socket_context_on_server_name(struct us_internal_ssl_socket_context_t *context, void (*cb)(struct us_internal_ssl_socket_context_t *, const char *));
+void *us_internal_ssl_socket_get_sni_userdata(struct us_internal_ssl_socket_t *s);
+void *us_internal_ssl_socket_context_find_server_name_userdata(struct us_internal_ssl_socket_context_t *context, const char *hostname_pattern);
+
+void *us_internal_ssl_socket_get_native_handle(struct us_internal_ssl_socket_t *s);
+void *us_internal_ssl_socket_context_get_native_handle(struct us_internal_ssl_socket_context_t *context);
+struct us_bun_verify_error_t us_internal_verify_error(struct us_internal_ssl_socket_t *s);
+struct us_internal_ssl_socket_context_t *us_internal_create_ssl_socket_context(struct us_loop_t *loop,
+ int context_ext_size, struct us_socket_context_options_t options);
+struct us_internal_ssl_socket_context_t *us_internal_bun_create_ssl_socket_context(struct us_loop_t *loop,
+ int context_ext_size, struct us_bun_socket_context_options_t options);
+
+void us_internal_ssl_socket_context_free(struct us_internal_ssl_socket_context_t *context);
+void us_internal_ssl_socket_context_on_open(struct us_internal_ssl_socket_context_t *context,
+ struct us_internal_ssl_socket_t *(*on_open)(struct us_internal_ssl_socket_t *s, int is_client, char *ip, int ip_length));
+
+void us_internal_ssl_socket_context_on_close(struct us_internal_ssl_socket_context_t *context,
+ struct us_internal_ssl_socket_t *(*on_close)(struct us_internal_ssl_socket_t *s, int code, void *reason));
+
+void us_internal_ssl_socket_context_on_data(struct us_internal_ssl_socket_context_t *context,
+ struct us_internal_ssl_socket_t *(*on_data)(struct us_internal_ssl_socket_t *s, char *data, int length));
+
+void us_internal_ssl_handshake(struct us_internal_ssl_socket_t *s, void (*on_handshake)(struct us_internal_ssl_socket_t *, int success, struct us_bun_verify_error_t verify_error, void* custom_data), void* custom_data);
+void us_internal_on_ssl_handshake(struct us_internal_ssl_socket_context_t * context, void (*on_handshake)(struct us_internal_ssl_socket_t *, int success, struct us_bun_verify_error_t verify_error, void* custom_data), void* custom_data);
+
+void us_internal_ssl_socket_context_on_writable(struct us_internal_ssl_socket_context_t *context,
+ struct us_internal_ssl_socket_t *(*on_writable)(struct us_internal_ssl_socket_t *s));
+
+void us_internal_ssl_socket_context_on_timeout(struct us_internal_ssl_socket_context_t *context,
+ struct us_internal_ssl_socket_t *(*on_timeout)(struct us_internal_ssl_socket_t *s));
+
+void us_internal_ssl_socket_context_on_long_timeout(struct us_internal_ssl_socket_context_t *context,
+ struct us_internal_ssl_socket_t *(*on_timeout)(struct us_internal_ssl_socket_t *s));
+
+void us_internal_ssl_socket_context_on_end(struct us_internal_ssl_socket_context_t *context,
+ struct us_internal_ssl_socket_t *(*on_end)(struct us_internal_ssl_socket_t *s));
+
+void us_internal_ssl_socket_context_on_connect_error(struct us_internal_ssl_socket_context_t *context,
+ struct us_internal_ssl_socket_t *(*on_connect_error)(struct us_internal_ssl_socket_t *s, int code));
+
+struct us_listen_socket_t *us_internal_ssl_socket_context_listen(struct us_internal_ssl_socket_context_t *context,
+ const char *host, int port, int options, int socket_ext_size);
+
+struct us_listen_socket_t *us_internal_ssl_socket_context_listen_unix(struct us_internal_ssl_socket_context_t *context,
+ const char *path, int options, int socket_ext_size);
+
+struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_connect(struct us_internal_ssl_socket_context_t *context,
+ const char *host, int port, const char *source_host, int options, int socket_ext_size);
+
+
+struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_connect_unix(struct us_internal_ssl_socket_context_t *context,
+ const char *server_path, int options, int socket_ext_size);
+
+int us_internal_ssl_socket_write(struct us_internal_ssl_socket_t *s, const char *data, int length, int msg_more);
+int us_internal_ssl_socket_raw_write(struct us_internal_ssl_socket_t *s, const char *data, int length, int msg_more);
+
+void us_internal_ssl_socket_timeout(struct us_internal_ssl_socket_t *s, unsigned int seconds);
+void *us_internal_ssl_socket_context_ext(struct us_internal_ssl_socket_context_t *s);
+struct us_internal_ssl_socket_context_t *us_internal_ssl_socket_get_context(struct us_internal_ssl_socket_t *s);
+void *us_internal_ssl_socket_ext(struct us_internal_ssl_socket_t *s);
+int us_internal_ssl_socket_is_shut_down(struct us_internal_ssl_socket_t *s);
+void us_internal_ssl_socket_shutdown(struct us_internal_ssl_socket_t *s);
+
+struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_adopt_socket(struct us_internal_ssl_socket_context_t *context,
+ struct us_internal_ssl_socket_t *s, int ext_size);
+
+struct us_internal_ssl_socket_t *us_internal_ssl_socket_wrap_with_tls(struct us_socket_t *s, struct us_bun_socket_context_options_t options, struct us_socket_events_t events, int socket_ext_size);
+struct us_internal_ssl_socket_context_t *us_internal_create_child_ssl_socket_context(struct us_internal_ssl_socket_context_t *context, int context_ext_size);
+struct us_loop_t *us_internal_ssl_socket_context_loop(struct us_internal_ssl_socket_context_t *context);
+struct us_internal_ssl_socket_t* us_internal_ssl_socket_open(struct us_internal_ssl_socket_t * s, int is_client, char* ip, int ip_length);
+
+int us_raw_root_certs(struct us_cert_string_t**out);
+#endif
+
+#endif // INTERNAL_H
diff --git a/packages/bun-usockets/src/internal/loop_data.h b/packages/bun-usockets/src/internal/loop_data.h
new file mode 100644
index 000000000..468c5235b
--- /dev/null
+++ b/packages/bun-usockets/src/internal/loop_data.h
@@ -0,0 +1,38 @@
+/*
+ * Authored by Alex Hultman, 2018-2019.
+ * Intellectual property of third-party.
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LOOP_DATA_H
+#define LOOP_DATA_H
+
+struct us_internal_loop_data_t {
+ struct us_timer_t *sweep_timer;
+ struct us_internal_async *wakeup_async;
+ int last_write_failed;
+ struct us_socket_context_t *head;
+ struct us_socket_context_t *iterator;
+ char *recv_buf;
+ void *ssl_data;
+ void (*pre_cb)(struct us_loop_t *);
+ void (*post_cb)(struct us_loop_t *);
+ struct us_socket_t *closed_head;
+ struct us_socket_t *low_prio_head;
+ int low_prio_budget;
+ /* We do not care if this flips or not, it doesn't matter */
+ long long iteration_nr;
+};
+
+#endif // LOOP_DATA_H
diff --git a/packages/bun-usockets/src/internal/networking/bsd.h b/packages/bun-usockets/src/internal/networking/bsd.h
new file mode 100644
index 000000000..d1a7be9e9
--- /dev/null
+++ b/packages/bun-usockets/src/internal/networking/bsd.h
@@ -0,0 +1,108 @@
+/*
+ * Authored by Alex Hultman, 2018-2019.
+ * Intellectual property of third-party.
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BSD_H
+#define BSD_H
+
+// top-most wrapper of bsd-like syscalls
+
+// holds everything you need from the bsd/winsock interfaces, only included by internal libusockets.h
+// here everything about the syscalls are inline-wrapped and included
+
+#include "libusockets.h"
+
+#ifdef _WIN32
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#pragma comment(lib, "ws2_32.lib")
+#define SETSOCKOPT_PTR_TYPE const char *
+#define LIBUS_SOCKET_ERROR INVALID_SOCKET
+#else
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+/* For socklen_t */
+#include <sys/socket.h>
+#define SETSOCKOPT_PTR_TYPE int *
+#define LIBUS_SOCKET_ERROR -1
+#endif
+
+#define LIBUS_UDP_MAX_SIZE (64 * 1024)
+#define LIBUS_UDP_MAX_NUM 1024
+
+struct bsd_addr_t {
+ struct sockaddr_storage mem;
+ socklen_t len;
+ char *ip;
+ int ip_length;
+ int port;
+};
+
+int bsd_sendmmsg(LIBUS_SOCKET_DESCRIPTOR fd, void *msgvec, unsigned int vlen, int flags);
+int bsd_recvmmsg(LIBUS_SOCKET_DESCRIPTOR fd, void *msgvec, unsigned int vlen, int flags, void *timeout);
+int bsd_udp_packet_buffer_payload_length(void *msgvec, int index);
+char *bsd_udp_packet_buffer_payload(void *msgvec, int index);
+char *bsd_udp_packet_buffer_peer(void *msgvec, int index);
+int bsd_udp_packet_buffer_local_ip(void *msgvec, int index, char *ip);
+int bsd_udp_packet_buffer_ecn(void *msgvec, int index);
+void *bsd_create_udp_packet_buffer();
+void bsd_udp_buffer_set_packet_payload(struct us_udp_packet_buffer_t *send_buf, int index, int offset, void *payload, int length, void *peer_addr);
+
+LIBUS_SOCKET_DESCRIPTOR apple_no_sigpipe(LIBUS_SOCKET_DESCRIPTOR fd);
+LIBUS_SOCKET_DESCRIPTOR bsd_set_nonblocking(LIBUS_SOCKET_DESCRIPTOR fd);
+void bsd_socket_nodelay(LIBUS_SOCKET_DESCRIPTOR fd, int enabled);
+void bsd_socket_flush(LIBUS_SOCKET_DESCRIPTOR fd);
+LIBUS_SOCKET_DESCRIPTOR bsd_create_socket(int domain, int type, int protocol);
+
+void bsd_close_socket(LIBUS_SOCKET_DESCRIPTOR fd);
+void bsd_shutdown_socket(LIBUS_SOCKET_DESCRIPTOR fd);
+void bsd_shutdown_socket_read(LIBUS_SOCKET_DESCRIPTOR fd);
+
+void internal_finalize_bsd_addr(struct bsd_addr_t *addr);
+
+int bsd_local_addr(LIBUS_SOCKET_DESCRIPTOR fd, struct bsd_addr_t *addr);
+int bsd_remote_addr(LIBUS_SOCKET_DESCRIPTOR fd, struct bsd_addr_t *addr);
+
+char *bsd_addr_get_ip(struct bsd_addr_t *addr);
+int bsd_addr_get_ip_length(struct bsd_addr_t *addr);
+
+int bsd_addr_get_port(struct bsd_addr_t *addr);
+
+// called by dispatch_ready_poll
+LIBUS_SOCKET_DESCRIPTOR bsd_accept_socket(LIBUS_SOCKET_DESCRIPTOR fd, struct bsd_addr_t *addr);
+
+int bsd_recv(LIBUS_SOCKET_DESCRIPTOR fd, void *buf, int length, int flags);
+int bsd_send(LIBUS_SOCKET_DESCRIPTOR fd, const char *buf, int length, int msg_more);
+int bsd_would_block();
+
+// return LIBUS_SOCKET_ERROR or the fd that represents listen socket
+// listen both on ipv6 and ipv4
+LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket(const char *host, int port, int options);
+
+LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket_unix(const char *path, int options);
+
+/* Creates an UDP socket bound to the hostname and port */
+LIBUS_SOCKET_DESCRIPTOR bsd_create_udp_socket(const char *host, int port);
+
+LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket(const char *host, int port, const char *source_host, int options);
+
+LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket_unix(const char *server_path, int options);
+
+#endif // BSD_H
diff --git a/packages/bun-usockets/src/libusockets.h b/packages/bun-usockets/src/libusockets.h
new file mode 100644
index 000000000..130e6f6ab
--- /dev/null
+++ b/packages/bun-usockets/src/libusockets.h
@@ -0,0 +1,408 @@
+/*
+ * Authored by Alex Hultman, 2018-2019.
+ * Intellectual property of third-party.
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef us_malloc
+#define us_malloc malloc
+#endif
+
+#ifndef us_realloc
+#define us_realloc realloc
+#endif
+
+#ifndef us_free
+#define us_free free
+#endif
+
+#ifndef LIBUSOCKETS_H
+#define LIBUSOCKETS_H
+
+
+/* 512kb shared receive buffer */
+#define LIBUS_RECV_BUFFER_LENGTH 524288
+/* A timeout granularity of 4 seconds means give or take 4 seconds from set timeout */
+#define LIBUS_TIMEOUT_GRANULARITY 4
+/* 32 byte padding of receive buffer ends */
+#define LIBUS_RECV_BUFFER_PADDING 32
+/* Guaranteed alignment of extension memory */
+#define LIBUS_EXT_ALIGNMENT 16
+
+/* Define what a socket descriptor is based on platform */
+#ifdef _WIN32
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+#include <winsock2.h>
+#define LIBUS_SOCKET_DESCRIPTOR SOCKET
+#else
+#define LIBUS_SOCKET_DESCRIPTOR int
+#endif
+
+#include "stddef.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+ /* No meaning, default listen option */
+ LIBUS_LISTEN_DEFAULT,
+ /* We exclusively own this port, do not share it */
+ LIBUS_LISTEN_EXCLUSIVE_PORT
+};
+
+/* Library types publicly available */
+struct us_socket_t;
+struct us_timer_t;
+struct us_socket_context_t;
+struct us_loop_t;
+struct us_poll_t;
+struct us_udp_socket_t;
+struct us_udp_packet_buffer_t;
+
+
+struct us_cert_string_t {
+ const char* str;
+ size_t len;
+};
+
+/* Public interface for UDP sockets */
+
+/* Peeks data and length of UDP payload */
+char *us_udp_packet_buffer_payload(struct us_udp_packet_buffer_t *buf, int index);
+int us_udp_packet_buffer_payload_length(struct us_udp_packet_buffer_t *buf, int index);
+
+/* Copies out local (received destination) ip (4 or 16 bytes) of received packet */
+int us_udp_packet_buffer_local_ip(struct us_udp_packet_buffer_t *buf, int index, char *ip);
+
+/* Get the bound port in host byte order */
+int us_udp_socket_bound_port(struct us_udp_socket_t *s);
+
+/* Peeks peer addr (sockaddr) of received packet */
+char *us_udp_packet_buffer_peer(struct us_udp_packet_buffer_t *buf, int index);
+
+/* Peeks ECN of received packet */
+int us_udp_packet_buffer_ecn(struct us_udp_packet_buffer_t *buf, int index);
+
+/* Receives a set of packets into specified packet buffer */
+int us_udp_socket_receive(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf);
+
+void us_udp_buffer_set_packet_payload(struct us_udp_packet_buffer_t *send_buf, int index, int offset, void *payload, int length, void *peer_addr);
+
+int us_udp_socket_send(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int num);
+
+/* Allocates a packet buffer that is reuable per thread. Mutated by us_udp_socket_receive. */
+struct us_udp_packet_buffer_t *us_create_udp_packet_buffer();
+
+/* Creates a (heavy-weight) UDP socket with a user space ring buffer. Again, this one is heavy weight and
+ * shoud be reused. One entire QUIC server can be implemented using only one single UDP socket so weight
+ * is not a concern as is the case for TCP sockets which are 1-to-1 with TCP connections. */
+//struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, void (*read_cb)(struct us_udp_socket_t *), unsigned short port);
+
+//struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), char *host, unsigned short port);
+
+struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, struct us_udp_packet_buffer_t *buf, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), const char *host, unsigned short port, void *user);
+
+/* This one is ugly, should be ext! not user */
+void *us_udp_socket_user(struct us_udp_socket_t *s);
+
+/* Binds the UDP socket to an interface and port */
+int us_udp_socket_bind(struct us_udp_socket_t *s, const char *hostname, unsigned int port);
+
+/* Public interfaces for timers */
+
+/* Create a new high precision, low performance timer. May fail and return null */
+struct us_timer_t *us_create_timer(struct us_loop_t *loop, int fallthrough, unsigned int ext_size);
+
+/* Returns user data extension for this timer */
+void *us_timer_ext(struct us_timer_t *timer);
+
+/* */
+void us_timer_close(struct us_timer_t *timer);
+
+/* Arm a timer with a delay from now and eventually a repeat delay.
+ * Specify 0 as repeat delay to disable repeating. Specify both 0 to disarm. */
+void us_timer_set(struct us_timer_t *timer, void (*cb)(struct us_timer_t *t), int ms, int repeat_ms);
+
+/* Returns the loop for this timer */
+struct us_loop_t *us_timer_loop(struct us_timer_t *t);
+
+/* Public interfaces for contexts */
+
+struct us_socket_context_options_t {
+ const char *key_file_name;
+ const char *cert_file_name;
+ const char *passphrase;
+ const char *dh_params_file_name;
+ const char *ca_file_name;
+ const char *ssl_ciphers;
+ int ssl_prefer_low_memory_usage; /* Todo: rename to prefer_low_memory_usage and apply for TCP as well */
+};
+
+struct us_socket_events_t {
+ struct us_socket_t *(*on_open)(struct us_socket_t *, int is_client, char *ip, int ip_length);
+ struct us_socket_t *(*on_data)(struct us_socket_t *, char *data, int length);
+ struct us_socket_t *(*on_writable)(struct us_socket_t *);
+ struct us_socket_t *(*on_close)(struct us_socket_t *, int code, void *reason);
+ //void (*on_timeout)(struct us_socket_context *);
+ struct us_socket_t *(*on_timeout)(struct us_socket_t *);
+ struct us_socket_t *(*on_long_timeout)(struct us_socket_t *);
+ struct us_socket_t *(*on_end)(struct us_socket_t *);
+ struct us_socket_t *(*on_connect_error)(struct us_socket_t *, int code);
+ void (*on_handshake)(struct us_socket_t*, int success, struct us_bun_verify_error_t verify_error, void* custom_data);
+};
+
+struct us_bun_verify_error_t {
+ long error;
+ const char* code;
+ const char* reason;
+};
+
+struct us_bun_socket_context_options_t {
+ const char *key_file_name;
+ const char *cert_file_name;
+ const char *passphrase;
+ const char *dh_params_file_name;
+ const char *ca_file_name;
+ const char *ssl_ciphers;
+ int ssl_prefer_low_memory_usage; /* Todo: rename to prefer_low_memory_usage and apply for TCP as well */
+ const char **key;
+ unsigned int key_count;
+ const char **cert;
+ unsigned int cert_count;
+ const char **ca;
+ unsigned int ca_count;
+ unsigned int secure_options;
+ int reject_unauthorized;
+ int request_cert;
+};
+
+/* Return 15-bit timestamp for this context */
+unsigned short us_socket_context_timestamp(int ssl, struct us_socket_context_t *context);
+
+/* Adds SNI domain and cert in asn1 format */
+void us_socket_context_add_server_name(int ssl, struct us_socket_context_t *context, const char *hostname_pattern, struct us_socket_context_options_t options, void *user);
+void us_bun_socket_context_add_server_name(int ssl, struct us_socket_context_t *context, const char *hostname_pattern, struct us_bun_socket_context_options_t options, void *user);
+void us_socket_context_remove_server_name(int ssl, struct us_socket_context_t *context, const char *hostname_pattern);
+void us_socket_context_on_server_name(int ssl, struct us_socket_context_t *context, void (*cb)(struct us_socket_context_t *, const char *hostname));
+void *us_socket_server_name_userdata(int ssl, struct us_socket_t *s);
+void *us_socket_context_find_server_name_userdata(int ssl, struct us_socket_context_t *context, const char *hostname_pattern);
+
+/* Returns the underlying SSL native handle, such as SSL_CTX or nullptr */
+void *us_socket_context_get_native_handle(int ssl, struct us_socket_context_t *context);
+
+/* A socket context holds shared callbacks and user data extension for associated sockets */
+struct us_socket_context_t *us_create_socket_context(int ssl, struct us_loop_t *loop,
+ int ext_size, struct us_socket_context_options_t options);
+struct us_socket_context_t *us_create_bun_socket_context(int ssl, struct us_loop_t *loop,
+ int ext_size, struct us_bun_socket_context_options_t options);
+
+/* Delete resources allocated at creation time. */
+void us_socket_context_free(int ssl, struct us_socket_context_t *context);
+struct us_bun_verify_error_t us_socket_verify_error(int ssl, struct us_socket_t *context);
+/* Setters of various async callbacks */
+void us_socket_context_on_open(int ssl, struct us_socket_context_t *context,
+ struct us_socket_t *(*on_open)(struct us_socket_t *s, int is_client, char *ip, int ip_length));
+void us_socket_context_on_close(int ssl, struct us_socket_context_t *context,
+ struct us_socket_t *(*on_close)(struct us_socket_t *s, int code, void *reason));
+void us_socket_context_on_data(int ssl, struct us_socket_context_t *context,
+ struct us_socket_t *(*on_data)(struct us_socket_t *s, char *data, int length));
+void us_socket_context_on_writable(int ssl, struct us_socket_context_t *context,
+ struct us_socket_t *(*on_writable)(struct us_socket_t *s));
+void us_socket_context_on_timeout(int ssl, struct us_socket_context_t *context,
+ struct us_socket_t *(*on_timeout)(struct us_socket_t *s));
+void us_socket_context_on_long_timeout(int ssl, struct us_socket_context_t *context,
+ struct us_socket_t *(*on_timeout)(struct us_socket_t *s));
+/* This one is only used for when a connecting socket fails in a late stage. */
+void us_socket_context_on_connect_error(int ssl, struct us_socket_context_t *context,
+ struct us_socket_t *(*on_connect_error)(struct us_socket_t *s, int code));
+
+void us_socket_context_on_handshake(int ssl, struct us_socket_context_t *context, void (*on_handshake)(struct us_socket_context_t *, int success, struct us_bun_verify_error_t verify_error, void* custom_data), void* custom_data);
+
+/* Emitted when a socket has been half-closed */
+void us_socket_context_on_end(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_end)(struct us_socket_t *s));
+
+/* Returns user data extension for this socket context */
+void *us_socket_context_ext(int ssl, struct us_socket_context_t *context);
+
+/* Closes all open sockets, including listen sockets. Does not invalidate the socket context. */
+void us_socket_context_close(int ssl, struct us_socket_context_t *context);
+
+/* Listen for connections. Acts as the main driving cog in a server. Will call set async callbacks. */
+struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_context_t *context,
+ const char *host, int port, int options, int socket_ext_size);
+
+struct us_listen_socket_t *us_socket_context_listen_unix(int ssl, struct us_socket_context_t *context,
+ const char *path, int options, int socket_ext_size);
+
+/* listen_socket.c/.h */
+void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls);
+
+/* Land in on_open or on_connection_error or return null or return socket */
+struct us_socket_t *us_socket_context_connect(int ssl, struct us_socket_context_t *context,
+ const char *host, int port, const char *source_host, int options, int socket_ext_size);
+
+struct us_socket_t *us_socket_context_connect_unix(int ssl, struct us_socket_context_t *context,
+ const char *server_path, int options, int socket_ext_size);
+
+/* Is this socket established? Can be used to check if a connecting socket has fired the on_open event yet.
+ * Can also be used to determine if a socket is a listen_socket or not, but you probably know that already. */
+int us_socket_is_established(int ssl, struct us_socket_t *s);
+
+/* Cancel a connecting socket. Can be used together with us_socket_timeout to limit connection times.
+ * Entirely destroys the socket - this function works like us_socket_close but does not trigger on_close event since
+ * you never got the on_open event first. */
+struct us_socket_t *us_socket_close_connecting(int ssl, struct us_socket_t *s);
+
+/* Returns the loop for this socket context. */
+struct us_loop_t *us_socket_context_loop(int ssl, struct us_socket_context_t *context);
+
+/* Invalidates passed socket, returning a new resized socket which belongs to a different socket context.
+ * Used mainly for "socket upgrades" such as when transitioning from HTTP to WebSocket. */
+struct us_socket_t *us_socket_context_adopt_socket(int ssl, struct us_socket_context_t *context, struct us_socket_t *s, int ext_size);
+
+/* Create a child socket context which acts much like its own socket context with its own callbacks yet still relies on the
+ * parent socket context for some shared resources. Child socket contexts should be used together with socket adoptions and nothing else. */
+struct us_socket_context_t *us_create_child_socket_context(int ssl, struct us_socket_context_t *context, int context_ext_size);
+
+/* Public interfaces for loops */
+
+/* Returns a new event loop with user data extension */
+struct us_loop_t *us_create_loop(void *hint, void (*wakeup_cb)(struct us_loop_t *loop),
+ void (*pre_cb)(struct us_loop_t *loop), void (*post_cb)(struct us_loop_t *loop), unsigned int ext_size);
+
+/* Frees the loop immediately */
+void us_loop_free(struct us_loop_t *loop);
+
+/* Returns the loop user data extension */
+void *us_loop_ext(struct us_loop_t *loop);
+
+/* Blocks the calling thread and drives the event loop until no more non-fallthrough polls are scheduled */
+void us_loop_run(struct us_loop_t *loop);
+
+
+/* Signals the loop from any thread to wake up and execute its wakeup handler from the loop's own running thread.
+ * This is the only fully thread-safe function and serves as the basis for thread safety */
+void us_wakeup_loop(struct us_loop_t *loop);
+
+/* Hook up timers in existing loop */
+void us_loop_integrate(struct us_loop_t *loop);
+
+/* Returns the loop iteration number */
+long long us_loop_iteration_number(struct us_loop_t *loop);
+
+/* Public interfaces for polls */
+
+/* A fallthrough poll does not keep the loop running, it falls through */
+struct us_poll_t *us_create_poll(struct us_loop_t *loop, int fallthrough, unsigned int ext_size);
+
+/* After stopping a poll you must manually free the memory */
+void us_poll_free(struct us_poll_t *p, struct us_loop_t *loop);
+
+/* Associate this poll with a socket descriptor and poll type */
+void us_poll_init(struct us_poll_t *p, LIBUS_SOCKET_DESCRIPTOR fd, int poll_type);
+
+/* Start, change and stop polling for events */
+void us_poll_start(struct us_poll_t *p, struct us_loop_t *loop, int events);
+void us_poll_change(struct us_poll_t *p, struct us_loop_t *loop, int events);
+void us_poll_stop(struct us_poll_t *p, struct us_loop_t *loop);
+
+/* Return what events we are polling for */
+int us_poll_events(struct us_poll_t *p);
+
+/* Returns the user data extension of this poll */
+void *us_poll_ext(struct us_poll_t *p);
+
+/* Get associated socket descriptor from a poll */
+LIBUS_SOCKET_DESCRIPTOR us_poll_fd(struct us_poll_t *p);
+
+/* Resize an active poll */
+struct us_poll_t *us_poll_resize(struct us_poll_t *p, struct us_loop_t *loop, unsigned int ext_size);
+
+/* Public interfaces for sockets */
+
+/* Returns the underlying native handle for a socket, such as SSL or file descriptor.
+ * In the case of file descriptor, the value of pointer is fd. */
+void *us_socket_get_native_handle(int ssl, struct us_socket_t *s);
+
+/* Write up to length bytes of data. Returns actual bytes written.
+ * Will call the on_writable callback of active socket context on failure to write everything off in one go.
+ * Set hint msg_more if you have more immediate data to write. */
+int us_socket_write(int ssl, struct us_socket_t *s, const char *data, int length, int msg_more);
+
+/* Set a low precision, high performance timer on a socket. A socket can only have one single active timer
+ * at any given point in time. Will remove any such pre set timer */
+void us_socket_timeout(int ssl, struct us_socket_t *s, unsigned int seconds);
+
+/* Set a low precision, high performance timer on a socket. Suitable for per-minute precision. */
+void us_socket_long_timeout(int ssl, struct us_socket_t *s, unsigned int minutes);
+
+/* Return the user data extension of this socket */
+void *us_socket_ext(int ssl, struct us_socket_t *s);
+
+/* Return the socket context of this socket */
+struct us_socket_context_t *us_socket_context(int ssl, struct us_socket_t *s);
+
+/* Withdraw any msg_more status and flush any pending data */
+void us_socket_flush(int ssl, struct us_socket_t *s);
+
+/* Shuts down the connection by sending FIN and/or close_notify */
+void us_socket_shutdown(int ssl, struct us_socket_t *s);
+
+/* Shuts down the connection in terms of read, meaning next event loop
+ * iteration will catch the socket being closed. Can be used to defer closing
+ * to next event loop iteration. */
+void us_socket_shutdown_read(int ssl, struct us_socket_t *s);
+
+/* Returns whether the socket has been shut down or not */
+int us_socket_is_shut_down(int ssl, struct us_socket_t *s);
+
+/* Returns whether this socket has been closed. Only valid if memory has not yet been released. */
+int us_socket_is_closed(int ssl, struct us_socket_t *s);
+
+/* Immediately closes the socket */
+struct us_socket_t *us_socket_close(int ssl, struct us_socket_t *s, int code, void *reason);
+
+/* Returns local port or -1 on failure. */
+int us_socket_local_port(int ssl, struct us_socket_t *s);
+
+/* Copy remote (IP) address of socket, or fail with zero length. */
+void us_socket_remote_address(int ssl, struct us_socket_t *s, char *buf, int *length);
+
+/* Bun extras */
+struct us_socket_t *us_socket_detach(int ssl, struct us_socket_t *s);
+struct us_socket_t *us_socket_attach(int ssl, LIBUS_SOCKET_DESCRIPTOR client_fd, struct us_socket_context_t *ctx, int flags, int socket_ext_size);
+struct us_socket_t *us_socket_wrap_with_tls(int ssl, struct us_socket_t *s, struct us_bun_socket_context_options_t options, struct us_socket_events_t events, int socket_ext_size);
+int us_socket_raw_write(int ssl, struct us_socket_t *s, const char *data, int length, int msg_more);
+struct us_socket_t* us_socket_open(int ssl, struct us_socket_t * s, int is_client, char* ip, int ip_length);
+int us_raw_root_certs(struct us_cert_string_t**out);
+
+#ifdef __cplusplus
+}
+#endif
+
+/* Decide what eventing system to use by default */
+#if !defined(LIBUS_USE_EPOLL) && !defined(LIBUS_USE_LIBUV) && !defined(LIBUS_USE_GCD) && !defined(LIBUS_USE_KQUEUE) && !defined(LIBUS_USE_ASIO)
+#if defined(_WIN32)
+#define LIBUS_USE_LIBUV
+#elif defined(__APPLE__) || defined(__FreeBSD__)
+#define LIBUS_USE_KQUEUE
+#else
+#define LIBUS_USE_EPOLL
+#endif
+#endif
+
+#endif // LIBUSOCKETS_H
diff --git a/packages/bun-usockets/src/loop.c b/packages/bun-usockets/src/loop.c
new file mode 100644
index 000000000..9ad1e64bf
--- /dev/null
+++ b/packages/bun-usockets/src/loop.c
@@ -0,0 +1,364 @@
+/*
+ * Authored by Alex Hultman, 2018-2021.
+ * Intellectual property of third-party.
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libusockets.h"
+#include "internal/internal.h"
+#include <stdlib.h>
+#include <sys/ioctl.h>
+
+/* The loop has 2 fallthrough polls */
+void us_internal_loop_data_init(struct us_loop_t *loop, void (*wakeup_cb)(struct us_loop_t *loop),
+ void (*pre_cb)(struct us_loop_t *loop), void (*post_cb)(struct us_loop_t *loop)) {
+ loop->data.sweep_timer = us_create_timer(loop, 1, 0);
+ loop->data.recv_buf = malloc(LIBUS_RECV_BUFFER_LENGTH + LIBUS_RECV_BUFFER_PADDING * 2);
+ loop->data.ssl_data = 0;
+ loop->data.head = 0;
+ loop->data.iterator = 0;
+ loop->data.closed_head = 0;
+ loop->data.low_prio_head = 0;
+ loop->data.low_prio_budget = 0;
+
+ loop->data.pre_cb = pre_cb;
+ loop->data.post_cb = post_cb;
+ loop->data.iteration_nr = 0;
+
+ loop->data.wakeup_async = us_internal_create_async(loop, 1, 0);
+ us_internal_async_set(loop->data.wakeup_async, (void (*)(struct us_internal_async *)) wakeup_cb);
+}
+
+void us_internal_loop_data_free(struct us_loop_t *loop) {
+#ifndef LIBUS_NO_SSL
+ us_internal_free_loop_ssl_data(loop);
+#endif
+
+ free(loop->data.recv_buf);
+
+ us_timer_close(loop->data.sweep_timer);
+ us_internal_async_close(loop->data.wakeup_async);
+}
+
+void us_wakeup_loop(struct us_loop_t *loop) {
+ us_internal_async_wakeup(loop->data.wakeup_async);
+}
+
+void us_internal_loop_link(struct us_loop_t *loop, struct us_socket_context_t *context) {
+ /* Insert this context as the head of loop */
+ context->next = loop->data.head;
+ context->prev = 0;
+ if (loop->data.head) {
+ loop->data.head->prev = context;
+ }
+ loop->data.head = context;
+}
+
+/* Unlink is called before free */
+void us_internal_loop_unlink(struct us_loop_t *loop, struct us_socket_context_t *context) {
+ if (loop->data.head == context) {
+ loop->data.head = context->next;
+ if (loop->data.head) {
+ loop->data.head->prev = 0;
+ }
+ } else {
+ context->prev->next = context->next;
+ if (context->next) {
+ context->next->prev = context->prev;
+ }
+ }
+}
+
+/* This functions should never run recursively */
+void us_internal_timer_sweep(struct us_loop_t *loop) {
+ struct us_internal_loop_data_t *loop_data = &loop->data;
+ /* For all socket contexts in this loop */
+ for (loop_data->iterator = loop_data->head; loop_data->iterator; loop_data->iterator = loop_data->iterator->next) {
+
+ struct us_socket_context_t *context = loop_data->iterator;
+
+ /* Update this context's timestamps (this could be moved to loop and done once) */
+ context->global_tick++;
+ unsigned char short_ticks = context->timestamp = context->global_tick % 240;
+ unsigned char long_ticks = context->long_timestamp = (context->global_tick / 15) % 240;
+
+ /* Begin at head */
+ struct us_socket_t *s = context->head_sockets;
+ while (s) {
+ /* Seek until end or timeout found (tightest loop) */
+ while (1) {
+ /* We only read from 1 random cache line here */
+ if (short_ticks == s->timeout || long_ticks == s->long_timeout) {
+ break;
+ }
+
+ /* Did we reach the end without a find? */
+ if ((s = s->next) == 0) {
+ goto next_context;
+ }
+ }
+
+ /* Here we have a timeout to emit (slow path) */
+ context->iterator = s;
+
+ if (short_ticks == s->timeout) {
+ s->timeout = 255;
+ context->on_socket_timeout(s);
+ }
+
+ if (context->iterator == s && long_ticks == s->long_timeout) {
+ s->long_timeout = 255;
+ context->on_socket_long_timeout(s);
+ }
+
+ /* Check for unlink / link (if the event handler did not modify the chain, we step 1) */
+ if (s == context->iterator) {
+ s = s->next;
+ } else {
+ /* The iterator was changed by event handler */
+ s = context->iterator;
+ }
+ }
+ /* We always store a 0 to context->iterator here since we are no longer iterating this context */
+ next_context:
+ context->iterator = 0;
+ }
+}
+
+/* We do not want to block the loop with tons and tons of CPU-intensive work for SSL handshakes.
+ * Spread it out during many loop iterations, prioritizing already open connections, they are far
+ * easier on CPU */
+static const int MAX_LOW_PRIO_SOCKETS_PER_LOOP_ITERATION = 5;
+
+void us_internal_handle_low_priority_sockets(struct us_loop_t *loop) {
+ struct us_internal_loop_data_t *loop_data = &loop->data;
+ struct us_socket_t *s;
+
+ loop_data->low_prio_budget = MAX_LOW_PRIO_SOCKETS_PER_LOOP_ITERATION;
+
+ for (s = loop_data->low_prio_head; s && loop_data->low_prio_budget > 0; s = loop_data->low_prio_head, loop_data->low_prio_budget--) {
+ /* Unlink this socket from the low-priority queue */
+ loop_data->low_prio_head = s->next;
+ if (s->next) s->next->prev = 0;
+ s->next = 0;
+
+ us_internal_socket_context_link_socket(s->context, s);
+ us_poll_change(&s->p, us_socket_context(0, s)->loop, us_poll_events(&s->p) | LIBUS_SOCKET_READABLE);
+
+ s->low_prio_state = 2;
+ }
+}
+
+/* Note: Properly takes the linked list and timeout sweep into account */
+void us_internal_free_closed_sockets(struct us_loop_t *loop) {
+ /* Free all closed sockets (maybe it is better to reverse order?) */
+ if (loop->data.closed_head) {
+ for (struct us_socket_t *s = loop->data.closed_head; s; ) {
+ struct us_socket_t *next = s->next;
+ us_poll_free((struct us_poll_t *) s, loop);
+ s = next;
+ }
+ loop->data.closed_head = 0;
+ }
+}
+
+void sweep_timer_cb(struct us_internal_callback_t *cb) {
+ us_internal_timer_sweep(cb->loop);
+}
+
+long long us_loop_iteration_number(struct us_loop_t *loop) {
+ return loop->data.iteration_nr;
+}
+
+/* These may have somewhat different meaning depending on the underlying event library */
+void us_internal_loop_pre(struct us_loop_t *loop) {
+ loop->data.iteration_nr++;
+ us_internal_handle_low_priority_sockets(loop);
+ loop->data.pre_cb(loop);
+}
+
+void us_internal_loop_post(struct us_loop_t *loop) {
+ us_internal_free_closed_sockets(loop);
+ loop->data.post_cb(loop);
+}
+
+void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int events) {
+ switch (us_internal_poll_type(p)) {
+ case POLL_TYPE_CALLBACK: {
+ struct us_internal_callback_t *cb = (struct us_internal_callback_t *) p;
+ /* Timers, asyncs should accept (read), while UDP sockets should obviously not */
+ if (!cb->leave_poll_ready) {
+ /* Let's just have this macro to silence the CodeQL alert regarding empty function when using libuv */
+ #ifndef LIBUS_USE_LIBUV
+ us_internal_accept_poll_event(p);
+ #endif
+ }
+ cb->cb(cb->cb_expects_the_loop ? (struct us_internal_callback_t *) cb->loop : (struct us_internal_callback_t *) &cb->p);
+ }
+ break;
+ case POLL_TYPE_SEMI_SOCKET: {
+ /* Both connect and listen sockets are semi-sockets
+ * but they poll for different events */
+ if (us_poll_events(p) == LIBUS_SOCKET_WRITABLE) {
+ struct us_socket_t *s = (struct us_socket_t *) p;
+
+ /* It is perfectly possible to come here with an error */
+ if (error) {
+ /* Emit error, close without emitting on_close */
+ s->context->on_connect_error(s, 0);
+ us_socket_close_connecting(0, s);
+ } else {
+ /* All sockets poll for readable */
+ us_poll_change(p, s->context->loop, LIBUS_SOCKET_READABLE);
+
+ /* We always use nodelay */
+ bsd_socket_nodelay(us_poll_fd(p), 1);
+
+ /* We are now a proper socket */
+ us_internal_poll_set_type(p, POLL_TYPE_SOCKET);
+
+ /* If we used a connection timeout we have to reset it here */
+ us_socket_timeout(0, s, 0);
+
+ s->context->on_open(s, 1, 0, 0);
+ }
+ } else {
+ struct us_listen_socket_t *listen_socket = (struct us_listen_socket_t *) p;
+ struct bsd_addr_t addr;
+
+ LIBUS_SOCKET_DESCRIPTOR client_fd = bsd_accept_socket(us_poll_fd(p), &addr);
+ if (client_fd == LIBUS_SOCKET_ERROR) {
+ /* Todo: start timer here */
+
+ } else {
+
+ /* Todo: stop timer if any */
+
+ do {
+ struct us_poll_t *accepted_p = us_create_poll(us_socket_context(0, &listen_socket->s)->loop, 0, sizeof(struct us_socket_t) - sizeof(struct us_poll_t) + listen_socket->socket_ext_size);
+ us_poll_init(accepted_p, client_fd, POLL_TYPE_SOCKET);
+ us_poll_start(accepted_p, listen_socket->s.context->loop, LIBUS_SOCKET_READABLE);
+
+ struct us_socket_t *s = (struct us_socket_t *) accepted_p;
+
+ s->context = listen_socket->s.context;
+ s->timeout = 255;
+ s->long_timeout = 255;
+ s->low_prio_state = 0;
+
+ /* We always use nodelay */
+ bsd_socket_nodelay(client_fd, 1);
+
+ us_internal_socket_context_link_socket(listen_socket->s.context, s);
+
+ listen_socket->s.context->on_open(s, 0, bsd_addr_get_ip(&addr), bsd_addr_get_ip_length(&addr));
+
+ /* Exit accept loop if listen socket was closed in on_open handler */
+ if (us_socket_is_closed(0, &listen_socket->s)) {
+ break;
+ }
+
+ } while ((client_fd = bsd_accept_socket(us_poll_fd(p), &addr)) != LIBUS_SOCKET_ERROR);
+ }
+ }
+ }
+ break;
+ case POLL_TYPE_SOCKET_SHUT_DOWN:
+ case POLL_TYPE_SOCKET: {
+ /* We should only use s, no p after this point */
+ struct us_socket_t *s = (struct us_socket_t *) p;
+
+ if (events & LIBUS_SOCKET_WRITABLE && !error) {
+ /* Note: if we failed a write as a socket of one loop then adopted
+ * to another loop, this will be wrong. Absurd case though */
+ s->context->loop->data.last_write_failed = 0;
+
+ s = s->context->on_writable(s);
+
+ if (us_socket_is_closed(0, s)) {
+ return;
+ }
+
+ /* If we have no failed write or if we shut down, then stop polling for more writable */
+ if (!s->context->loop->data.last_write_failed || us_socket_is_shut_down(0, s)) {
+ us_poll_change(&s->p, us_socket_context(0, s)->loop, us_poll_events(&s->p) & LIBUS_SOCKET_READABLE);
+ }
+ }
+
+ if (events & LIBUS_SOCKET_READABLE) {
+ /* Contexts may prioritize down sockets that are currently readable, e.g. when SSL handshake has to be done.
+ * SSL handshakes are CPU intensive, so we limit the number of handshakes per loop iteration, and move the rest
+ * to the low-priority queue */
+ if (s->context->is_low_prio(s)) {
+ if (s->low_prio_state == 2) {
+ s->low_prio_state = 0; /* Socket has been delayed and now it's time to process incoming data for one iteration */
+ } else if (s->context->loop->data.low_prio_budget > 0) {
+ s->context->loop->data.low_prio_budget--; /* Still having budget for this iteration - do normal processing */
+ } else {
+ us_poll_change(&s->p, us_socket_context(0, s)->loop, us_poll_events(&s->p) & LIBUS_SOCKET_WRITABLE);
+ us_internal_socket_context_unlink_socket(s->context, s);
+
+ /* Link this socket to the low-priority queue - we use a LIFO queue, to prioritize newer clients that are
+ * maybe not already timeouted - sounds unfair, but works better in real-life with smaller client-timeouts
+ * under high load */
+ s->prev = 0;
+ s->next = s->context->loop->data.low_prio_head;
+ if (s->next) s->next->prev = s;
+ s->context->loop->data.low_prio_head = s;
+
+ s->low_prio_state = 1;
+
+ break;
+ }
+ }
+
+ int length = bsd_recv(us_poll_fd(&s->p), s->context->loop->data.recv_buf + LIBUS_RECV_BUFFER_PADDING, LIBUS_RECV_BUFFER_LENGTH, 0);
+ if (length > 0) {
+ s = s->context->on_data(s, s->context->loop->data.recv_buf + LIBUS_RECV_BUFFER_PADDING, length);
+ } else if (!length) {
+ if (us_socket_is_shut_down(0, s)) {
+ /* We got FIN back after sending it */
+ /* Todo: We should give "CLEAN SHUTDOWN" as reason here */
+ s = us_socket_close(0, s, 0, NULL);
+ } else {
+ /* We got FIN, so stop polling for readable */
+ us_poll_change(&s->p, us_socket_context(0, s)->loop, us_poll_events(&s->p) & LIBUS_SOCKET_WRITABLE);
+ s = s->context->on_end(s);
+ }
+ } else if (length == LIBUS_SOCKET_ERROR && !bsd_would_block()) {
+ /* Todo: decide also here what kind of reason we should give */
+ s = us_socket_close(0, s, 0, NULL);
+ return;
+ }
+ }
+
+ /* Such as epollerr epollhup */
+ if (error) {
+ /* Todo: decide what code we give here */
+ s = us_socket_close(0, s, 0, NULL);
+ return;
+ }
+ }
+ break;
+ }
+}
+
+/* Integration only requires the timer to be set up */
+void us_loop_integrate(struct us_loop_t *loop) {
+ us_timer_set(loop->data.sweep_timer, (void (*)(struct us_timer_t *)) sweep_timer_cb, LIBUS_TIMEOUT_GRANULARITY * 1000, LIBUS_TIMEOUT_GRANULARITY * 1000);
+}
+
+void *us_loop_ext(struct us_loop_t *loop) {
+ return loop + 1;
+}
diff --git a/packages/bun-usockets/src/quic.c b/packages/bun-usockets/src/quic.c
new file mode 100644
index 000000000..529074632
--- /dev/null
+++ b/packages/bun-usockets/src/quic.c
@@ -0,0 +1,1071 @@
+#ifdef LIBUS_USE_QUIC
+
+/* Todo: quic layer should not use bsd layer directly (sendmmsg) */
+#include "internal/networking/bsd.h"
+
+#include "quic.h"
+
+
+
+#include "lsquic.h"
+#include "lsquic_types.h"
+#include "lsxpack_header.h"
+
+/* Todo: remove these */
+#ifndef _WIN32
+#include <netinet/in.h>
+#include <errno.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void leave_all();
+
+/*
+struct sockaddr_in client_addr = {
+ AF_INET,
+ 1,
+ 1
+};
+
+struct sockaddr_in server_addr = {
+ AF_INET,
+ 2,
+ 2
+};*/
+
+ // used in process_quic
+ lsquic_engine_t *global_engine;
+ lsquic_engine_t *global_client_engine;
+
+/* Socket context */
+struct us_quic_socket_context_s {
+
+ struct us_udp_packet_buffer_t *recv_buf;
+ //struct us_udp_packet_buffer_t *send_buf;
+ int outgoing_packets;
+
+ //struct us_udp_socket_t *udp_socket;
+ struct us_loop_t *loop;
+ lsquic_engine_t *engine;
+ lsquic_engine_t *client_engine;
+
+ // we store the options the context was created with here
+ us_quic_socket_context_options_t options;
+
+ void(*on_stream_data)(us_quic_stream_t *s, char *data, int length);
+ void(*on_stream_end)(us_quic_stream_t *s);
+ void(*on_stream_headers)(us_quic_stream_t *s);
+ void(*on_stream_open)(us_quic_stream_t *s, int is_client);
+ void(*on_stream_close)(us_quic_stream_t *s);
+ void(*on_stream_writable)(us_quic_stream_t *s);
+ void(*on_open)(us_quic_socket_t *s, int is_client);
+ void(*on_close)(us_quic_socket_t *s);
+};
+
+/* Setters */
+void us_quic_socket_context_on_stream_data(us_quic_socket_context_t *context, void(*on_stream_data)(us_quic_stream_t *s, char *data, int length)) {
+ context->on_stream_data = on_stream_data;
+}
+void us_quic_socket_context_on_stream_end(us_quic_socket_context_t *context, void(*on_stream_end)(us_quic_stream_t *s)) {
+ context->on_stream_end = on_stream_end;
+}
+void us_quic_socket_context_on_stream_headers(us_quic_socket_context_t *context, void(*on_stream_headers)(us_quic_stream_t *s)) {
+ context->on_stream_headers = on_stream_headers;
+}
+void us_quic_socket_context_on_stream_open(us_quic_socket_context_t *context, void(*on_stream_open)(us_quic_stream_t *s, int is_client)) {
+ context->on_stream_open = on_stream_open;
+}
+void us_quic_socket_context_on_stream_close(us_quic_socket_context_t *context, void(*on_stream_close)(us_quic_stream_t *s)) {
+ context->on_stream_close = on_stream_close;
+}
+void us_quic_socket_context_on_open(us_quic_socket_context_t *context, void(*on_open)(us_quic_socket_t *s, int is_client)) {
+ context->on_open = on_open;
+}
+void us_quic_socket_context_on_close(us_quic_socket_context_t *context, void(*on_close)(us_quic_socket_t *s)) {
+ context->on_close = on_close;
+}
+void us_quic_socket_context_on_stream_writable(us_quic_socket_context_t *context, void(*on_stream_writable)(us_quic_stream_t *s)) {
+ context->on_stream_writable = on_stream_writable;
+}
+
+/* UDP handlers */
+void on_udp_socket_writable(struct us_udp_socket_t *s) {
+ /* Need context from socket here */
+ us_quic_socket_context_t *context = us_udp_socket_user(s);
+
+ /* We just continue now */
+ lsquic_engine_send_unsent_packets(context->engine);
+}
+
+// we need two differetn handlers to know to put it in client or servcer context
+void on_udp_socket_data_client(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int packets) {
+
+ int fd = us_poll_fd((struct us_poll_t *) s);
+ //printf("Reading on fd: %d\n", fd);
+
+ //printf("UDP (client) socket got data: %p\n", s);
+
+ /* We need to lookup the context from the udp socket */
+ //us_udpus_udp_socket_context(s);
+ // do we have udp socket contexts? or do we just have user data?
+
+ us_quic_socket_context_t *context = us_udp_socket_user(s);
+
+ /* We just shove it to lsquic */
+ for (int i = 0; i < packets; i++) {
+ char *payload = us_udp_packet_buffer_payload(buf, i);
+ int length = us_udp_packet_buffer_payload_length(buf, i);
+ int ecn = us_udp_packet_buffer_ecn(buf, i);
+ void *peer_addr = us_udp_packet_buffer_peer(buf, i);
+
+ //printf("Reading UDP of size %d\n", length);
+
+ char ip[16];
+ int ip_length = us_udp_packet_buffer_local_ip(buf, i, ip);
+ if (!ip_length) {
+ printf("We got no ip on received packet!\n");
+ exit(0);
+ }
+
+ //printf("Our received destination IP length is: %d\n", ip_length);
+
+ int port = us_udp_socket_bound_port(s);
+ //printf("We received packet on port: %d\n", port);
+
+ /* We build our address based on what the dest addr is */
+ struct sockaddr_storage local_addr = {0};
+ if (ip_length == 16) {
+ struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *) &local_addr;
+
+ ipv6->sin6_family = AF_INET6;
+ ipv6->sin6_port = ntohs(port);
+ memcpy(ipv6->sin6_addr.s6_addr, ip, 16);
+ } else {
+ struct sockaddr_in *ipv4 = (struct sockaddr_in *) &local_addr;
+
+ ipv4->sin_family = AF_INET;
+ ipv4->sin_port = ntohs(port);
+ memcpy(&ipv4->sin_addr.s_addr, ip, 4);
+ }
+
+
+ int ret = lsquic_engine_packet_in(context->client_engine, payload, length, (struct sockaddr *) &local_addr, peer_addr, (void *) s, 0);
+ //printf("Engine returned: %d\n", ret);
+
+
+ }
+
+ lsquic_engine_process_conns(context->client_engine);
+
+}
+
+void on_udp_socket_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int packets) {
+
+
+ //printf("UDP socket got data: %p\n", s);
+
+ /* We need to lookup the context from the udp socket */
+ //us_udpus_udp_socket_context(s);
+ // do we have udp socket contexts? or do we just have user data?
+
+ us_quic_socket_context_t *context = us_udp_socket_user(s);
+
+ // process conns now? to accept new connections?
+ lsquic_engine_process_conns(context->engine);
+
+ /* We just shove it to lsquic */
+ for (int i = 0; i < packets; i++) {
+ char *payload = us_udp_packet_buffer_payload(buf, i);
+ int length = us_udp_packet_buffer_payload_length(buf, i);
+ int ecn = us_udp_packet_buffer_ecn(buf, i);
+ void *peer_addr = us_udp_packet_buffer_peer(buf, i);
+
+ //printf("Reading UDP of size %d\n", length);
+
+ char ip[16];
+ int ip_length = us_udp_packet_buffer_local_ip(buf, i, ip);
+ if (!ip_length) {
+ printf("We got no ip on received packet!\n");
+ exit(0);
+ }
+
+ //printf("Our received destination IP length is: %d\n", ip_length);
+
+ int port = us_udp_socket_bound_port(s);
+ //printf("We received packet on port: %d\n", port);
+
+ /* We build our address based on what the dest addr is */
+ struct sockaddr_storage local_addr = {0};
+ if (ip_length == 16) {
+ struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *) &local_addr;
+
+ ipv6->sin6_family = AF_INET6;
+ ipv6->sin6_port = ntohs(port);
+ memcpy(ipv6->sin6_addr.s6_addr, ip, 16);
+ } else {
+
+ struct sockaddr_in *ipv4 = (struct sockaddr_in *) &local_addr;
+
+ ipv4->sin_family = AF_INET;
+ ipv4->sin_port = ntohs(port);
+ memcpy(&ipv4->sin_addr.s_addr, ip, 4);
+ }
+
+
+ int ret = lsquic_engine_packet_in(context->engine, payload, length, (struct sockaddr *) &local_addr, peer_addr, (void *) s, 0);
+ //printf("Engine returned: %d\n", ret);
+
+
+ }
+
+ lsquic_engine_process_conns(context->engine);
+
+}
+
+/* Let's use this on Windows and macOS where it is not defined (todo: put in bsd.h) */
+#ifndef UIO_MAXIOV
+#define UIO_MAXIOV 1024
+
+#ifndef _WIN32
+struct mmsghdr {
+ struct msghdr msg_hdr; /* Message header */
+ unsigned int msg_len; /* Number of bytes transmitted */
+};
+#endif
+#endif
+
+/* Server and client packet out is identical */
+int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_specs) {
+#ifndef _WIN32
+ us_quic_socket_context_t *context = ctx;
+
+ /* A run is at most UIO_MAXIOV datagrams long */
+ struct mmsghdr hdrs[UIO_MAXIOV];
+ int run_length = 0;
+
+ /* We assume that thiss whole cb will never be called with 0 specs */
+ struct us_udp_socket_t *last_socket = (struct us_udp_socket_t *) specs[0].peer_ctx;
+
+ int sent = 0;
+ for (int i = 0; i < n_specs; i++) {
+ /* Send this run if we need to */
+ if (run_length == UIO_MAXIOV || specs[i].peer_ctx != last_socket) {
+ int ret = bsd_sendmmsg(us_poll_fd((struct us_poll_t *) last_socket), hdrs, run_length, 0);
+ if (ret != run_length) {
+ if (ret == -1) {
+ printf("unhandled udp backpressure!\n");
+ return sent;
+ } else {
+ printf("unhandled udp backpressure!\n");
+ errno = EAGAIN;
+ return sent + ret;
+ }
+ }
+ sent += ret;
+ run_length = 0;
+ last_socket = specs[i].peer_ctx;
+ //printf("different socket breask run!\n");
+ }
+
+ /* Continue existing run or start a new one */
+ //memset(&hdrs[i].msg_hdr, 0, sizeof(hdrs[i].msg_hdr));
+ memset(&hdrs[run_length], 0, sizeof(hdrs[run_length]));
+
+ hdrs[run_length].msg_hdr.msg_name = (void *) specs[i].dest_sa;
+ hdrs[run_length].msg_hdr.msg_namelen = (AF_INET == specs[i].dest_sa->sa_family ?
+ sizeof(struct sockaddr_in) :
+ sizeof(struct sockaddr_in6)),
+ hdrs[run_length].msg_hdr.msg_iov = specs[i].iov;
+ hdrs[run_length].msg_hdr.msg_iovlen = specs[i].iovlen;
+ hdrs[run_length].msg_hdr.msg_flags = 0;
+
+ run_length++;
+ }
+
+ /* Send last run */
+ if (run_length) {
+ int ret = bsd_sendmmsg(us_poll_fd((struct us_poll_t *) last_socket), hdrs, run_length, 0);
+ if (ret == -1) {
+ printf("backpressure! A\n");
+ return sent;
+ }
+ if (sent + ret != n_specs) {
+ printf("backpressure! B\n");
+ printf("errno is: %d\n", errno);
+ errno = EAGAIN;
+ }
+ //printf("Returning %d of %d\n", sent + ret, n_specs);
+ return sent + ret;
+ }
+
+ //printf("Returning %d\n", n_specs);
+
+#endif
+
+ return n_specs;
+}
+
+lsquic_conn_ctx_t *on_new_conn(void *stream_if_ctx, lsquic_conn_t *c) {
+ us_quic_socket_context_t *context = stream_if_ctx;
+
+ printf("Context is: %p\n", context);
+
+ /* We need to create some kind of socket here */
+
+ int is_client = 0;
+ if (lsquic_conn_get_engine(c) == context->client_engine) {
+ is_client = 1;
+ }
+
+ context->on_open((us_quic_socket_t *) c, is_client);
+
+ return (lsquic_conn_ctx_t *) context;
+}
+
+void us_quic_socket_create_stream(us_quic_socket_t *s, int ext_size) {
+ lsquic_conn_make_stream((lsquic_conn_t *) s);
+
+ // here we need to allocate and attach the user data
+
+}
+
+void on_conn_closed(lsquic_conn_t *c) {
+ us_quic_socket_context_t *context = (us_quic_socket_context_t *) lsquic_conn_get_ctx(c);
+
+ printf("on_conn_closed!\n");
+
+ context->on_close((us_quic_socket_t *) c);
+}
+
+lsquic_stream_ctx_t *on_new_stream(void *stream_if_ctx, lsquic_stream_t *s) {
+
+ /* In true usockets style we always want read */
+ lsquic_stream_wantread(s, 1);
+
+ us_quic_socket_context_t *context = stream_if_ctx;
+
+ // the conn's ctx should point at the udp socket and the socket context
+ // the ext size of streams and conn's are set by the listen/connect calls, which
+ // are the calls that create the UDP socket so we need conn to point to the UDP socket
+ // to get that ext_size set in listen/connect calls, back here.
+ // todo: hardcoded for now
+
+ int ext_size = 256;
+
+ void *ext = malloc(ext_size);
+ // yes hello
+ strcpy(ext, "Hello I am ext!");
+
+ int is_client = 0;
+ if (lsquic_conn_get_engine(lsquic_stream_conn(s)) == context->client_engine) {
+ is_client = 1;
+ }
+
+ // luckily we can set the ext before we return
+ lsquic_stream_set_ctx(s, ext);
+ context->on_stream_open((us_quic_stream_t *) s, is_client);
+
+ return ext;
+}
+
+//#define V(v) (v), strlen(v)
+
+// header bug is really just an offset buffer - perfect for per context!
+// could even use cork buffer or similar
+struct header_buf
+{
+ unsigned off;
+ char buf[UINT16_MAX];
+};
+
+int
+header_set_ptr (struct lsxpack_header *hdr, struct header_buf *header_buf,
+ const char *name, size_t name_len,
+ const char *val, size_t val_len)
+{
+ if (header_buf->off + name_len + val_len <= sizeof(header_buf->buf))
+ {
+ memcpy(header_buf->buf + header_buf->off, name, name_len);
+ memcpy(header_buf->buf + header_buf->off + name_len, val, val_len);
+ lsxpack_header_set_offset2(hdr, header_buf->buf + header_buf->off,
+ 0, name_len, name_len, val_len);
+ header_buf->off += name_len + val_len;
+ return 0;
+ }
+ else
+ return -1;
+}
+
+/* Static storage should be per context or really per loop */
+struct header_buf hbuf;
+struct lsxpack_header headers_arr[10];
+
+void us_quic_socket_context_set_header(us_quic_socket_context_t *context, int index, const char *key, int key_length, const char *value, int value_length) {
+ if (header_set_ptr(&headers_arr[index], &hbuf, key, key_length, value, value_length) != 0) {
+ printf("CANNOT FORMAT HEADER!\n");
+ exit(0);
+ }
+}
+
+void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_quic_stream_t *s, int num, int has_body) {
+
+ lsquic_http_headers_t headers = {
+ .count = num,
+ .headers = headers_arr,
+ };
+ // last here is whether this is eof or not (has body)
+ if (lsquic_stream_send_headers((lsquic_stream_t *) s, &headers, has_body ? 0 : 1)) {// pass 0 if data
+ printf("CANNOT SEND HEADERS!\n");
+ exit(0);
+ }
+
+ /* Reset header offset */
+ hbuf.off = 0;
+}
+
+int us_quic_stream_is_client(us_quic_stream_t *s) {
+ us_quic_socket_context_t *context = (us_quic_socket_context_t *) lsquic_conn_get_ctx(lsquic_stream_conn((lsquic_stream_t *) s));
+
+ int is_client = 0;
+ if (lsquic_conn_get_engine(lsquic_stream_conn((lsquic_stream_t *) s)) == context->client_engine) {
+ is_client = 1;
+ }
+ return is_client;
+}
+
+us_quic_socket_t *us_quic_stream_socket(us_quic_stream_t *s) {
+ return (us_quic_socket_t *) lsquic_stream_conn((lsquic_stream_t *) s);
+}
+
+//#include <errno.h>
+
+
+// only for servers?
+static void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) {
+
+ /* The user data of the connection owning the stream, points to the socket context */
+ us_quic_socket_context_t *context = (us_quic_socket_context_t *) lsquic_conn_get_ctx(lsquic_stream_conn(s));
+
+ /* This object is (and must be) fetched from a stream by
+ * calling lsquic_stream_get_hset() before the stream can be read. */
+ /* This call must precede calls to lsquic_stream_read(), lsquic_stream_readv(), and lsquic_stream_readf(). */
+ void *header_set = lsquic_stream_get_hset(s);
+ if (header_set) {
+ context->on_stream_headers((us_quic_stream_t *) s);
+ // header management is obviously broken and needs to be per-stream
+ leave_all();
+ }
+
+ // all of this logic should be moved to uws and WE here should only hand over the data
+
+ char temp[4096] = {0};
+ int nr = lsquic_stream_read(s, temp, 4096);
+
+ // emit on_end when we receive fin, regardless of whether we emitted data yet
+ if (nr == 0) {
+ // any time we read EOF we stop reading
+ lsquic_stream_wantread(s, 0);
+ context->on_stream_end((us_quic_stream_t *) s);
+ } else if (nr == -1) {
+ if (errno != EWOULDBLOCK) {
+ // error handling should not be needed if we use lsquic correctly
+ printf("UNHANDLED ON_READ ERROR\n");
+ exit(0);
+ }
+ // if we for some reason could not read even though we were told to read, we just ignore it
+ // this should not really happen but whatever
+ } else {
+ // otherwise if we have data, then emit it
+ context->on_stream_data((us_quic_stream_t *) s, temp, nr);
+ }
+
+ // that's it
+ return;
+
+ //lsquic_stream_readf
+
+ printf("read returned: %d\n", nr);
+
+ // we will get 9, ebadf if we read from a closed stream
+ if (nr == -1) {
+ printf("Error in reading! errno is: %d\n", errno);
+ if (errno != EWOULDBLOCK) {
+ printf("Errno is not EWOULDBLOCK\n");
+ } else {
+ printf("Errno is would block, fine!\n");
+ }
+ exit(0);
+ return;
+ }
+
+ /* We have reached EOF */
+ if (nr == 0) {
+
+ /* Are we polling for writable (todo: make this check faster)? */
+ if (lsquic_stream_wantwrite(s, 1)) {
+
+ // we happened to be polling for writable so leave the connection open until on_write eventually closes it
+ printf("we are polling for write, so leaving the stream open!\n");
+
+ // stop reading though!
+ lsquic_stream_wantread(s, 0); // I hope this is fine? half open?
+
+ } else {
+ // we weren't polling for writable so reset it to old value
+ lsquic_stream_wantwrite(s, 0);
+
+ // I guess we can close it since we have called shutdown before this so data should flow out
+ lsquic_stream_close(s);
+ }
+
+ // reached the EOF
+ //lsquic_stream_close(s);
+ //lsquic_stream_wantread(s, 0);
+ return;
+ }
+
+ //printf("read: %d\n", nr);
+
+ //printf("%s\n", temp);
+
+ // why do we get tons of zero reads?
+ // maybe it doesn't matter, if we can parse this input then we are fine
+ //lsquic_stream_wantread(s, 0);
+ //lsquic_stream_wantwrite(s, 1);
+
+ printf("on_stream_data: %d\n", nr);
+ context->on_stream_data((us_quic_stream_t *) s, temp, nr);
+}
+
+int us_quic_stream_write(us_quic_stream_t *s, char *data, int length) {
+ lsquic_stream_t *stream = (lsquic_stream_t *) s;
+ int ret = lsquic_stream_write((lsquic_stream_t *) s, data, length);
+ // just like otherwise, we automatically poll for writable when failed
+ if (ret != length) {
+ lsquic_stream_wantwrite((lsquic_stream_t *) s, 1);
+ } else {
+ lsquic_stream_wantwrite((lsquic_stream_t *) s, 0);
+ }
+ return ret;
+}
+
+static void on_write (lsquic_stream_t *s, lsquic_stream_ctx_t *h) {
+
+ us_quic_socket_context_t *context = (us_quic_socket_context_t *) lsquic_conn_get_ctx(lsquic_stream_conn(s));
+
+ context->on_stream_writable((us_quic_stream_t *) s);
+
+ // here we might want to check if the user did write to failure or not, and if the user did not write, stop polling for writable
+ // i think that is what we do for http1
+}
+
+static void on_stream_close (lsquic_stream_t *s, lsquic_stream_ctx_t *h) {
+ //printf("STREAM CLOSED!\n");
+}
+
+#include "openssl/ssl.h"
+
+static char s_alpn[0x100];
+
+int add_alpn (const char *alpn)
+{
+ size_t alpn_len, all_len;
+
+ alpn_len = strlen(alpn);
+ if (alpn_len > 255)
+ return -1;
+
+ all_len = strlen(s_alpn);
+ if (all_len + 1 + alpn_len + 1 > sizeof(s_alpn))
+ return -1;
+
+ s_alpn[all_len] = alpn_len;
+ memcpy(&s_alpn[all_len + 1], alpn, alpn_len);
+ s_alpn[all_len + 1 + alpn_len] = '\0';
+ return 0;
+}
+
+static int select_alpn(SSL *ssl, const unsigned char **out, unsigned char *outlen,
+ const unsigned char *in, unsigned int inlen, void *arg) {
+ int r;
+
+ printf("select_alpn\n");
+
+ r = SSL_select_next_proto((unsigned char **) out, outlen, in, inlen,
+ (unsigned char *) s_alpn, strlen(s_alpn));
+ if (r == OPENSSL_NPN_NEGOTIATED) {
+ printf("OPENSSL_NPN_NEGOTIATED\n");
+ return SSL_TLSEXT_ERR_OK;
+ }
+ else
+ {
+ printf("no supported protocol can be selected!\n");
+ //LSQ_WARN("no supported protocol can be selected from %.*s",
+ //(int) inlen, (char *) in);
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+}
+
+SSL_CTX *old_ctx;
+
+int server_name_cb(SSL *s, int *al, void *arg) {
+ printf("yolo SNI server_name_cb\n");
+
+ SSL_set_SSL_CTX(s, old_ctx);
+
+ printf("existing name is: %s\n", SSL_get_servername(s, TLSEXT_NAMETYPE_host_name));
+
+ if (!SSL_get_servername(s, TLSEXT_NAMETYPE_host_name)) {
+ SSL_set_tlsext_host_name(s, "YOLO NAME!");
+ printf("set name is: %s\n", SSL_get_servername(s, TLSEXT_NAMETYPE_host_name));
+ }
+
+
+ return SSL_TLSEXT_ERR_OK;
+}
+
+// this one is required for servers
+struct ssl_ctx_st *get_ssl_ctx(void *peer_ctx, const struct sockaddr *local) {
+ printf("getting ssl ctx now, peer_ctx: %p\n", peer_ctx);
+
+ // peer_ctx point to the us_udp_socket_t that passed the UDP packet in via
+ // lsquic_engine_packet_in (it got passed as peer_ctx)
+ // we want the per-context ssl cert from this udp socket
+ struct us_udp_socket_t *udp_socket = (struct us_udp_socket_t *) peer_ctx;
+
+ // the udp socket of a server points to the context
+ struct us_quic_socket_context_s *context = us_udp_socket_user(udp_socket);
+
+ if (old_ctx) {
+ return old_ctx;
+ }
+
+ // peer_ctx should be the options struct!
+ us_quic_socket_context_options_t *options = &context->options;
+
+
+ SSL_CTX *ctx = SSL_CTX_new(TLS_method());
+
+ old_ctx = ctx;
+
+ SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION);
+ SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION);
+
+ //SSL_CTX_set_default_verify_paths(ctx);
+
+ // probably cannot use this when http is in use?
+ // alpn is needed
+ SSL_CTX_set_alpn_select_cb(ctx, select_alpn, NULL);
+
+ // sni is needed
+ SSL_CTX_set_tlsext_servername_callback(ctx, server_name_cb);
+ //long SSL_CTX_set_tlsext_servername_arg(SSL_CTX *ctx, void *arg);
+
+ printf("Key: %s\n", options->key_file_name);
+ printf("Cert: %s\n", options->cert_file_name);
+
+ int a = SSL_CTX_use_certificate_chain_file(ctx, options->cert_file_name);
+ int b = SSL_CTX_use_PrivateKey_file(ctx, options->key_file_name, SSL_FILETYPE_PEM);
+
+ printf("loaded cert and key? %d, %d\n", a, b);
+
+ return ctx;
+}
+
+SSL_CTX *sni_lookup(void *lsquic_cert_lookup_ctx, const struct sockaddr *local, const char *sni) {
+ printf("simply returning old ctx in sni\n");
+ return old_ctx;
+}
+
+int log_buf_cb(void *logger_ctx, const char *buf, size_t len) {
+ printf("%.*s\n", (int) len, buf);
+ return 0;
+}
+
+int us_quic_stream_shutdown_read(us_quic_stream_t *s) {
+ lsquic_stream_t *stream = (lsquic_stream_t *) s;
+
+ int ret = lsquic_stream_shutdown((lsquic_stream_t *) s, 0);
+ if (ret != 0) {
+ printf("cannot shutdown stream!\n");
+ exit(0);
+ }
+
+ return 0;
+}
+
+void *us_quic_stream_ext(us_quic_stream_t *s) {
+ return lsquic_stream_get_ctx((lsquic_stream_t *) s);
+}
+
+void us_quic_stream_close(us_quic_stream_t *s) {
+ lsquic_stream_t *stream = (lsquic_stream_t *) s;
+
+ int ret = lsquic_stream_close((lsquic_stream_t *) s);
+ if (ret != 0) {
+ printf("cannot close stream!\n");
+ exit(0);
+ }
+
+ return;
+}
+
+int us_quic_stream_shutdown(us_quic_stream_t *s) {
+ lsquic_stream_t *stream = (lsquic_stream_t *) s;
+
+ int ret = lsquic_stream_shutdown((lsquic_stream_t *) s, 1);
+ if (ret != 0) {
+ printf("cannot shutdown stream!\n");
+ exit(0);
+ }
+
+ return 0;
+}
+
+// header of header set
+struct header_set_hd {
+ int offset;
+};
+
+// let's just store last header set here
+struct header_set_hd *last_hset;
+
+// just a shitty marker for now
+struct processed_header {
+ void *name, *value;
+ int name_length, value_length;
+};
+
+int us_quic_socket_context_get_header(us_quic_socket_context_t *context, int index, char **name, int *name_length, char **value, int *value_length) {
+
+ if (index < last_hset->offset) {
+
+ struct processed_header *pd = (struct processed_header *) (last_hset + 1);
+
+ pd = pd + index;
+
+ *name = pd->name;
+ *value = pd->value;
+ *value_length = pd->value_length;
+ *name_length = pd->name_length;
+
+ return 1;
+ }
+
+ return 0;
+
+}
+
+char pool[1000][4096];
+int pool_top = 0;
+
+void *take() {
+ if (pool_top == 1000) {
+ printf("out of memory\n");
+ exit(0);
+ }
+ return pool[pool_top++];
+}
+
+void leave_all() {
+ pool_top = 0;
+}
+
+
+// header set callbacks
+void *hsi_create_header_set(void *hsi_ctx, lsquic_stream_t *stream, int is_push_promise) {
+
+ //printf("hsi_create_header_set\n");
+
+ void *hset = take();//malloc(1024);
+ memset(hset, 0, sizeof(struct header_set_hd));
+
+ // hsi_ctx is set in engine creation below
+
+ // I guess we just return whatever here, what we return here is gettable via the stream
+
+ // gettable via lsquic_stream_get_hset
+
+ // return user defined header set
+
+ return hset;
+}
+
+void hsi_discard_header_set(void *hdr_set) {
+ // this is pretty much the destructor of above constructor
+
+ printf("hsi_discard_header!\n");
+}
+
+// one header set allocates one 8kb buffer from a linked list of available buffers
+
+
+// 8kb of preallocated heap for headers
+char header_decode_heap[1024 * 8];
+int header_decode_heap_offset = 0;
+
+struct lsxpack_header *hsi_prepare_decode(void *hdr_set, struct lsxpack_header *hdr, size_t space) {
+
+ //printf("hsi_prepare_decode\n");
+
+ if (!hdr) {
+ char *mem = take();
+ hdr = (struct lsxpack_header *) mem;//malloc(sizeof(struct lsxpack_header));
+ memset(hdr, 0, sizeof(struct lsxpack_header));
+ hdr->buf = mem + sizeof(struct lsxpack_header);//take();//malloc(space);
+ lsxpack_header_prepare_decode(hdr, hdr->buf, 0, space);
+ } else {
+
+ if (space > 4096 - sizeof(struct lsxpack_header)) {
+ printf("not hanlded!\n");
+ exit(0);
+ }
+
+ hdr->val_len = space;
+ //hdr->buf = realloc(hdr->buf, space);
+ }
+
+ return hdr;
+}
+
+int hsi_process_header(void *hdr_set, struct lsxpack_header *hdr) {
+
+ // I guess this is the emitting of the header to app space
+
+ //printf("hsi_process_header: %p\n", hdr);
+
+ struct header_set_hd *hd = hdr_set;
+ struct processed_header *proc_hdr = (struct processed_header *) (hd + 1);
+
+ if (!hdr) {
+ //printf("end of headers!\n");
+
+ last_hset = hd;
+
+ // mark end, well we can also just read the offset!
+ //memset(&proc_hdr[hd->offset], 0, sizeof(struct processed_header));
+
+ return 0;
+ }
+
+ /*if (hdr->hpack_index) {
+ printf("header has hpack index: %d\n", hdr->hpack_index);
+ }
+
+ if (hdr->qpack_index) {
+ printf("header has qpack index: %d\n", hdr->qpack_index);
+ }*/
+
+ proc_hdr[hd->offset].value = &hdr->buf[hdr->val_offset];
+ proc_hdr[hd->offset].name = &hdr->buf[hdr->name_offset];
+ proc_hdr[hd->offset].value_length = hdr->val_len;
+ proc_hdr[hd->offset].name_length = hdr->name_len;
+
+ //printf("header %.*s = %.*s\n", hdr->name_len, &hdr->buf[hdr->name_offset], hdr->val_len, &hdr->buf[hdr->val_offset]);
+
+ hd->offset++;
+
+ return 0;
+}
+
+//extern us_quic_socket_context_t *context;
+
+void timer_cb(struct us_timer_t *t) {
+ //printf("Processing conns from timer\n");
+ lsquic_engine_process_conns(global_engine);
+ lsquic_engine_process_conns(global_client_engine);
+
+ // these are handled by this timer, should be polling for udp writable
+ lsquic_engine_send_unsent_packets(global_engine);
+ lsquic_engine_send_unsent_packets(global_client_engine);
+}
+
+// lsquic_conn
+us_quic_socket_context_t *us_quic_socket_context(us_quic_socket_t *s) {
+ return (us_quic_socket_context_t *) lsquic_conn_get_ctx((lsquic_conn_t *) s);
+}
+
+void *us_quic_socket_context_ext(us_quic_socket_context_t *context) {
+ return context + 1;
+}
+
+// this will be for both client and server, but will be only for either h3 or raw quic
+us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_options_t options, int ext_size) {
+
+
+ printf("Creating socket context with ssl: %s\n", options.key_file_name);
+
+ // every _listen_ call creates a new udp socket that feeds inputs to the engine in the context
+ // every context has its own send buffer and udp send socket (not bound to any port or ip?)
+
+ // or just make it so that once you listen, it will listen on that port for input, and the context will use
+ // the first udp socket for output as it doesn't matter which one is used
+
+ /* Holds all callbacks */
+ us_quic_socket_context_t *context = malloc(sizeof(struct us_quic_socket_context_s) + ext_size);
+
+ // the option is put on the socket context
+ context->options = options;
+
+ context->loop = loop;
+ //context->udp_socket = 0;
+
+ /* Allocate per thread, UDP packet buffers */
+ context->recv_buf = us_create_udp_packet_buffer();
+ //context->send_buf = us_create_udp_packet_buffer();
+
+ /* Init lsquic engine */
+ if (0 != lsquic_global_init(LSQUIC_GLOBAL_CLIENT|LSQUIC_GLOBAL_SERVER)) {
+ exit(EXIT_FAILURE);
+ }
+
+ static struct lsquic_stream_if stream_callbacks = {
+ .on_close = on_stream_close,
+ .on_conn_closed = on_conn_closed,
+ .on_write = on_write,
+ .on_read = on_read,
+ .on_new_stream = on_new_stream,
+ .on_new_conn = on_new_conn
+ };
+
+ //memset(&stream_callbacks, 13, sizeof(struct lsquic_stream_if));
+
+ static struct lsquic_hset_if hset_if = {
+ .hsi_discard_header_set = hsi_discard_header_set,
+ .hsi_create_header_set = hsi_create_header_set,
+ .hsi_prepare_decode = hsi_prepare_decode,
+ .hsi_process_header = hsi_process_header
+ };
+
+
+ add_alpn("h3");
+
+ struct lsquic_engine_api engine_api = {
+ .ea_packets_out = send_packets_out,
+ .ea_packets_out_ctx = (void *) context, /* For example */
+ .ea_stream_if = &stream_callbacks,
+ .ea_stream_if_ctx = context,
+
+ .ea_get_ssl_ctx = get_ssl_ctx,
+
+ // lookup certificate
+ .ea_lookup_cert = sni_lookup,
+ .ea_cert_lu_ctx = 0,
+
+ // these are zero anyways
+ .ea_hsi_ctx = 0,
+ .ea_hsi_if = &hset_if,
+ };
+
+ ///printf("log: %d\n", lsquic_set_log_level("debug"));
+
+ static struct lsquic_logger_if logger = {
+ .log_buf = log_buf_cb,
+ };
+
+
+
+ //lsquic_logger_init(&logger, 0, LLTS_NONE);
+
+ /* Create an engine in server mode with HTTP behavior: */
+ context->engine = lsquic_engine_new(LSENG_SERVER | LSENG_HTTP, &engine_api);
+
+ struct lsquic_engine_api engine_api_client = {
+ .ea_packets_out = send_packets_out,
+ .ea_packets_out_ctx = (void *) context, /* For example */
+ .ea_stream_if = &stream_callbacks,
+ .ea_stream_if_ctx = context,
+
+ //.ea_get_ssl_ctx = get_ssl_ctx, // for client?
+
+ // lookup certificate
+ //.ea_lookup_cert = sni_lookup, // for client?
+ //.ea_cert_lu_ctx = 13, // for client?
+
+ // these are zero anyways
+ .ea_hsi_ctx = 0,
+ .ea_hsi_if = &hset_if,
+ };
+
+ context->client_engine = lsquic_engine_new(LSENG_HTTP, &engine_api_client);
+
+ printf("Engine: %p\n", context->engine);
+ printf("Client Engine: %p\n", context->client_engine);
+
+ // start a timer to handle connections
+ struct us_timer_t *delayTimer = us_create_timer(loop, 0, 0);
+ us_timer_set(delayTimer, timer_cb, 50, 50);
+
+ // used by process_quic
+ global_engine = context->engine;
+ global_client_engine = context->client_engine;
+
+ return context;
+}
+
+us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t *context, const char *host, int port, int ext_size) {
+ /* We literally do create a listen socket */
+ return (us_quic_listen_socket_t *) us_create_udp_socket(context->loop, /*context->recv_buf*/ NULL, on_udp_socket_data, on_udp_socket_writable, host, port, context);
+ //return NULL;
+}
+
+/* A client connection is its own UDP socket, while a server connection makes use of the shared listen UDP socket */
+us_quic_socket_t *us_quic_socket_context_connect(us_quic_socket_context_t *context, const char *host, int port, int ext_size) {
+ printf("Connecting..\n");
+
+
+ // localhost 9004 ipv4
+ struct sockaddr_storage storage = {0};
+ // struct sockaddr_in *addr = (struct sockaddr_in *) &storage;
+ // addr->sin_addr.s_addr = 16777343;
+ // addr->sin_port = htons(9004);
+ // addr->sin_family = AF_INET;
+
+ struct sockaddr_in6 *addr = (struct sockaddr_in6 *) &storage;
+ addr->sin6_addr.s6_addr[15] = 1;
+ addr->sin6_port = htons(9004);
+ addr->sin6_family = AF_INET6;
+
+ // Create the UDP socket binding to ephemeral port
+ struct us_udp_socket_t *udp_socket = us_create_udp_socket(context->loop, /*context->recv_buf*/ NULL, on_udp_socket_data_client, on_udp_socket_writable, 0, 0, context);
+
+ // Determine what port we got, creating the local sockaddr
+ int ephemeral = us_udp_socket_bound_port(udp_socket);
+
+ printf("Connecting with udp socket bound to port: %d\n", ephemeral);
+
+ printf("Client udp socket is: %p\n", udp_socket);
+
+
+ // let's call ourselves an ipv6 client and see if that solves anything
+ struct sockaddr_storage local_storage = {0};
+ // struct sockaddr_in *local_addr = (struct sockaddr_in *) &local_storage;
+ // local_addr->sin_addr.s_addr = 16777343;
+ // local_addr->sin_port = htons(ephemeral);
+ // local_addr->sin_family = AF_INET;
+
+ struct sockaddr_in6 *local_addr = (struct sockaddr_in6 *) &local_storage;
+ local_addr->sin6_addr.s6_addr[15] = 1;
+ local_addr->sin6_port = htons(ephemeral);
+ local_addr->sin6_family = AF_INET6;
+
+ // Refer to the UDP socket, and from that, get the context?
+
+ // Create an UDP socket with host-picked port, or well, any port for now
+
+ // we need 1 socket for servers, then we bind multiple ports to that one socket
+
+ void *client = lsquic_engine_connect(context->client_engine, LSQVER_I001, (struct sockaddr *) local_addr, (struct sockaddr *) addr, udp_socket, (lsquic_conn_ctx_t *) udp_socket, "sni", 0, 0, 0, 0, 0);
+
+ printf("Client: %p\n", client);
+
+ // this is requiored to even have packetgs sending out (run this in post)
+ lsquic_engine_process_conns(context->client_engine);
+
+ return client;
+}
+
+#endif
diff --git a/packages/bun-usockets/src/quic.h b/packages/bun-usockets/src/quic.h
new file mode 100644
index 000000000..6d33d27b6
--- /dev/null
+++ b/packages/bun-usockets/src/quic.h
@@ -0,0 +1,68 @@
+#ifdef LIBUS_USE_QUIC
+
+#ifndef LIBUS_QUIC_H
+#define LIBUS_QUIC_H
+
+/* Experimental QUIC functions */
+
+#include "libusockets.h"
+
+typedef struct {
+ const char *cert_file_name;
+ const char *key_file_name;
+ const char *passphrase;
+} us_quic_socket_context_options_t;
+
+
+typedef struct {
+ /* Refers to either the shared listen socket or the client UDP socket */
+ void *udp_socket;
+} us_quic_socket_t;
+
+struct us_quic_socket_context_s;
+struct us_quic_listen_socket_s;
+struct us_quic_stream_s;
+
+typedef struct us_quic_socket_context_s us_quic_socket_context_t;
+typedef struct us_quic_listen_socket_s us_quic_listen_socket_t;
+typedef struct us_quic_stream_s us_quic_stream_t;
+
+
+void *us_quic_stream_ext(us_quic_stream_t *s);
+int us_quic_stream_write(us_quic_stream_t *s, char *data, int length);
+int us_quic_stream_shutdown(us_quic_stream_t *s);
+int us_quic_stream_shutdown_read(us_quic_stream_t *s);
+void us_quic_stream_close(us_quic_stream_t *s);
+
+int us_quic_socket_context_get_header(us_quic_socket_context_t *context, int index, char **name, int *name_length, char **value, int *value_length);
+
+
+void us_quic_socket_context_set_header(us_quic_socket_context_t *context, int index, const char *key, int key_length, const char *value, int value_length);
+void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_quic_stream_t *s, int num, int has_body);
+
+us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_options_t options, int ext_size);
+us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t *context, const char *host, int port, int ext_size);
+us_quic_socket_t *us_quic_socket_context_connect(us_quic_socket_context_t *context, const char *host, int port, int ext_size);
+
+void us_quic_socket_create_stream(us_quic_socket_t *s, int ext_size);
+us_quic_socket_t *us_quic_stream_socket(us_quic_stream_t *s);
+
+/* This one is ugly and is only used to make clean examples */
+int us_quic_stream_is_client(us_quic_stream_t *s);
+
+void us_quic_socket_context_on_stream_data(us_quic_socket_context_t *context, void(*on_stream_data)(us_quic_stream_t *s, char *data, int length));
+void us_quic_socket_context_on_stream_end(us_quic_socket_context_t *context, void(*on_stream_data)(us_quic_stream_t *s));
+void us_quic_socket_context_on_stream_headers(us_quic_socket_context_t *context, void(*on_stream_headers)(us_quic_stream_t *s));
+void us_quic_socket_context_on_stream_open(us_quic_socket_context_t *context, void(*on_stream_open)(us_quic_stream_t *s, int is_client));
+void us_quic_socket_context_on_stream_close(us_quic_socket_context_t *context, void(*on_stream_close)(us_quic_stream_t *s));
+void us_quic_socket_context_on_open(us_quic_socket_context_t *context, void(*on_open)(us_quic_socket_t *s, int is_client));
+void us_quic_socket_context_on_close(us_quic_socket_context_t *context, void(*on_close)(us_quic_socket_t *s));
+void us_quic_socket_context_on_stream_writable(us_quic_socket_context_t *context, void(*on_stream_writable)(us_quic_stream_t *s));
+
+
+
+void *us_quic_socket_context_ext(us_quic_socket_context_t *context);
+us_quic_socket_context_t *us_quic_socket_context(us_quic_socket_t *s);
+
+#endif
+#endif \ No newline at end of file
diff --git a/packages/bun-usockets/src/socket.c b/packages/bun-usockets/src/socket.c
new file mode 100644
index 000000000..61cbb2c0f
--- /dev/null
+++ b/packages/bun-usockets/src/socket.c
@@ -0,0 +1,295 @@
+/*
+ * Authored by Alex Hultman, 2018-2021.
+ * Intellectual property of third-party.
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libusockets.h"
+#include "internal/internal.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+/* Shared with SSL */
+
+int us_socket_local_port(int ssl, struct us_socket_t *s) {
+ struct bsd_addr_t addr;
+ if (bsd_local_addr(us_poll_fd(&s->p), &addr)) {
+ return -1;
+ } else {
+ return bsd_addr_get_port(&addr);
+ }
+}
+
+void us_socket_shutdown_read(int ssl, struct us_socket_t *s) {
+ /* This syscall is idempotent so no extra check is needed */
+ bsd_shutdown_socket_read(us_poll_fd((struct us_poll_t *) s));
+}
+
+void us_socket_remote_address(int ssl, struct us_socket_t *s, char *buf, int *length) {
+ struct bsd_addr_t addr;
+ if (bsd_remote_addr(us_poll_fd(&s->p), &addr) || *length < bsd_addr_get_ip_length(&addr)) {
+ *length = 0;
+ } else {
+ *length = bsd_addr_get_ip_length(&addr);
+ memcpy(buf, bsd_addr_get_ip(&addr), *length);
+ }
+}
+
+struct us_socket_context_t *us_socket_context(int ssl, struct us_socket_t *s) {
+ return s->context;
+}
+
+void us_socket_timeout(int ssl, struct us_socket_t *s, unsigned int seconds) {
+ if (seconds) {
+ s->timeout = ((unsigned int)s->context->timestamp + ((seconds + 3) >> 2)) % 240;
+ } else {
+ s->timeout = 255;
+ }
+}
+
+void us_socket_long_timeout(int ssl, struct us_socket_t *s, unsigned int minutes) {
+ if (minutes) {
+ s->long_timeout = ((unsigned int)s->context->long_timestamp + minutes) % 240;
+ } else {
+ s->long_timeout = 255;
+ }
+}
+
+void us_socket_flush(int ssl, struct us_socket_t *s) {
+ if (!us_socket_is_shut_down(0, s)) {
+ bsd_socket_flush(us_poll_fd((struct us_poll_t *) s));
+ }
+}
+
+int us_socket_is_closed(int ssl, struct us_socket_t *s) {
+ return s->prev == (struct us_socket_t *) s->context;
+}
+
+int us_socket_is_established(int ssl, struct us_socket_t *s) {
+ /* Everything that is not POLL_TYPE_SEMI_SOCKET is established */
+ return us_internal_poll_type((struct us_poll_t *) s) != POLL_TYPE_SEMI_SOCKET;
+}
+
+/* Exactly the same as us_socket_close but does not emit on_close event */
+struct us_socket_t *us_socket_close_connecting(int ssl, struct us_socket_t *s) {
+ if (!us_socket_is_closed(0, s)) {
+ us_internal_socket_context_unlink_socket(s->context, s);
+ us_poll_stop((struct us_poll_t *) s, s->context->loop);
+ bsd_close_socket(us_poll_fd((struct us_poll_t *) s));
+
+ /* Link this socket to the close-list and let it be deleted after this iteration */
+ s->next = s->context->loop->data.closed_head;
+ s->context->loop->data.closed_head = s;
+
+ /* Any socket with prev = context is marked as closed */
+ s->prev = (struct us_socket_t *) s->context;
+
+ //return s->context->on_close(s, code, reason);
+ }
+ return s;
+}
+
+/* Same as above but emits on_close */
+struct us_socket_t *us_socket_close(int ssl, struct us_socket_t *s, int code, void *reason) {
+ if (!us_socket_is_closed(0, s)) {
+ if (s->low_prio_state == 1) {
+ /* Unlink this socket from the low-priority queue */
+ if (!s->prev) s->context->loop->data.low_prio_head = s->next;
+ else s->prev->next = s->next;
+
+ if (s->next) s->next->prev = s->prev;
+
+ s->prev = 0;
+ s->next = 0;
+ s->low_prio_state = 0;
+ } else {
+ us_internal_socket_context_unlink_socket(s->context, s);
+ }
+ #ifdef LIBUS_USE_KQUEUE
+ // kqueue automatically removes the fd from the set on close
+ // we can skip the system call for that case
+ us_internal_loop_update_pending_ready_polls(s->context->loop, (struct us_poll_t *)s, 0, us_poll_events((struct us_poll_t*)s), 0);
+ #else
+ /* Disable any instance of us in the pending ready poll list */
+ us_poll_stop((struct us_poll_t *) s, s->context->loop);
+ #endif
+ bsd_close_socket(us_poll_fd((struct us_poll_t *) s));
+
+ /* Link this socket to the close-list and let it be deleted after this iteration */
+ s->next = s->context->loop->data.closed_head;
+ s->context->loop->data.closed_head = s;
+
+ /* Any socket with prev = context is marked as closed */
+ s->prev = (struct us_socket_t *) s->context;
+
+ return s->context->on_close(s, code, reason);
+ }
+ return s;
+}
+
+// This function is the same as us_socket_close but:
+// - does not emit on_close event
+// - does not close
+struct us_socket_t *us_socket_detach(int ssl, struct us_socket_t *s) {
+ if (!us_socket_is_closed(0, s)) {
+ if (s->low_prio_state == 1) {
+ /* Unlink this socket from the low-priority queue */
+ if (!s->prev) s->context->loop->data.low_prio_head = s->next;
+ else s->prev->next = s->next;
+
+ if (s->next) s->next->prev = s->prev;
+
+ s->prev = 0;
+ s->next = 0;
+ s->low_prio_state = 0;
+ } else {
+ us_internal_socket_context_unlink(s->context, s);
+ }
+ us_poll_stop((struct us_poll_t *) s, s->context->loop);
+
+ /* Link this socket to the close-list and let it be deleted after this iteration */
+ s->next = s->context->loop->data.closed_head;
+ s->context->loop->data.closed_head = s;
+
+ /* Any socket with prev = context is marked as closed */
+ s->prev = (struct us_socket_t *) s->context;
+
+ return s;
+ }
+ return s;
+}
+
+// This function is used for moving a socket between two different event loops
+struct us_socket_t *us_socket_attach(int ssl, LIBUS_SOCKET_DESCRIPTOR client_fd, struct us_socket_context_t *ctx, int flags, int socket_ext_size) {
+ struct us_poll_t *accepted_p = us_create_poll(ctx->loop, 0, sizeof(struct us_socket_t) - sizeof(struct us_poll_t) + socket_ext_size);
+ us_poll_init(accepted_p, client_fd, POLL_TYPE_SOCKET);
+ us_poll_start(accepted_p, ctx->loop, flags);
+
+ struct us_socket_t *s = (struct us_socket_t *) accepted_p;
+
+ s->context = ctx;
+ s->timeout = 0;
+ s->low_prio_state = 0;
+
+ /* We always use nodelay */
+ bsd_socket_nodelay(client_fd, 1);
+ us_internal_socket_context_link(ctx, s);
+
+ if (ctx->on_open) ctx->on_open(s, 0, 0, 0);
+
+ return s;
+}
+
+/* Not shared with SSL */
+
+void *us_socket_get_native_handle(int ssl, struct us_socket_t *s) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ return us_internal_ssl_socket_get_native_handle((struct us_internal_ssl_socket_t *) s);
+ }
+#endif
+
+ return (void *) (uintptr_t) us_poll_fd((struct us_poll_t *) s);
+}
+
+int us_socket_write(int ssl, struct us_socket_t *s, const char *data, int length, int msg_more) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ return us_internal_ssl_socket_write((struct us_internal_ssl_socket_t *) s, data, length, msg_more);
+ }
+#endif
+
+ if (us_socket_is_closed(ssl, s) || us_socket_is_shut_down(ssl, s)) {
+ return 0;
+ }
+
+ int written = bsd_send(us_poll_fd(&s->p), data, length, msg_more);
+ if (written != length) {
+ s->context->loop->data.last_write_failed = 1;
+ us_poll_change(&s->p, s->context->loop, LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE);
+ }
+
+ return written < 0 ? 0 : written;
+}
+
+void *us_socket_ext(int ssl, struct us_socket_t *s) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ return us_internal_ssl_socket_ext((struct us_internal_ssl_socket_t *) s);
+ }
+#endif
+
+ return s + 1;
+}
+
+int us_socket_is_shut_down(int ssl, struct us_socket_t *s) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ return us_internal_ssl_socket_is_shut_down((struct us_internal_ssl_socket_t *) s);
+ }
+#endif
+
+ return us_internal_poll_type(&s->p) == POLL_TYPE_SOCKET_SHUT_DOWN;
+}
+
+void us_socket_shutdown(int ssl, struct us_socket_t *s) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ us_internal_ssl_socket_shutdown((struct us_internal_ssl_socket_t *) s);
+ return;
+ }
+#endif
+
+ /* Todo: should we emit on_close if calling shutdown on an already half-closed socket?
+ * We need more states in that case, we need to track RECEIVED_FIN
+ * so far, the app has to track this and call close as needed */
+ if (!us_socket_is_closed(ssl, s) && !us_socket_is_shut_down(ssl, s)) {
+ us_internal_poll_set_type(&s->p, POLL_TYPE_SOCKET_SHUT_DOWN);
+ us_poll_change(&s->p, s->context->loop, us_poll_events(&s->p) & LIBUS_SOCKET_READABLE);
+ bsd_shutdown_socket(us_poll_fd((struct us_poll_t *) s));
+ }
+}
+
+/*
+ Note: this assumes that the socket is non-TLS and will be adopted and wrapped with a new TLS context
+ context ext will not be copied to the new context, new context will contain us_wrapped_socket_context_t on ext
+*/
+struct us_socket_t *us_socket_wrap_with_tls(int ssl, struct us_socket_t *s, struct us_bun_socket_context_options_t options, struct us_socket_events_t events, int socket_ext_size) {
+ // only accepts non-TLS sockets
+ if (ssl) {
+ return NULL;
+ }
+
+ return(struct us_socket_t *) us_internal_ssl_socket_wrap_with_tls(s, options, events, socket_ext_size);
+}
+
+// if a TLS socket calls this, it will start SSL call open event and TLS handshake if required
+// will have no effect if the socket is closed or is not TLS
+struct us_socket_t* us_socket_open(int ssl, struct us_socket_t * s, int is_client, char* ip, int ip_length) {
+ if (ssl) {
+ return(struct us_socket_t *) us_internal_ssl_socket_open((struct us_internal_ssl_socket_t *)s, is_client, ip, ip_length);
+ }
+ return s;
+}
+
+int us_socket_raw_write(int ssl, struct us_socket_t *s, const char *data, int length, int msg_more) {
+#ifndef LIBUS_NO_SSL
+ if (ssl) {
+ return us_internal_ssl_socket_raw_write((struct us_internal_ssl_socket_t *) s, data, length, msg_more);
+ }
+#endif
+ // non-TLS is always raw
+ return us_socket_write(ssl, s, data, length, msg_more);
+}
diff --git a/packages/bun-usockets/src/udp.c b/packages/bun-usockets/src/udp.c
new file mode 100644
index 000000000..70a7d3a00
--- /dev/null
+++ b/packages/bun-usockets/src/udp.c
@@ -0,0 +1,147 @@
+/*
+ * Authored by Alex Hultman, 2018-2021.
+ * Intellectual property of third-party.
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libusockets.h"
+#include "internal/internal.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int us_udp_packet_buffer_ecn(struct us_udp_packet_buffer_t *buf, int index) {
+ return bsd_udp_packet_buffer_ecn(buf, index);
+}
+
+int us_udp_packet_buffer_local_ip(struct us_udp_packet_buffer_t *buf, int index, char *ip) {
+ return bsd_udp_packet_buffer_local_ip(buf, index, ip);
+}
+
+char *us_udp_packet_buffer_peer(struct us_udp_packet_buffer_t *buf, int index) {
+ return bsd_udp_packet_buffer_peer(buf, index);
+}
+
+char *us_udp_packet_buffer_payload(struct us_udp_packet_buffer_t *buf, int index) {
+ return bsd_udp_packet_buffer_payload(buf, index);
+}
+
+int us_udp_packet_buffer_payload_length(struct us_udp_packet_buffer_t *buf, int index) {
+ return bsd_udp_packet_buffer_payload_length(buf, index);
+}
+
+// what should we return? number of sent datagrams?
+int us_udp_socket_send(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int num) {
+ int fd = us_poll_fd((struct us_poll_t *) s);
+
+ // we need to poll out if we failed
+
+ return bsd_sendmmsg(fd, buf, num, 0);
+}
+
+int us_udp_socket_receive(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf) {
+ int fd = us_poll_fd((struct us_poll_t *) s);
+ return bsd_recvmmsg(fd, buf, LIBUS_UDP_MAX_NUM, 0, 0);
+}
+
+void us_udp_buffer_set_packet_payload(struct us_udp_packet_buffer_t *send_buf, int index, int offset, void *payload, int length, void *peer_addr) {
+ bsd_udp_buffer_set_packet_payload(send_buf, index, offset, payload, length, peer_addr);
+}
+
+struct us_udp_packet_buffer_t *us_create_udp_packet_buffer() {
+ return (struct us_udp_packet_buffer_t *) bsd_create_udp_packet_buffer();
+}
+
+struct us_internal_udp_t {
+ struct us_internal_callback_t cb;
+ struct us_udp_packet_buffer_t *receive_buf;
+ void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int);
+ void (*drain_cb)(struct us_udp_socket_t *);
+ void *user;
+ /* An UDP socket can only ever be bound to one single port regardless of how
+ * many interfaces it may listen to. Therefore we cache the port after creation
+ * and use it to build a proper and full sockaddr_in or sockaddr_in6 for every received packet */
+ int port;
+};
+
+int us_udp_socket_bound_port(struct us_udp_socket_t *s) {
+ return ((struct us_internal_udp_t *) s)->port;
+}
+
+/* Internal wrapper, move from here */
+void internal_on_udp_read(struct us_udp_socket_t *s) {
+
+ // lookup receive buffer and callback here
+ struct us_internal_udp_t *udp = (struct us_internal_udp_t *) s;
+
+ int packets = us_udp_socket_receive(s, udp->receive_buf);
+ //printf("Packets: %d\n", packets);
+
+ // we need to get the socket data and lookup its callback here
+
+
+ udp->data_cb(s, udp->receive_buf, packets);
+}
+
+void *us_udp_socket_user(struct us_udp_socket_t *s) {
+ struct us_internal_udp_t *udp = (struct us_internal_udp_t *) s;
+
+ return udp->user;
+}
+
+struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, struct us_udp_packet_buffer_t *buf, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), const char *host, unsigned short port, void *user) {
+
+ LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_udp_socket(host, port);
+ if (fd == LIBUS_SOCKET_ERROR) {
+ return 0;
+ }
+
+ /* If buf is 0 then create one here */
+ if (!buf) {
+ buf = us_create_udp_packet_buffer();
+ }
+
+ int ext_size = 0;
+ int fallthrough = 0;
+
+ struct us_poll_t *p = us_create_poll(loop, fallthrough, sizeof(struct us_internal_udp_t) + ext_size);
+ us_poll_init(p, fd, POLL_TYPE_CALLBACK);
+
+ struct us_internal_udp_t *cb = (struct us_internal_udp_t *) p;
+ cb->cb.loop = loop;
+ cb->cb.cb_expects_the_loop = 0;
+ cb->cb.leave_poll_ready = 1;
+
+ /* Get and store the port once */
+ struct bsd_addr_t tmp;
+ bsd_local_addr(fd, &tmp);
+ cb->port = bsd_addr_get_port(&tmp);
+
+ printf("The port of UDP is: %d\n", cb->port);
+
+ /* There is no udp socket context, only user data */
+ /* This should really be ext like everything else */
+ cb->user = user;
+
+ cb->data_cb = data_cb;
+ cb->receive_buf = buf;
+ cb->drain_cb = drain_cb;
+
+ cb->cb.cb = (void (*)(struct us_internal_callback_t *)) internal_on_udp_read;
+
+ us_poll_start((struct us_poll_t *) cb, cb->cb.loop, LIBUS_SOCKET_READABLE);
+
+ return (struct us_udp_socket_t *) cb;
+} \ No newline at end of file