Add UUID v7 generation to Redbean

This commit is contained in:
terror 2024-06-11 19:29:00 +12:00
parent 01267ea0f5
commit bc74d79c89
5 changed files with 308 additions and 143 deletions

View file

@ -1981,6 +1981,10 @@ function Underlong(str) end
--- @return string --- @return string
function UuidV4() end function UuidV4() end
--- Generate a uuid_v7
--- @return string
function UuidV7() end
---@param x integer ---@param x integer
---@return integer # position of first bit set. ---@return integer # position of first bit set.
--- Passing `0` will raise an error. Same as the Intel x86 instruction BSF. --- Passing `0` will raise an error. Same as the Intel x86 instruction BSF.

View file

@ -1065,6 +1065,9 @@ FUNCTIONS
UuidV4() -> str UuidV4() -> str
Returns an uuid v4 string. Returns an uuid v4 string.
UuidV7() -> str
Returns an uuid v7 string.
Fetch(url:str[,body:str|{method=value:str,body=value:str,headers=table,...}]) Fetch(url:str[,body:str|{method=value:str,body=value:str,headers=table,...}])
├─→ status:int, {header:str=value:str,...}, body:str ├─→ status:int, {header:str=value:str,...}, body:str
└─→ nil, error:str └─→ nil, error:str
@ -1612,7 +1615,7 @@ FUNCTIONS
called from `.init.lua`. This function is not available in called from `.init.lua`. This function is not available in
unsecure mode. unsecure mode.
ProgramSslRequired(mandatory:str) ProgramSslRequired(mandatory:bool)
Enables the blocking of HTTP so that all inbound clients and Enables the blocking of HTTP so that all inbound clients and
must use the TLS transport layer. This has the same effect as must use the TLS transport layer. This has the same effect as
the `-J` flag. Fetch() is still allowed to make outbound HTTP the `-J` flag. Fetch() is still allowed to make outbound HTTP

View file

@ -52,7 +52,7 @@
#include "libc/sysv/consts/rusage.h" #include "libc/sysv/consts/rusage.h"
#include "libc/sysv/consts/sock.h" #include "libc/sysv/consts/sock.h"
#include "libc/thread/thread.h" #include "libc/thread/thread.h"
#include "libc/time/time.h" #include "libc/time.h"
#include "libc/x/x.h" #include "libc/x/x.h"
#include "net/http/escape.h" #include "net/http/escape.h"
#include "net/http/http.h" #include "net/http/http.h"
@ -475,7 +475,8 @@ int LuaSlurp(lua_State *L) {
} }
if (rc != -1) { if (rc != -1) {
got = rc; got = rc;
if (!got) break; if (!got)
break;
luaL_addlstring(&b, tb, got); luaL_addlstring(&b, tb, got);
} else if (errno == EINTR) { } else if (errno == EINTR) {
errno = olderr; errno = olderr;
@ -617,7 +618,8 @@ dontinline int LuaBase32Impl(lua_State *L,
const char *a = luaL_optlstring(L, 2, "", &al); const char *a = luaL_optlstring(L, 2, "", &al);
if (!IS2POW(al) || al > 128 || al == 1) if (!IS2POW(al) || al > 128 || al == 1)
return luaL_error(L, "alphabet length is not a power of 2 in range 2..128"); return luaL_error(L, "alphabet length is not a power of 2 in range 2..128");
if (!(p = B32(s, sl, a, al, &sl))) return luaL_error(L, "out of memory"); if (!(p = B32(s, sl, a, al, &sl)))
return luaL_error(L, "out of memory");
lua_pushlstring(L, p, sl); lua_pushlstring(L, p, sl);
free(p); free(p);
return 1; return 1;
@ -693,10 +695,12 @@ int LuaGetCryptoHash(lua_State *L) {
const void *p = luaL_checklstring(L, 2, &pl); const void *p = luaL_checklstring(L, 2, &pl);
const void *k = luaL_optlstring(L, 3, "", &kl); const void *k = luaL_optlstring(L, 3, "", &kl);
const mbedtls_md_info_t *digest = mbedtls_md_info_from_string(h); const mbedtls_md_info_t *digest = mbedtls_md_info_from_string(h);
if (!digest) return luaL_argerror(L, 1, "unknown hash type"); if (!digest)
return luaL_argerror(L, 1, "unknown hash type");
if (kl == 0) { if (kl == 0) {
// no key provided, run generic hash function // no key provided, run generic hash function
if ((digest->f_md)(p, pl, d)) return luaL_error(L, "bad input data"); if ((digest->f_md)(p, pl, d))
return luaL_error(L, "bad input data");
} else if (mbedtls_md_hmac(digest, k, kl, p, pl, d)) { } else if (mbedtls_md_hmac(digest, k, kl, p, pl, d)) {
return luaL_error(L, "bad input data"); return luaL_error(L, "bad input data");
} }
@ -854,6 +858,22 @@ int LuaUuidV4(lua_State *L) {
return 1; return 1;
} }
int LuaUuidV7(lua_State *L) {
char uuid_str[37] = {0};
struct timespec now = timespec_real();
uint64_t time_now = timespec_tonanos(now);
uint64_t random_data = _rand64();
snprintf(uuid_str, sizeof(uuid_str), "%08x-%04x-%04x-%04x-%012llx",
(uint32_t)(time_now >> 32), //8
(uint16_t)(time_now >> 16), //4
(uint16_t)((0x7 << 12) | (time_now >> 4 & 0x0fff)), //4
(uint16_t)((0b10 << 14 | ((time_now & 0x000f) << 10)) | (random_data & 0x03FF)), //4
(uint64_t)(random_data >> 4 & 0xFFFFFFFFFFFF) //12
);
lua_pushfstring(L, uuid_str);
return 1;
}
static dontinline int LuaHasherImpl(lua_State *L, size_t k, static dontinline int LuaHasherImpl(lua_State *L, size_t k,
int H(const void *, size_t, uint8_t *)) { int H(const void *, size_t, uint8_t *)) {
size_t n; size_t n;

View file

@ -91,6 +91,7 @@ int LuaSlurp(lua_State *);
int LuaUncompress(lua_State *); int LuaUncompress(lua_State *);
int LuaUnderlong(lua_State *); int LuaUnderlong(lua_State *);
int LuaUuidV4(lua_State *); int LuaUuidV4(lua_State *);
int LuaUuidV7(lua_State *);
int LuaVisualizeControlCodes(lua_State *); int LuaVisualizeControlCodes(lua_State *);
void LuaPushUrlView(lua_State *, struct UrlView *); void LuaPushUrlView(lua_State *, struct UrlView *);

View file

@ -644,11 +644,14 @@ static bool ShouldAvoidGzip(void) {
static char *MergePaths(const char *p, size_t n, const char *q, size_t m, static char *MergePaths(const char *p, size_t n, const char *q, size_t m,
size_t *z) { size_t *z) {
char *r; char *r;
if (n && p[n - 1] == '/') --n; if (n && p[n - 1] == '/')
if (m && q[0] == '/') ++q, --m; --n;
if (m && q[0] == '/')
++q, --m;
r = xmalloc(n + 1 + m + 1); r = xmalloc(n + 1 + m + 1);
mempcpy(mempcpy(mempcpy(mempcpy(r, p, n), "/", 1), q, m), "", 1); mempcpy(mempcpy(mempcpy(mempcpy(r, p, n), "/", 1), q, m), "", 1);
if (z) *z = n + 1 + m; if (z)
*z = n + 1 + m;
return r; return r;
} }
@ -698,7 +701,8 @@ static void AppendCert(mbedtls_x509_crt *cert, mbedtls_pk_context *key) {
static void InternCertificate(mbedtls_x509_crt *cert, mbedtls_x509_crt *prev) { static void InternCertificate(mbedtls_x509_crt *cert, mbedtls_x509_crt *prev) {
int r; int r;
size_t i; size_t i;
if (cert->next) InternCertificate(cert->next, cert); if (cert->next)
InternCertificate(cert->next, cert);
if (prev) { if (prev) {
if (mbedtls_x509_crt_check_parent(prev, cert, 1)) { if (mbedtls_x509_crt_check_parent(prev, cert, 1)) {
DEBUGF("(ssl) unbundling %`'s from %`'s", DEBUGF("(ssl) unbundling %`'s from %`'s",
@ -728,18 +732,22 @@ static void InternCertificate(mbedtls_x509_crt *cert, mbedtls_x509_crt *prev) {
LogCertificate("loaded certificate", cert); LogCertificate("loaded certificate", cert);
if (!cert->next && !IsSelfSigned(cert) && cert->max_pathlen) { if (!cert->next && !IsSelfSigned(cert) && cert->max_pathlen) {
for (i = 0; i < certs.n; ++i) { for (i = 0; i < certs.n; ++i) {
if (!certs.p[i].cert) continue; if (!certs.p[i].cert)
continue;
if (mbedtls_pk_can_do(&cert->pk, certs.p[i].cert->sig_pk) && if (mbedtls_pk_can_do(&cert->pk, certs.p[i].cert->sig_pk) &&
!mbedtls_x509_crt_check_parent(cert, certs.p[i].cert, 1) && !mbedtls_x509_crt_check_parent(cert, certs.p[i].cert, 1) &&
!IsSelfSigned(certs.p[i].cert)) { !IsSelfSigned(certs.p[i].cert)) {
if (ChainCertificate(cert, certs.p[i].cert)) break; if (ChainCertificate(cert, certs.p[i].cert))
break;
} }
} }
} }
if (!IsSelfSigned(cert)) { if (!IsSelfSigned(cert)) {
for (i = 0; i < certs.n; ++i) { for (i = 0; i < certs.n; ++i) {
if (!certs.p[i].cert) continue; if (!certs.p[i].cert)
if (certs.p[i].cert->next) continue; continue;
if (certs.p[i].cert->next)
continue;
if (certs.p[i].cert->max_pathlen && if (certs.p[i].cert->max_pathlen &&
mbedtls_pk_can_do(&certs.p[i].cert->pk, cert->sig_pk) && mbedtls_pk_can_do(&certs.p[i].cert->pk, cert->sig_pk) &&
!mbedtls_x509_crt_check_parent(certs.p[i].cert, cert, 1)) { !mbedtls_x509_crt_check_parent(certs.p[i].cert, cert, 1)) {
@ -782,7 +790,8 @@ static void ProgramPrivateKey(const char *p, size_t n) {
rc = mbedtls_pk_parse_key(key, waqapi, n + 1, 0, 0); rc = mbedtls_pk_parse_key(key, waqapi, n + 1, 0, 0);
mbedtls_platform_zeroize(waqapi, n); mbedtls_platform_zeroize(waqapi, n);
free(waqapi); free(waqapi);
if (rc != 0) FATALF("(ssl) error: load key (grep -0x%04x)", -rc); if (rc != 0)
FATALF("(ssl) error: load key (grep -0x%04x)", -rc);
for (i = 0; i < certs.n; ++i) { for (i = 0; i < certs.n; ++i) {
if (certs.p[i].cert && !certs.p[i].key && if (certs.p[i].cert && !certs.p[i].key &&
!mbedtls_pk_check_pair(&certs.p[i].cert->pk, key)) { !mbedtls_pk_check_pair(&certs.p[i].cert->pk, key)) {
@ -811,7 +820,8 @@ static void ProgramPort(long port) {
if (!(0 <= port && port <= 65535)) { if (!(0 <= port && port <= 65535)) {
FATALF("(cfg) error: bad port: %d", port); FATALF("(cfg) error: bad port: %d", port);
} }
if (port == 443) listeningonport443 = true; if (port == 443)
listeningonport443 = true;
ports.p = realloc(ports.p, ++ports.n * sizeof(*ports.p)); ports.p = realloc(ports.p, ++ports.n * sizeof(*ports.p));
ports.p[ports.n - 1] = port; ports.p[ports.n - 1] = port;
} }
@ -942,13 +952,15 @@ static void DescribeAddress(char buf[40], uint32_t addr, uint16_t port) {
static inline int GetServerAddr(uint32_t *ip, uint16_t *port) { static inline int GetServerAddr(uint32_t *ip, uint16_t *port) {
*ip = ntohl(serveraddr->sin_addr.s_addr); *ip = ntohl(serveraddr->sin_addr.s_addr);
if (port) *port = ntohs(serveraddr->sin_port); if (port)
*port = ntohs(serveraddr->sin_port);
return 0; return 0;
} }
static inline int GetClientAddr(uint32_t *ip, uint16_t *port) { static inline int GetClientAddr(uint32_t *ip, uint16_t *port) {
*ip = ntohl(clientaddr.sin_addr.s_addr); *ip = ntohl(clientaddr.sin_addr.s_addr);
if (port) *port = ntohs(clientaddr.sin_port); if (port)
*port = ntohs(clientaddr.sin_port);
return 0; return 0;
} }
@ -1038,7 +1050,8 @@ static void ProgramTimeout(long ms) {
static void ProgramCache(long x, const char *s) { static void ProgramCache(long x, const char *s) {
cacheseconds = x; cacheseconds = x;
if (s) cachedirective = strdup(s); if (s)
cachedirective = strdup(s);
} }
static void SetDefaults(void) { static void SetDefaults(void) {
@ -1183,9 +1196,11 @@ static void ChangeUser(void) {
} }
static void Daemonize(void) { static void Daemonize(void) {
if (fork() > 0) exit(0); if (fork() > 0)
exit(0);
setsid(); setsid();
if (fork() > 0) _exit(0); if (fork() > 0)
_exit(0);
umask(0); umask(0);
} }
@ -1209,7 +1224,8 @@ static void LuaEvalCode(const char *code) {
// handle `-F PATH` arg // handle `-F PATH` arg
static void LuaEvalFile(const char *path) { static void LuaEvalFile(const char *path) {
char *f = gc(xslurp(path, 0)); char *f = gc(xslurp(path, 0));
if (!f) FATALF("(cfg) error: failed to read file %`'s", path); if (!f)
FATALF("(cfg) error: failed to read file %`'s", path);
LuaEvalCode(f); LuaEvalCode(f);
} }
@ -1465,8 +1481,10 @@ static ssize_t WritevAll(int fd, struct iovec *iov, int iovlen) {
total = 0; total = 0;
do { do {
if (i) { if (i) {
while (i < iovlen && !iov[i].iov_len) ++i; while (i < iovlen && !iov[i].iov_len)
if (i == iovlen) break; ++i;
if (i == iovlen)
break;
} }
if ((rc = writev(fd, iov + i, iovlen - i)) != -1) { if ((rc = writev(fd, iov + i, iovlen - i)) != -1) {
wrote = rc; wrote = rc;
@ -1501,7 +1519,8 @@ static int TlsFlush(struct TlsBio *bio, const unsigned char *buf, size_t len) {
v[1].iov_base = (void *)buf; v[1].iov_base = (void *)buf;
v[1].iov_len = len; v[1].iov_len = len;
if (WritevAll(bio->fd, v, 2) != -1) { if (WritevAll(bio->fd, v, 2) != -1) {
if (bio->c > 0) bio->c = 0; if (bio->c > 0)
bio->c = 0;
} else if (errno == EINTR) { } else if (errno == EINTR) {
errno = 0; errno = 0;
return MBEDTLS_ERR_NET_CONN_RESET; return MBEDTLS_ERR_NET_CONN_RESET;
@ -1526,7 +1545,8 @@ static int TlsSend(void *ctx, const unsigned char *buf, size_t len) {
bio->c += len; bio->c += len;
return len; return len;
} }
if ((rc = TlsFlush(bio, buf, len)) < 0) return rc; if ((rc = TlsFlush(bio, buf, len)) < 0)
return rc;
return len; return len;
} }
@ -1534,11 +1554,13 @@ static int TlsRecvImpl(void *ctx, unsigned char *p, size_t n, uint32_t o) {
int r; int r;
struct iovec v[2]; struct iovec v[2];
struct TlsBio *bio = ctx; struct TlsBio *bio = ctx;
if ((r = TlsFlush(bio, 0, 0)) < 0) return r; if ((r = TlsFlush(bio, 0, 0)) < 0)
return r;
if (bio->a < bio->b) { if (bio->a < bio->b) {
r = MIN(n, bio->b - bio->a); r = MIN(n, bio->b - bio->a);
memcpy(p, bio->t + bio->a, r); memcpy(p, bio->t + bio->a, r);
if ((bio->a += r) == bio->b) bio->a = bio->b = 0; if ((bio->a += r) == bio->b)
bio->a = bio->b = 0;
return r; return r;
} }
v[0].iov_base = p; v[0].iov_base = p;
@ -1559,7 +1581,8 @@ static int TlsRecvImpl(void *ctx, unsigned char *p, size_t n, uint32_t o) {
return MBEDTLS_ERR_NET_RECV_FAILED; return MBEDTLS_ERR_NET_RECV_FAILED;
} }
} }
if (r > n) bio->b = r - n; if (r > n)
bio->b = r - n;
return MIN(n, r); return MIN(n, r);
} }
@ -1655,11 +1678,15 @@ static void NotifyClose(void) {
static void WipeSigningKeys(void) { static void WipeSigningKeys(void) {
size_t i; size_t i;
if (uniprocess) return; if (uniprocess)
return;
for (i = 0; i < certs.n; ++i) { for (i = 0; i < certs.n; ++i) {
if (!certs.p[i].key) continue; if (!certs.p[i].key)
if (!certs.p[i].cert) continue; continue;
if (!certs.p[i].cert->ca_istrue) continue; if (!certs.p[i].cert)
continue;
if (!certs.p[i].cert->ca_istrue)
continue;
mbedtls_pk_free(certs.p[i].key); mbedtls_pk_free(certs.p[i].key);
Free(&certs.p[i].key); Free(&certs.p[i].key);
} }
@ -1695,7 +1722,8 @@ static void CertsDestroy(void) {
} }
static void WipeServingKeys(void) { static void WipeServingKeys(void) {
if (uniprocess) return; if (uniprocess)
return;
mbedtls_ssl_ticket_free(&ssltick); mbedtls_ssl_ticket_free(&ssltick);
mbedtls_ssl_key_cert_free(conf.key_cert), conf.key_cert = 0; mbedtls_ssl_key_cert_free(conf.key_cert), conf.key_cert = 0;
CertsDestroy(); CertsDestroy();
@ -1897,13 +1925,15 @@ static void ConfigureCertificate(mbedtls_x509write_cert *cw, struct Cert *ca,
// //
for (int i = 0; i < ips.n; ++i) { for (int i = 0; i < ips.n; ++i) {
uint32_t ip = ips.p[i]; uint32_t ip = ips.p[i];
if (IsLoopbackIp(ip)) continue; if (IsLoopbackIp(ip))
continue;
char rname[NI_MAXHOST]; char rname[NI_MAXHOST];
struct sockaddr_in addr4 = {AF_INET, 0, {htonl(ip)}}; struct sockaddr_in addr4 = {AF_INET, 0, {htonl(ip)}};
if (getnameinfo((struct sockaddr *)&addr4, sizeof(addr4), rname, if (getnameinfo((struct sockaddr *)&addr4, sizeof(addr4), rname,
sizeof(rname), 0, 0, NI_NAMEREQD) == 0) { sizeof(rname), 0, 0, NI_NAMEREQD) == 0) {
char *s = gc(strdup(rname)); char *s = gc(strdup(rname));
if (!name) name = s; if (!name)
name = s;
bool isduplicate = false; bool isduplicate = false;
for (int j = 0; j < nsan; ++j) { for (int j = 0; j < nsan; ++j) {
if (san[j].tag == MBEDTLS_X509_SAN_DNS_NAME && if (san[j].tag == MBEDTLS_X509_SAN_DNS_NAME &&
@ -1925,7 +1955,8 @@ static void ConfigureCertificate(mbedtls_x509write_cert *cw, struct Cert *ca,
// add san entry to cert for each ip address owned by system // add san entry to cert for each ip address owned by system
for (int i = 0; i < ips.n; ++i) { for (int i = 0; i < ips.n; ++i) {
uint32_t ip = ips.p[i]; uint32_t ip = ips.p[i];
if (IsLoopbackIp(ip)) continue; if (IsLoopbackIp(ip))
continue;
san = realloc(san, ++nsan * sizeof(*san)); san = realloc(san, ++nsan * sizeof(*san));
san[nsan - 1].tag = MBEDTLS_X509_SAN_IP_ADDRESS; san[nsan - 1].tag = MBEDTLS_X509_SAN_IP_ADDRESS;
san[nsan - 1].ip4 = ip; san[nsan - 1].ip4 = ip;
@ -1974,9 +2005,12 @@ static void ConfigureCertificate(mbedtls_x509write_cert *cw, struct Cert *ca,
static struct Cert GetKeySigningKey(void) { static struct Cert GetKeySigningKey(void) {
size_t i; size_t i;
for (i = 0; i < certs.n; ++i) { for (i = 0; i < certs.n; ++i) {
if (!certs.p[i].key) continue; if (!certs.p[i].key)
if (!certs.p[i].cert) continue; continue;
if (!certs.p[i].cert->ca_istrue) continue; if (!certs.p[i].cert)
continue;
if (!certs.p[i].cert->ca_istrue)
continue;
if (mbedtls_x509_crt_check_key_usage(certs.p[i].cert, if (mbedtls_x509_crt_check_key_usage(certs.p[i].cert,
MBEDTLS_X509_KU_KEY_CERT_SIGN)) { MBEDTLS_X509_KU_KEY_CERT_SIGN)) {
continue; continue;
@ -2059,7 +2093,8 @@ static void LoadCertificates(void) {
} }
#ifdef MBEDTLS_ECP_C #ifdef MBEDTLS_ECP_C
ecp = GenerateEcpCertificate(ksk.key ? &ksk : 0); ecp = GenerateEcpCertificate(ksk.key ? &ksk : 0);
if (!havecert) UseCertificate(&conf, &ecp, "server"); if (!havecert)
UseCertificate(&conf, &ecp, "server");
if (!haveclientcert && ksk.key) { if (!haveclientcert && ksk.key) {
UseCertificate(&confcli, &ecp, "client"); UseCertificate(&confcli, &ecp, "client");
} }
@ -2068,7 +2103,8 @@ static void LoadCertificates(void) {
#ifdef MBEDTLS_RSA_C #ifdef MBEDTLS_RSA_C
if (!norsagen) { if (!norsagen) {
rsa = GenerateRsaCertificate(ksk.key ? &ksk : 0); rsa = GenerateRsaCertificate(ksk.key ? &ksk : 0);
if (!havecert) UseCertificate(&conf, &rsa, "server"); if (!havecert)
UseCertificate(&conf, &rsa, "server");
if (!haveclientcert && ksk.key) { if (!haveclientcert && ksk.key) {
UseCertificate(&confcli, &rsa, "client"); UseCertificate(&confcli, &rsa, "client");
} }
@ -2237,11 +2273,13 @@ static bool OpenZip(bool force) {
static struct Asset *GetAssetZip(const char *path, size_t pathlen) { static struct Asset *GetAssetZip(const char *path, size_t pathlen) {
uint32_t i, step, hash; uint32_t i, step, hash;
if (pathlen > 1 && path[0] == '/') ++path, --pathlen; if (pathlen > 1 && path[0] == '/')
++path, --pathlen;
hash = Hash(path, pathlen); hash = Hash(path, pathlen);
for (step = 0;; ++step) { for (step = 0;; ++step) {
i = (hash + ((step * (step + 1)) >> 1)) & (assets.n - 1); i = (hash + ((step * (step + 1)) >> 1)) & (assets.n - 1);
if (!assets.p[i].hash) return NULL; if (!assets.p[i].hash)
return NULL;
if (hash == assets.p[i].hash && if (hash == assets.p[i].hash &&
pathlen == ZIP_CFILE_NAMESIZE(zmap + assets.p[i].cf) && pathlen == ZIP_CFILE_NAMESIZE(zmap + assets.p[i].cf) &&
memcmp(path, ZIP_CFILE_NAME(zmap + assets.p[i].cf), pathlen) == 0) { memcmp(path, ZIP_CFILE_NAME(zmap + assets.p[i].cf), pathlen) == 0) {
@ -2288,7 +2326,8 @@ static struct Asset *GetAsset(const char *path, size_t pathlen) {
} }
static char *AppendHeader(char *p, const char *k, const char *v) { static char *AppendHeader(char *p, const char *k, const char *v) {
if (!v) return p; if (!v)
return p;
return AppendCrlf(stpcpy(stpcpy(stpcpy(p, k), ": "), v)); return AppendCrlf(stpcpy(stpcpy(stpcpy(p, k), ": "), v));
} }
@ -2316,7 +2355,8 @@ static char *AppendExpires(char *p, int64_t t) {
} }
static char *AppendCache(char *p, int64_t seconds, char *directive) { static char *AppendCache(char *p, int64_t seconds, char *directive) {
if (seconds < 0) return p; if (seconds < 0)
return p;
p = stpcpy(p, "Cache-Control: max-age="); p = stpcpy(p, "Cache-Control: max-age=");
p = FormatUint64(p, seconds); p = FormatUint64(p, seconds);
if (!seconds) { if (!seconds) {
@ -2392,7 +2432,8 @@ static void *LoadAsset(struct Asset *a, size_t *out_size) {
} }
if (!a->file) { if (!a->file) {
size = GetZipLfileUncompressedSize(zmap + a->lf); size = GetZipLfileUncompressedSize(zmap + a->lf);
if (size == SIZE_MAX || !(data = malloc(size + 1))) return NULL; if (size == SIZE_MAX || !(data = malloc(size + 1)))
return NULL;
if (IsCompressed(a)) { if (IsCompressed(a)) {
if (!Inflate(data, size, ZIP_LFILE_CONTENT(zmap + a->lf), if (!Inflate(data, size, ZIP_LFILE_CONTENT(zmap + a->lf),
GetZipCfileCompressedSize(zmap + a->cf))) { GetZipCfileCompressedSize(zmap + a->cf))) {
@ -2407,7 +2448,8 @@ static void *LoadAsset(struct Asset *a, size_t *out_size) {
return NULL; return NULL;
} }
data[size] = '\0'; data[size] = '\0';
if (out_size) *out_size = size; if (out_size)
*out_size = size;
return data; return data;
} else { } else {
LockInc(&shared->c.slurps); LockInc(&shared->c.slurps);
@ -2622,7 +2664,8 @@ static ssize_t YieldGenerator(struct iovec v[3]) {
int nresults, status; int nresults, status;
if (cpm.isyielding > 1) { if (cpm.isyielding > 1) {
do { do {
if (!YL || lua_status(YL) != LUA_YIELD) return 0; // done yielding if (!YL || lua_status(YL) != LUA_YIELD)
return 0; // done yielding
cpm.contentlength = 0; cpm.contentlength = 0;
status = lua_resume(YL, NULL, 0, &nresults); status = lua_resume(YL, NULL, 0, &nresults);
if (status != LUA_OK && status != LUA_YIELD) { if (status != LUA_OK && status != LUA_YIELD) {
@ -2631,7 +2674,8 @@ static ssize_t YieldGenerator(struct iovec v[3]) {
return -1; return -1;
} }
lua_pop(YL, nresults); lua_pop(YL, nresults);
if (!cpm.contentlength) UseOutput(); if (!cpm.contentlength)
UseOutput();
// continue yielding if nothing to return to keep generator running // continue yielding if nothing to return to keep generator running
} while (!cpm.contentlength); } while (!cpm.contentlength);
} }
@ -2667,7 +2711,8 @@ static int LuaCallWithYield(lua_State *L) {
CHECK_GT(lua_gettop(L), 0); // make sure that coroutine is anchored CHECK_GT(lua_gettop(L), 0); // make sure that coroutine is anchored
YL = co; YL = co;
cpm.generator = YieldGenerator; cpm.generator = YieldGenerator;
if (!cpm.isyielding) cpm.isyielding = 1; if (!cpm.isyielding)
cpm.isyielding = 1;
status = LUA_OK; status = LUA_OK;
} }
return status; return status;
@ -2770,7 +2815,8 @@ static ssize_t InflateGenerator(struct iovec v[3]) {
dg.s.next_out = dg.b; dg.s.next_out = dg.b;
dg.s.avail_out = dg.z; dg.s.avail_out = dg.z;
rc = inflate(&dg.s, Z_NO_FLUSH); rc = inflate(&dg.s, Z_NO_FLUSH);
if (rc != Z_OK && rc != Z_STREAM_END) DIEF("(zip) inflate()→%d", rc); if (rc != Z_OK && rc != Z_STREAM_END)
DIEF("(zip) inflate()→%d", rc);
no = dg.z - dg.s.avail_out; no = dg.z - dg.s.avail_out;
if (no) { if (no) {
v[i].iov_base = dg.b; v[i].iov_base = dg.b;
@ -2877,7 +2923,8 @@ static char *GetAssetPath(uint8_t *zcf, size_t *out_size) {
p2[0] = '/'; p2[0] = '/';
memcpy(p2 + 1, p1, n1); memcpy(p2 + 1, p1, n1);
p2[1 + n1] = '\0'; p2[1 + n1] = '\0';
if (out_size) *out_size = 1 + n1; if (out_size)
*out_size = 1 + n1;
return p2; return p2;
} }
@ -2928,8 +2975,10 @@ static void LaunchBrowser(const char *path) {
port = ntohs(servers.p[0].addr.sin_port); port = ntohs(servers.p[0].addr.sin_port);
} }
// assign a loopback address if no server or unknown server address // assign a loopback address if no server or unknown server address
if (!servers.n || !addr.s_addr) addr.s_addr = htonl(INADDR_LOOPBACK); if (!servers.n || !addr.s_addr)
if (*path != '/') path = gc(xasprintf("/%s", path)); addr.s_addr = htonl(INADDR_LOOPBACK);
if (*path != '/')
path = gc(xasprintf("/%s", path));
launch_browser(gc(xasprintf("http://%s:%d%s", inet_ntoa(addr), port, path))); launch_browser(gc(xasprintf("http://%s:%d%s", inet_ntoa(addr), port, path)));
} }
@ -3102,11 +3151,13 @@ static const char *MergeNames(const char *a, const char *b) {
} }
static void AppendLong1(const char *a, long x) { static void AppendLong1(const char *a, long x) {
if (x) appendf(&cpm.outbuf, "%s: %ld\r\n", a, x); if (x)
appendf(&cpm.outbuf, "%s: %ld\r\n", a, x);
} }
static void AppendLong2(const char *a, const char *b, long x) { static void AppendLong2(const char *a, const char *b, long x) {
if (x) appendf(&cpm.outbuf, "%s.%s: %ld\r\n", a, b, x); if (x)
appendf(&cpm.outbuf, "%s.%s: %ld\r\n", a, b, x);
} }
static void AppendTimeval(const char *a, struct timeval *tv) { static void AppendTimeval(const char *a, struct timeval *tv) {
@ -3282,7 +3333,8 @@ static char *HandleRedirect(struct Redirect *r) {
} else { } else {
LockInc(&shared->c.redirects); LockInc(&shared->c.redirects);
code = r->code; code = r->code;
if (!code) code = 307; if (!code)
code = 307;
DEBUGF("(rsp) %d redirect to %`'s", code, r->location.s); DEBUGF("(rsp) %d redirect to %`'s", code, r->location.s);
return AppendHeader( return AppendHeader(
SetStatus(code, GetHttpReason(code)), "Location", SetStatus(code, GetHttpReason(code)), "Location",
@ -3629,8 +3681,10 @@ static void StoreAsset(const char *path, size_t pathlen, const char *data,
} }
INFOF("(srvr) storing asset %`'s", path); INFOF("(srvr) storing asset %`'s", path);
disk = gflags = iattrs = 0; disk = gflags = iattrs = 0;
if (isutf8(path, pathlen)) gflags |= kZipGflagUtf8; if (isutf8(path, pathlen))
if (istext(data, datalen)) iattrs |= kZipIattrText; gflags |= kZipGflagUtf8;
if (istext(data, datalen))
iattrs |= kZipIattrText;
crc = crc32_z(0, data, datalen); crc = crc32_z(0, data, datalen);
if (datalen < 100) { if (datalen < 100) {
method = kZipCompressionNone; method = kZipCompressionNone;
@ -3661,9 +3715,12 @@ static void StoreAsset(const char *path, size_t pathlen, const char *data,
OpenZip(false); OpenZip(false);
now = timespec_real(); now = timespec_real();
a = GetAssetZip(path, pathlen); a = GetAssetZip(path, pathlen);
if (!mode) mode = a ? GetMode(a) : 0644; if (!mode)
if (!(mode & S_IFMT)) mode |= S_IFREG; mode = a ? GetMode(a) : 0644;
if (pathlen > 1 && path[0] == '/') ++path, --pathlen; if (!(mode & S_IFMT))
mode |= S_IFREG;
if (pathlen > 1 && path[0] == '/')
++path, --pathlen;
dosmode = !(mode & 0200) ? kNtFileAttributeReadonly : 0; dosmode = !(mode & 0200) ? kNtFileAttributeReadonly : 0;
ft = (now.tv_sec + MODERNITYSECONDS) * HECTONANOSECONDS; ft = (now.tv_sec + MODERNITYSECONDS) * HECTONANOSECONDS;
GetDosLocalTime(now.tv_sec, &mtime, &mdate); GetDosLocalTime(now.tv_sec, &mtime, &mdate);
@ -3812,12 +3869,14 @@ static void StoreFile(const char *path) {
struct stat st; struct stat st;
size_t plen, tlen; size_t plen, tlen;
const char *target = path; const char *target = path;
if (startswith(target, "./")) target += 2; if (startswith(target, "./"))
target += 2;
tlen = strlen(target); tlen = strlen(target);
if (!IsReasonablePath(target, tlen)) if (!IsReasonablePath(target, tlen))
FATALF("(cfg) error: can't store %`'s: contains '.' or '..' segments", FATALF("(cfg) error: can't store %`'s: contains '.' or '..' segments",
target); target);
if (lstat(path, &st) == -1) FATALF("(cfg) error: can't stat %`'s: %m", path); if (lstat(path, &st) == -1)
FATALF("(cfg) error: can't stat %`'s: %m", path);
if (!(p = xslurp(path, &plen))) if (!(p = xslurp(path, &plen)))
FATALF("(cfg) error: can't read %`'s: %m", path); FATALF("(cfg) error: can't read %`'s: %m", path);
StoreAsset(target, tlen, p, plen, st.st_mode & 0777); StoreAsset(target, tlen, p, plen, st.st_mode & 0777);
@ -3831,10 +3890,13 @@ static void StorePath(const char *dirpath) {
if (!isdirectory(dirpath) && !endswith(dirpath, "/")) { if (!isdirectory(dirpath) && !endswith(dirpath, "/")) {
return StoreFile(dirpath); return StoreFile(dirpath);
} }
if (!(d = opendir(dirpath))) FATALF("(cfg) error: can't open %`'s", dirpath); if (!(d = opendir(dirpath)))
FATALF("(cfg) error: can't open %`'s", dirpath);
while ((e = readdir(d))) { while ((e = readdir(d))) {
if (strcmp(e->d_name, ".") == 0) continue; if (strcmp(e->d_name, ".") == 0)
if (strcmp(e->d_name, "..") == 0) continue; continue;
if (strcmp(e->d_name, "..") == 0)
continue;
path = gc(xjoinpaths(dirpath, e->d_name)); path = gc(xjoinpaths(dirpath, e->d_name));
if (e->d_type == DT_DIR) { if (e->d_type == DT_DIR) {
StorePath(path); StorePath(path);
@ -3861,7 +3923,8 @@ static int LuaStoreAsset(lua_State *L) {
static void ReseedRng(mbedtls_ctr_drbg_context *r, const char *s) { static void ReseedRng(mbedtls_ctr_drbg_context *r, const char *s) {
#ifndef UNSECURE #ifndef UNSECURE
if (unsecure) return; if (unsecure)
return;
CHECK_EQ(0, mbedtls_ctr_drbg_reseed(r, (void *)s, strlen(s))); CHECK_EQ(0, mbedtls_ctr_drbg_reseed(r, (void *)s, strlen(s)));
#endif #endif
} }
@ -3869,8 +3932,10 @@ static void ReseedRng(mbedtls_ctr_drbg_context *r, const char *s) {
static void LogMessage(const char *d, const char *s, size_t n) { static void LogMessage(const char *d, const char *s, size_t n) {
size_t n2, n3; size_t n2, n3;
char *s2, *s3; char *s2, *s3;
if (!LOGGABLE(kLogInfo)) return; if (!LOGGABLE(kLogInfo))
while (n && (s[n - 1] == '\r' || s[n - 1] == '\n')) --n; return;
while (n && (s[n - 1] == '\r' || s[n - 1] == '\n'))
--n;
if ((s2 = DecodeLatin1(s, n, &n2))) { if ((s2 = DecodeLatin1(s, n, &n2))) {
if ((s3 = IndentLines(s2, n2, &n3, 1))) { if ((s3 = IndentLines(s2, n2, &n3, 1))) {
INFOF("(stat) %s %,ld byte message\r\n%.*s", d, n, n3, s3); INFOF("(stat) %s %,ld byte message\r\n%.*s", d, n, n3, s3);
@ -3883,9 +3948,12 @@ static void LogMessage(const char *d, const char *s, size_t n) {
static void LogBody(const char *d, const char *s, size_t n) { static void LogBody(const char *d, const char *s, size_t n) {
char *s2, *s3; char *s2, *s3;
size_t n2, n3; size_t n2, n3;
if (!n) return; if (!n)
if (!LOGGABLE(kLogInfo)) return; return;
while (n && (s[n - 1] == '\r' || s[n - 1] == '\n')) --n; if (!LOGGABLE(kLogInfo))
return;
while (n && (s[n - 1] == '\r' || s[n - 1] == '\n'))
--n;
if ((s2 = VisualizeControlCodes(s, n, &n2))) { if ((s2 = VisualizeControlCodes(s, n, &n2))) {
if ((s3 = IndentLines(s2, n2, &n3, 1))) { if ((s3 = IndentLines(s2, n2, &n3, 1))) {
INFOF("(stat) %s %,ld byte payload\r\n%.*s", d, n, n3, s3); INFOF("(stat) %s %,ld byte payload\r\n%.*s", d, n, n3, s3);
@ -4107,7 +4175,8 @@ static int LuaGetUser(lua_State *L) {
if (url.user.p) { if (url.user.p) {
LuaPushUrlView(L, &url.user); LuaPushUrlView(L, &url.user);
} else if ((p = gc(GetBasicAuthorization(&n)))) { } else if ((p = gc(GetBasicAuthorization(&n)))) {
if (!(q = memchr(p, ':', n))) q = p + n; if (!(q = memchr(p, ':', n)))
q = p + n;
lua_pushlstring(L, p, q - p); lua_pushlstring(L, p, q - p);
} else { } else {
lua_pushnil(L); lua_pushnil(L);
@ -4148,8 +4217,10 @@ static int LuaGetHost(lua_State *L) {
static int LuaGetPort(lua_State *L) { static int LuaGetPort(lua_State *L) {
int i, x = 0; int i, x = 0;
OnlyCallDuringRequest(L, "GetPort"); OnlyCallDuringRequest(L, "GetPort");
for (i = 0; i < url.port.n; ++i) x = url.port.p[i] - '0' + x * 10; for (i = 0; i < url.port.n; ++i)
if (!x) x = ntohs(serveraddr->sin_port); x = url.port.p[i] - '0' + x * 10;
if (!x)
x = ntohs(serveraddr->sin_port);
lua_pushinteger(L, x); lua_pushinteger(L, x);
return 1; return 1;
} }
@ -4219,7 +4290,8 @@ static int LuaSetHeader(lua_State *L) {
OnlyCallDuringRequest(L, "SetHeader"); OnlyCallDuringRequest(L, "SetHeader");
key = luaL_checklstring(L, 1, &keylen); key = luaL_checklstring(L, 1, &keylen);
val = luaL_optlstring(L, 2, 0, &vallen); val = luaL_optlstring(L, 2, 0, &vallen);
if (!val) return 0; if (!val)
return 0;
if ((h = GetHttpHeader(key, keylen)) == -1) { if ((h = GetHttpHeader(key, keylen)) == -1) {
if (!IsValidHttpToken(key, keylen)) { if (!IsValidHttpToken(key, keylen)) {
luaL_argerror(L, 1, "invalid"); luaL_argerror(L, 1, "invalid");
@ -4283,7 +4355,8 @@ static int LuaGetCookie(lua_State *L) {
} else { } else {
lua_pushnil(L); lua_pushnil(L);
} }
if (cookie) free(cookie); if (cookie)
free(cookie);
return 1; return 1;
} }
@ -4503,7 +4576,8 @@ static int LuaProgramUniprocess(lua_State *L) {
return luaL_argerror(L, 1, "invalid uniprocess mode; boolean expected"); return luaL_argerror(L, 1, "invalid uniprocess mode; boolean expected");
} }
lua_pushboolean(L, uniprocess); lua_pushboolean(L, uniprocess);
if (lua_isboolean(L, 1)) uniprocess = lua_toboolean(L, 1); if (lua_isboolean(L, 1))
uniprocess = lua_toboolean(L, 1);
return 1; return 1;
} }
@ -4525,7 +4599,8 @@ static int LuaProgramMaxWorkers(lua_State *L) {
return luaL_argerror(L, 1, "invalid number of workers; integer expected"); return luaL_argerror(L, 1, "invalid number of workers; integer expected");
} }
lua_pushinteger(L, maxworkers); lua_pushinteger(L, maxworkers);
if (lua_isinteger(L, 1)) maxworkers = lua_tointeger(L, 1); if (lua_isinteger(L, 1))
maxworkers = lua_tointeger(L, 1);
maxworkers = MAX(maxworkers, 1); maxworkers = MAX(maxworkers, 1);
return 1; return 1;
} }
@ -4803,7 +4878,8 @@ static int LuaIsAssetCompressed(lua_State *L) {
static bool Blackhole(uint32_t ip) { static bool Blackhole(uint32_t ip) {
char buf[4]; char buf[4];
if (blackhole.fd <= 0) return false; if (blackhole.fd <= 0)
return false;
WRITE32BE(buf, ip); WRITE32BE(buf, ip);
if (sendto(blackhole.fd, &buf, 4, 0, (struct sockaddr *)&blackhole.addr, if (sendto(blackhole.fd, &buf, 4, 0, (struct sockaddr *)&blackhole.addr,
sizeof(blackhole.addr)) != -1) { sizeof(blackhole.addr)) != -1) {
@ -4927,8 +5003,10 @@ static int LuaProgramTokenBucket(lua_State *L) {
reject, // reject, //
ignore, // ignore, //
ban); ban);
if (ignore == -1) ignore = -128; if (ignore == -1)
if (ban == -1) ban = -128; ignore = -128;
if (ban == -1)
ban = -128;
if (ban >= 0 && (IsLinux() || IsBsd())) { if (ban >= 0 && (IsLinux() || IsBsd())) {
uint32_t testip = 0; uint32_t testip = 0;
blackhole.addr.sun_family = AF_UNIX; blackhole.addr.sun_family = AF_UNIX;
@ -4955,14 +5033,16 @@ static int LuaProgramTokenBucket(lua_State *L) {
tokenbucket.replenish = timespec_fromnanos(1 / replenish * 1e9); tokenbucket.replenish = timespec_fromnanos(1 / replenish * 1e9);
int pid = fork(); int pid = fork();
npassert(pid != -1); npassert(pid != -1);
if (!pid) Replenisher(); if (!pid)
Replenisher();
++shared->workers; ++shared->workers;
return 0; return 0;
} }
static const char *GetContentTypeExt(const char *path, size_t n) { static const char *GetContentTypeExt(const char *path, size_t n) {
const char *r = NULL, *e; const char *r = NULL, *e;
if ((r = FindContentType(path, n))) return r; if ((r = FindContentType(path, n)))
return r;
#ifndef STATIC #ifndef STATIC
int top; int top;
lua_State *L = GL; lua_State *L = GL;
@ -5053,7 +5133,8 @@ static bool LuaRunAsset(const char *path, bool mandatory) {
if (status != LUA_OK || LuaCallWithTrace(L, 0, 0, NULL) != LUA_OK) { if (status != LUA_OK || LuaCallWithTrace(L, 0, 0, NULL) != LUA_OK) {
LogLuaError("lua code", lua_tostring(L, -1)); LogLuaError("lua code", lua_tostring(L, -1));
lua_pop(L, 1); // pop error lua_pop(L, 1); // pop error
if (mandatory) exit(1); if (mandatory)
exit(1);
} }
} }
} }
@ -5288,6 +5369,7 @@ static const luaL_Reg kLuaFuncs[] = {
{"Uncompress", LuaUncompress}, // {"Uncompress", LuaUncompress}, //
{"Underlong", LuaUnderlong}, // {"Underlong", LuaUnderlong}, //
{"UuidV4", LuaUuidV4}, // {"UuidV4", LuaUuidV4}, //
{"UuidV7", LuaUuidV7}, //
{"VisualizeControlCodes", LuaVisualizeControlCodes}, // {"VisualizeControlCodes", LuaVisualizeControlCodes}, //
{"Write", LuaWrite}, // {"Write", LuaWrite}, //
{"bin", LuaBin}, // {"bin", LuaBin}, //
@ -5416,7 +5498,8 @@ static void LuaPrint(lua_State *L) {
n = lua_gettop(L); n = lua_gettop(L);
if (n > 0) { if (n > 0) {
for (i = 1; i <= n; i++) { for (i = 1; i <= n; i++) {
if (i > 1) appendw(&b, '\t'); if (i > 1)
appendw(&b, '\t');
struct EncoderConfig conf = { struct EncoderConfig conf = {
.maxdepth = 64, .maxdepth = 64,
.sorted = true, .sorted = true,
@ -5450,12 +5533,14 @@ static int LuaInterpreter(lua_State *L) {
const char *script; const char *script;
if (optind < __argc) { if (optind < __argc) {
script = __argv[optind]; script = __argv[optind];
if (!strcmp(script, "-")) script = 0; if (!strcmp(script, "-"))
script = 0;
if ((status = luaL_loadfile(L, script)) == LUA_OK) { if ((status = luaL_loadfile(L, script)) == LUA_OK) {
lua_getglobal(L, "arg"); lua_getglobal(L, "arg");
n = luaL_len(L, -1); n = luaL_len(L, -1);
luaL_checkstack(L, n + 3, "too many script args"); luaL_checkstack(L, n + 3, "too many script args");
for (i = 1; i <= n; i++) lua_rawgeti(L, -i, i); for (i = 1; i <= n; i++)
lua_rawgeti(L, -i, i);
lua_remove(L, -i); // remove arg table from stack lua_remove(L, -i); // remove arg table from stack
TRACE_BEGIN; TRACE_BEGIN;
status = lua_runchunk(L, n, LUA_MULTRET); status = lua_runchunk(L, n, LUA_MULTRET);
@ -5469,7 +5554,8 @@ static int LuaInterpreter(lua_State *L) {
EnableRawMode(); EnableRawMode();
for (;;) { for (;;) {
status = lua_loadline(L); status = lua_loadline(L);
if (status == -1) break; // eof if (status == -1)
break; // eof
if (status == -2) { if (status == -2) {
if (errno == EINTR) { if (errno == EINTR) {
if ((sig = linenoiseGetInterrupt())) { if ((sig = linenoiseGetInterrupt())) {
@ -5579,10 +5665,14 @@ static void LuaOnServerReload(bool reindex) {
} }
static const char *DescribeClose(void) { static const char *DescribeClose(void) {
if (killed) return "killed"; if (killed)
if (meltdown) return "meltdown"; return "killed";
if (terminated) return "terminated"; if (meltdown)
if (connectionclose) return "connection closed"; return "meltdown";
if (terminated)
return "terminated";
if (connectionclose)
return "connection closed";
return "destroyed"; return "destroyed";
} }
@ -5859,7 +5949,8 @@ static char *ReadMore(void) {
ssize_t rc; ssize_t rc;
LockInc(&shared->c.frags); LockInc(&shared->c.frags);
if ((rc = reader(client, inbuf.p + amtread, inbuf.n - amtread)) != -1) { if ((rc = reader(client, inbuf.p + amtread, inbuf.n - amtread)) != -1) {
if (!(got = rc)) return HandlePayloadDisconnect(); if (!(got = rc))
return HandlePayloadDisconnect();
amtread += got; amtread += got;
} else if (errno == EINTR) { } else if (errno == EINTR) {
LockInc(&shared->c.readinterrupts); LockInc(&shared->c.readinterrupts);
@ -5877,10 +5968,12 @@ static char *ReadMore(void) {
static char *SynchronizeLength(void) { static char *SynchronizeLength(void) {
char *p; char *p;
if (hdrsize + payloadlength > amtread) { if (hdrsize + payloadlength > amtread) {
if (hdrsize + payloadlength > inbuf.n) return HandleHugePayload(); if (hdrsize + payloadlength > inbuf.n)
return HandleHugePayload();
SendContinueIfNeeded(); SendContinueIfNeeded();
while (amtread < hdrsize + payloadlength) { while (amtread < hdrsize + payloadlength) {
if ((p = ReadMore())) return p; if ((p = ReadMore()))
return p;
} }
} }
cpm.msgsize = hdrsize + payloadlength; cpm.msgsize = hdrsize + payloadlength;
@ -5894,9 +5987,11 @@ static char *SynchronizeChunked(void) {
SendContinueIfNeeded(); SendContinueIfNeeded();
while (!(transferlength = Unchunk(&u, inbuf.p + hdrsize, amtread - hdrsize, while (!(transferlength = Unchunk(&u, inbuf.p + hdrsize, amtread - hdrsize,
&payloadlength))) { &payloadlength))) {
if ((p = ReadMore())) return p; if ((p = ReadMore()))
return p;
} }
if (transferlength == -1) return HandleHugePayload(); if (transferlength == -1)
return HandleHugePayload();
cpm.msgsize = hdrsize + transferlength; cpm.msgsize = hdrsize + transferlength;
return NULL; return NULL;
} }
@ -5987,8 +6082,10 @@ static char *HandleRequest(void) {
} else { } else {
return HandleVersionNotSupported(); return HandleVersionNotSupported();
} }
if ((p = SynchronizeStream())) return p; if ((p = SynchronizeStream()))
if (logbodies) LogBody("received", inbuf.p + hdrsize, payloadlength); return p;
if (logbodies)
LogBody("received", inbuf.p + hdrsize, payloadlength);
if (cpm.msg.version < 11 || HeaderEqualCase(kHttpConnection, "close")) { if (cpm.msg.version < 11 || HeaderEqualCase(kHttpConnection, "close")) {
connectionclose = true; connectionclose = true;
} }
@ -6025,7 +6122,8 @@ static char *HandleRequest(void) {
} }
FreeLater(url.params.p); FreeLater(url.params.p);
#ifndef STATIC #ifndef STATIC
if (hasonhttprequest) return LuaOnHttpRequest(); if (hasonhttprequest)
return LuaOnHttpRequest();
#endif #endif
return Route(url.host.p, url.host.n, url.path.p, url.path.n); return Route(url.host.p, url.host.n, url.path.p, url.path.n);
} }
@ -6041,7 +6139,8 @@ static char *Route(const char *host, size_t hostlen, const char *path,
return p; return p;
} }
if (SlicesEqual(path, pathlen, "/", 1)) { if (SlicesEqual(path, pathlen, "/", 1)) {
if ((p = ServeIndex("/", 1))) return p; if ((p = ServeIndex("/", 1)))
return p;
return ServeListing(); return ServeListing();
} else if ((p = RoutePath(path, pathlen))) { } else if ((p = RoutePath(path, pathlen))) {
return p; return p;
@ -6090,16 +6189,19 @@ static char *RouteHost(const char *host, size_t hostlen, const char *path,
hp = hm <= sizeof(b) ? b : FreeLater(xmalloc(hm)); hp = hm <= sizeof(b) ? b : FreeLater(xmalloc(hm));
hp[0] = '/'; hp[0] = '/';
mempcpy(mempcpy(hp + 1, host, hostlen), path, pathlen); mempcpy(mempcpy(hp + 1, host, hostlen), path, pathlen);
if ((p = RoutePath(hp, hn))) return p; if ((p = RoutePath(hp, hn)))
return p;
if (!isdigit(host[0])) { if (!isdigit(host[0])) {
if (hostlen > 4 && if (hostlen > 4 &&
READ32LE(host) == ('w' | 'w' << 8 | 'w' << 16 | '.' << 24)) { READ32LE(host) == ('w' | 'w' << 8 | 'w' << 16 | '.' << 24)) {
mempcpy(mempcpy(hp + 1, host + 4, hostlen - 4), path, pathlen); mempcpy(mempcpy(hp + 1, host + 4, hostlen - 4), path, pathlen);
if ((p = RoutePath(hp, hn - 4))) return p; if ((p = RoutePath(hp, hn - 4)))
return p;
} else { } else {
mempcpy(mempcpy(mempcpy(hp + 1, "www.", 4), host, hostlen), path, mempcpy(mempcpy(mempcpy(hp + 1, "www.", 4), host, hostlen), path,
pathlen); pathlen);
if ((p = RoutePath(hp, hn + 4))) return p; if ((p = RoutePath(hp, hn + 4)))
return p;
} }
} }
} }
@ -6123,7 +6225,8 @@ static inline bool IsLua(struct Asset *a) {
static char *HandleAsset(struct Asset *a, const char *path, size_t pathlen) { static char *HandleAsset(struct Asset *a, const char *path, size_t pathlen) {
char *p; char *p;
#ifndef STATIC #ifndef STATIC
if (IsLua(a)) return ServeLua(a, path, pathlen); if (IsLua(a))
return ServeLua(a, path, pathlen);
#endif #endif
if (cpm.msg.method == kHttpGet || cpm.msg.method == kHttpHead) { if (cpm.msg.method == kHttpGet || cpm.msg.method == kHttpHead) {
LockInc(&shared->c.staticrequests); LockInc(&shared->c.staticrequests);
@ -6150,8 +6253,10 @@ static const char *GetContentType(struct Asset *a, const char *path, size_t n) {
} }
static bool IsNotModified(struct Asset *a) { static bool IsNotModified(struct Asset *a) {
if (cpm.msg.version < 10) return false; if (cpm.msg.version < 10)
if (!HasHeader(kHttpIfModifiedSince)) return false; return false;
if (!HasHeader(kHttpIfModifiedSince))
return false;
return a->lastmodified <= return a->lastmodified <=
ParseHttpDateTime(HeaderData(kHttpIfModifiedSince), ParseHttpDateTime(HeaderData(kHttpIfModifiedSince),
HeaderLength(kHttpIfModifiedSince)); HeaderLength(kHttpIfModifiedSince));
@ -6217,8 +6322,10 @@ static char *ServeAsset(struct Asset *a, const char *path, size_t pathlen) {
static char *SetStatus(unsigned code, const char *reason) { static char *SetStatus(unsigned code, const char *reason) {
if (cpm.msg.version == 10) { if (cpm.msg.version == 10) {
if (code == 307) code = 302; if (code == 307)
if (code == 308) code = 301; code = 302;
if (code == 308)
code = 301;
} }
cpm.statuscode = code; cpm.statuscode = code;
cpm.hascontenttype = false; cpm.hascontenttype = false;
@ -6321,7 +6428,8 @@ static bool StreamResponse(char *p) {
iov[3].iov_len = 0; iov[3].iov_len = 0;
iov[4].iov_base = 0; iov[4].iov_base = 0;
iov[4].iov_len = 0; iov[4].iov_len = 0;
if ((rc = cpm.generator(iov + 2)) <= 0) break; if ((rc = cpm.generator(iov + 2)) <= 0)
break;
if (cpm.msg.version >= 11) { if (cpm.msg.version >= 11) {
s = chunkbuf; s = chunkbuf;
s += uint64toarray_radix16(rc, s); s += uint64toarray_radix16(rc, s);
@ -6329,7 +6437,8 @@ static bool StreamResponse(char *p) {
iov[1].iov_base = chunkbuf; iov[1].iov_base = chunkbuf;
iov[1].iov_len = s - chunkbuf; iov[1].iov_len = s - chunkbuf;
} }
if (Send(iov, 6) == -1) break; if (Send(iov, 6) == -1)
break;
iov[0].iov_base = 0; iov[0].iov_base = 0;
iov[0].iov_len = 0; iov[0].iov_len = 0;
} }
@ -6350,8 +6459,9 @@ static bool HandleMessageActual(void) {
long reqtime, contime; long reqtime, contime;
char *p; char *p;
struct timespec now; struct timespec now;
if ((rc = ParseHttpMessage(&cpm.msg, inbuf.p, amtread)) != -1) { if ((rc = ParseHttpMessage(&cpm.msg, inbuf.p, amtread, inbuf.n)) != -1) {
if (!rc) return false; if (!rc)
return false;
hdrsize = rc; hdrsize = rc;
if (logmessages) { if (logmessages) {
LogMessage("received", inbuf.p, hdrsize); LogMessage("received", inbuf.p, hdrsize);
@ -6373,8 +6483,10 @@ static bool HandleMessageActual(void) {
} }
if (cpm.msg.version >= 10) { if (cpm.msg.version >= 10) {
p = AppendCrlf(stpcpy(stpcpy(p, "Date: "), shared->currentdate)); p = AppendCrlf(stpcpy(stpcpy(p, "Date: "), shared->currentdate));
if (!cpm.branded) p = stpcpy(p, serverheader); if (!cpm.branded)
if (extrahdrs) p = stpcpy(p, extrahdrs); p = stpcpy(p, serverheader);
if (extrahdrs)
p = stpcpy(p, extrahdrs);
if (connectionclose) { if (connectionclose) {
p = stpcpy(p, "Connection: close\r\n"); p = stpcpy(p, "Connection: close\r\n");
} else if (timeout.tv_sec < 0 && cpm.msg.version >= 11) { } else if (timeout.tv_sec < 0 && cpm.msg.version >= 11) {
@ -6395,7 +6507,8 @@ static bool HandleMessageActual(void) {
now = timespec_real(); now = timespec_real();
reqtime = timespec_tomicros(timespec_sub(now, startrequest)); reqtime = timespec_tomicros(timespec_sub(now, startrequest));
contime = timespec_tomicros(timespec_sub(now, startconnection)); contime = timespec_tomicros(timespec_sub(now, startconnection));
if (hasonloglatency) LuaOnLogLatency(reqtime, contime); if (hasonloglatency)
LuaOnLogLatency(reqtime, contime);
if (loglatency || LOGGABLE(kLogDebug)) if (loglatency || LOGGABLE(kLogDebug))
LOGF(kLogDebug, "(stat) %`'.*s latency r: %,ldµs c: %,ldµs", LOGF(kLogDebug, "(stat) %`'.*s latency r: %,ldµs c: %,ldµs",
cpm.msg.uri.b - cpm.msg.uri.a, inbuf.p + cpm.msg.uri.a, reqtime, cpm.msg.uri.b - cpm.msg.uri.a, inbuf.p + cpm.msg.uri.a, reqtime,
@ -6422,8 +6535,10 @@ static void InitRequest(void) {
} }
static bool IsSsl(unsigned char c) { static bool IsSsl(unsigned char c) {
if (c == 22) return true; if (c == 22)
if (!(c & 128)) return false; return true;
if (!(c & 128))
return false;
/* RHEL5 sends SSLv2 hello but supports TLS */ /* RHEL5 sends SSLv2 hello but supports TLS */
DEBUGF("(ssl) %s SSLv2 hello D:", DescribeClient()); DEBUGF("(ssl) %s SSLv2 hello D:", DescribeClient());
return true; return true;
@ -6440,7 +6555,8 @@ static void HandleMessages(void) {
for (;;) { for (;;) {
if (!cpm.msg.i && amtread) { if (!cpm.msg.i && amtread) {
startrequest = timespec_real(); startrequest = timespec_real();
if (HandleMessage()) break; if (HandleMessage())
break;
} }
if ((rc = reader(client, inbuf.p + amtread, inbuf.n - amtread)) != -1) { if ((rc = reader(client, inbuf.p + amtread, inbuf.n - amtread)) != -1) {
startrequest = timespec_real(); startrequest = timespec_real();
@ -6483,7 +6599,8 @@ static void HandleMessages(void) {
errno = 0; errno = 0;
} else if (errno == EAGAIN) { } else if (errno == EAGAIN) {
LockInc(&shared->c.readtimeouts); LockInc(&shared->c.readtimeouts);
if (amtread) SendTimeout(); if (amtread)
SendTimeout();
NotifyClose(); NotifyClose();
LogClose("read timeout"); LogClose("read timeout");
return; return;
@ -6646,7 +6763,8 @@ static void *MemoryMonitor(void *arg) {
if (tty != -1) { if (tty != -1) {
for (gen = 0, mi = 0, b = 0; !terminatemonitor;) { for (gen = 0, mi = 0, b = 0; !terminatemonitor;) {
workers = atomic_load_explicit(&shared->workers, memory_order_relaxed); workers = atomic_load_explicit(&shared->workers, memory_order_relaxed);
if (id) id = MAX(1, MIN(id, workers)); if (id)
id = MAX(1, MIN(id, workers));
if (!id && workers) { if (!id && workers) {
usleep(50000); usleep(50000);
continue; continue;
@ -6931,11 +7049,16 @@ static int HandleConnection(size_t i) {
static void MakeExecutableModifiable(void) { static void MakeExecutableModifiable(void) {
#ifdef __x86_64__ #ifdef __x86_64__
int ft; int ft;
if (!(SUPPORT_VECTOR & (_HOSTMETAL | _HOSTWINDOWS | _HOSTXNU))) return; if (!(SUPPORT_VECTOR & (_HOSTMETAL | _HOSTWINDOWS | _HOSTXNU)))
if (IsWindows()) return; // TODO return;
if (IsOpenbsd()) return; // TODO if (IsWindows())
if (IsNetbsd()) return; // TODO return; // TODO
if (endswith(zpath, ".dbg")) return; if (IsOpenbsd())
return; // TODO
if (IsNetbsd())
return; // TODO
if (endswith(zpath, ".dbg"))
return;
close(zfd); close(zfd);
ft = ftrace_enabled(0); ft = ftrace_enabled(0);
if ((zfd = __open_executable()) == -1) { if ((zfd = __open_executable()) == -1) {
@ -6999,8 +7122,10 @@ static int HandlePoll(int ms) {
if (nfds) { if (nfds) {
// handle pollid/o events // handle pollid/o events
for (pollid = 0; pollid < 1 + servers.n; ++pollid) { for (pollid = 0; pollid < 1 + servers.n; ++pollid) {
if (!polls[pollid].revents) continue; if (!polls[pollid].revents)
if (polls[pollid].fd < 0) continue; continue;
if (polls[pollid].fd < 0)
continue;
if (polls[pollid].fd) { if (polls[pollid].fd) {
// handle listen socket // handle listen socket
lua_repl_lock(); lua_repl_lock();
@ -7011,12 +7136,14 @@ static int HandlePoll(int ms) {
rc = HandleConnection(serverid); rc = HandleConnection(serverid);
ishandlingconnection = false; ishandlingconnection = false;
lua_repl_unlock(); lua_repl_unlock();
if (rc == -1) return -1; if (rc == -1)
return -1;
#ifndef STATIC #ifndef STATIC
} else { } else {
// handle standard input // handle standard input
rc = HandleReadline(); rc = HandleReadline();
if (rc == -1) return rc; if (rc == -1)
return rc;
#endif #endif
} }
} }
@ -7024,7 +7151,8 @@ static int HandlePoll(int ms) {
} else if (__ttyconf.replmode) { } else if (__ttyconf.replmode) {
// handle refresh repl line // handle refresh repl line
rc = HandleReadline(); rc = HandleReadline();
if (rc < 0) return rc; if (rc < 0)
return rc;
#endif #endif
} }
} else { } else {
@ -7094,7 +7222,8 @@ static void Listen(void) {
} }
port = ntohs(servers.p[n].addr.sin_port); port = ntohs(servers.p[n].addr.sin_port);
ip = ntohl(servers.p[n].addr.sin_addr.s_addr); ip = ntohl(servers.p[n].addr.sin_addr.s_addr);
if (ip == INADDR_ANY) ip = INADDR_LOOPBACK; if (ip == INADDR_ANY)
ip = INADDR_LOOPBACK;
INFOF("(srvr) listen http://%hhu.%hhu.%hhu.%hhu:%d", ip >> 24, ip >> 16, INFOF("(srvr) listen http://%hhu.%hhu.%hhu.%hhu:%d", ip >> 24, ip >> 16,
ip >> 8, ip, port); ip >> 8, ip, port);
if (printport && !ports.p[j]) { if (printport && !ports.p[j]) {
@ -7122,7 +7251,8 @@ static void HandleShutdown(void) {
CloseServerFds(); CloseServerFds();
INFOF("(srvr) received %s", strsignal(shutdownsig)); INFOF("(srvr) received %s", strsignal(shutdownsig));
if (shutdownsig != SIGINT && shutdownsig != SIGQUIT) { if (shutdownsig != SIGINT && shutdownsig != SIGQUIT) {
if (!killed) terminated = false; if (!killed)
terminated = false;
INFOF("(srvr) killing process group"); INFOF("(srvr) killing process group");
KillGroup(); KillGroup();
} }
@ -7193,7 +7323,8 @@ static void SigInit(void) {
static void TlsInit(void) { static void TlsInit(void) {
#ifndef UNSECURE #ifndef UNSECURE
int suite; int suite;
if (unsecure) return; if (unsecure)
return;
if (suiteb && !mbedtls_aes_uses_hardware()) { if (suiteb && !mbedtls_aes_uses_hardware()) {
WARNF("(srvr) requested suiteb crypto, but hardware aes not present"); WARNF("(srvr) requested suiteb crypto, but hardware aes not present");
@ -7227,7 +7358,8 @@ static void TlsInit(void) {
mbedtls_ssl_ticket_parse, &ssltick); mbedtls_ssl_ticket_parse, &ssltick);
} }
if (sslinitialized) return; if (sslinitialized)
return;
sslinitialized = true; sslinitialized = true;
LoadCertificates(); LoadCertificates();
@ -7257,7 +7389,8 @@ static void TlsInit(void) {
static void TlsDestroy(void) { static void TlsDestroy(void) {
#ifndef UNSECURE #ifndef UNSECURE
if (unsecure) return; if (unsecure)
return;
mbedtls_ssl_free(&ssl); mbedtls_ssl_free(&ssl);
mbedtls_ssl_free(&sslcli); mbedtls_ssl_free(&sslcli);
mbedtls_ctr_drbg_free(&rng); mbedtls_ctr_drbg_free(&rng);
@ -7352,9 +7485,11 @@ static void GetOpts(int argc, char *argv[]) {
} }
} }
// if storing asset(s) is requested, don't need to continue // if storing asset(s) is requested, don't need to continue
if (storeasset) exit(0); if (storeasset)
exit(0);
// we don't want to drop into a repl after using -e in -i mode // we don't want to drop into a repl after using -e in -i mode
if (interpretermode && got_e_arg) exit(0); if (interpretermode && got_e_arg)
exit(0);
} }
void RedBean(int argc, char *argv[]) { void RedBean(int argc, char *argv[]) {
@ -7362,7 +7497,8 @@ void RedBean(int argc, char *argv[]) {
int fd; int fd;
// don't complain about --assimilate if it's the only parameter, // don't complain about --assimilate if it's the only parameter,
// as it can only get here if it's already native or assimilated // as it can only get here if it's already native or assimilated
if (argc == 2 && strcmp(argv[1], "--assimilate") == 0) return; if (argc == 2 && strcmp(argv[1], "--assimilate") == 0)
return;
if (IsLinux()) { if (IsLinux()) {
// disable weird linux capabilities // disable weird linux capabilities
for (int e = errno, i = 0;; ++i) { for (int e = errno, i = 0;; ++i) {
@ -7415,7 +7551,8 @@ void RedBean(int argc, char *argv[]) {
shared->workers = 1; shared->workers = 1;
} }
if (daemonize) { if (daemonize) {
if (!logpath) ProgramLogPath("/dev/null"); if (!logpath)
ProgramLogPath("/dev/null");
dup2(2, 1); dup2(2, 1);
} }
SigInit(); SigInit();