2020-09-07 04:39:00 +00:00
|
|
|
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
|
|
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
|
|
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
|
|
|
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
|
|
|
|
│ │
|
2020-12-28 01:18:44 +00:00
|
|
|
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
|
|
|
|
│ any purpose with or without fee is hereby granted, provided that the │
|
|
|
|
|
│ above copyright notice and this permission notice appear in all copies. │
|
2020-09-07 04:39:00 +00:00
|
|
|
|
│ │
|
2020-12-28 01:18:44 +00:00
|
|
|
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
|
|
|
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
|
|
|
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
|
|
|
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
|
|
|
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
|
|
|
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
|
|
|
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
|
|
|
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
2020-09-07 04:39:00 +00:00
|
|
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
2021-05-03 19:09:35 +00:00
|
|
|
|
#include "libc/bits/bits.h"
|
|
|
|
|
#include "libc/bits/likely.h"
|
2021-03-27 14:29:55 +00:00
|
|
|
|
#include "libc/bits/popcnt.h"
|
2021-03-01 07:42:35 +00:00
|
|
|
|
#include "libc/bits/safemacros.internal.h"
|
2020-09-07 04:39:00 +00:00
|
|
|
|
#include "libc/calls/calls.h"
|
2021-05-03 18:52:24 +00:00
|
|
|
|
#include "libc/calls/sigbits.h"
|
2021-06-24 19:31:26 +00:00
|
|
|
|
#include "libc/calls/struct/dirent.h"
|
2021-05-14 12:36:58 +00:00
|
|
|
|
#include "libc/calls/struct/flock.h"
|
2021-04-18 18:34:59 +00:00
|
|
|
|
#include "libc/calls/struct/rusage.h"
|
2021-05-03 18:52:24 +00:00
|
|
|
|
#include "libc/calls/struct/sigaction.h"
|
2020-10-06 06:11:49 +00:00
|
|
|
|
#include "libc/calls/struct/stat.h"
|
2021-06-24 19:31:26 +00:00
|
|
|
|
#include "libc/dns/dns.h"
|
|
|
|
|
#include "libc/dns/hoststxt.h"
|
2021-05-05 14:25:39 +00:00
|
|
|
|
#include "libc/dos.h"
|
2021-03-07 19:31:44 +00:00
|
|
|
|
#include "libc/errno.h"
|
2020-12-09 23:04:54 +00:00
|
|
|
|
#include "libc/fmt/conv.h"
|
|
|
|
|
#include "libc/fmt/itoa.h"
|
2020-09-07 04:39:00 +00:00
|
|
|
|
#include "libc/log/check.h"
|
|
|
|
|
#include "libc/log/log.h"
|
2021-06-24 19:31:26 +00:00
|
|
|
|
#include "libc/macros.internal.h"
|
2020-10-06 06:11:49 +00:00
|
|
|
|
#include "libc/math.h"
|
2021-05-14 12:36:58 +00:00
|
|
|
|
#include "libc/mem/alloca.h"
|
2021-03-29 08:22:49 +00:00
|
|
|
|
#include "libc/mem/fmt.h"
|
2021-06-24 19:31:26 +00:00
|
|
|
|
#include "libc/mem/mem.h"
|
2021-03-27 14:29:55 +00:00
|
|
|
|
#include "libc/nexgen32e/bsf.h"
|
|
|
|
|
#include "libc/nexgen32e/bsr.h"
|
2020-10-06 06:11:49 +00:00
|
|
|
|
#include "libc/nexgen32e/crc32.h"
|
2021-05-03 19:09:35 +00:00
|
|
|
|
#include "libc/nt/enum/fileflagandattributes.h"
|
2021-06-24 19:31:26 +00:00
|
|
|
|
#include "libc/rand/rand.h"
|
2021-04-18 18:34:59 +00:00
|
|
|
|
#include "libc/runtime/clktck.h"
|
2021-05-03 19:09:35 +00:00
|
|
|
|
#include "libc/runtime/directmap.internal.h"
|
2021-05-03 18:52:24 +00:00
|
|
|
|
#include "libc/runtime/gc.internal.h"
|
2021-04-24 20:58:34 +00:00
|
|
|
|
#include "libc/runtime/runtime.h"
|
2020-09-07 04:39:00 +00:00
|
|
|
|
#include "libc/sock/sock.h"
|
|
|
|
|
#include "libc/stdio/stdio.h"
|
2021-05-01 12:11:35 +00:00
|
|
|
|
#include "libc/str/str.h"
|
2021-07-04 19:21:29 +00:00
|
|
|
|
#include "libc/str/undeflate.h"
|
2020-09-07 04:39:00 +00:00
|
|
|
|
#include "libc/sysv/consts/af.h"
|
2020-10-06 06:11:49 +00:00
|
|
|
|
#include "libc/sysv/consts/auxv.h"
|
2021-06-24 19:31:26 +00:00
|
|
|
|
#include "libc/sysv/consts/dt.h"
|
2020-09-07 04:39:00 +00:00
|
|
|
|
#include "libc/sysv/consts/ex.h"
|
|
|
|
|
#include "libc/sysv/consts/exit.h"
|
2021-05-14 12:36:58 +00:00
|
|
|
|
#include "libc/sysv/consts/f.h"
|
2020-09-07 04:39:00 +00:00
|
|
|
|
#include "libc/sysv/consts/inaddr.h"
|
|
|
|
|
#include "libc/sysv/consts/ipproto.h"
|
2021-05-03 19:09:35 +00:00
|
|
|
|
#include "libc/sysv/consts/lock.h"
|
2021-05-03 08:21:50 +00:00
|
|
|
|
#include "libc/sysv/consts/madv.h"
|
2020-10-06 06:11:49 +00:00
|
|
|
|
#include "libc/sysv/consts/map.h"
|
2021-04-18 18:34:59 +00:00
|
|
|
|
#include "libc/sysv/consts/msync.h"
|
2020-10-06 06:11:49 +00:00
|
|
|
|
#include "libc/sysv/consts/o.h"
|
2021-06-24 19:31:26 +00:00
|
|
|
|
#include "libc/sysv/consts/poll.h"
|
2020-10-06 06:11:49 +00:00
|
|
|
|
#include "libc/sysv/consts/prot.h"
|
2021-04-23 17:45:19 +00:00
|
|
|
|
#include "libc/sysv/consts/rusage.h"
|
2021-05-03 19:09:35 +00:00
|
|
|
|
#include "libc/sysv/consts/s.h"
|
2020-09-07 04:39:00 +00:00
|
|
|
|
#include "libc/sysv/consts/shut.h"
|
2021-06-24 19:31:26 +00:00
|
|
|
|
#include "libc/sysv/consts/sig.h"
|
2020-09-07 04:39:00 +00:00
|
|
|
|
#include "libc/sysv/consts/so.h"
|
|
|
|
|
#include "libc/sysv/consts/sock.h"
|
|
|
|
|
#include "libc/sysv/consts/sol.h"
|
2020-10-11 04:18:53 +00:00
|
|
|
|
#include "libc/sysv/consts/tcp.h"
|
2021-03-25 09:21:13 +00:00
|
|
|
|
#include "libc/sysv/consts/w.h"
|
2020-09-07 04:39:00 +00:00
|
|
|
|
#include "libc/time/time.h"
|
|
|
|
|
#include "libc/x/x.h"
|
2020-10-06 06:11:49 +00:00
|
|
|
|
#include "libc/zip.h"
|
2021-03-25 09:21:13 +00:00
|
|
|
|
#include "net/http/escape.h"
|
2020-09-07 04:39:00 +00:00
|
|
|
|
#include "net/http/http.h"
|
2021-04-23 17:45:19 +00:00
|
|
|
|
#include "net/http/ip.h"
|
2021-04-18 18:34:59 +00:00
|
|
|
|
#include "net/http/url.h"
|
2021-07-08 04:44:27 +00:00
|
|
|
|
#include "net/https/https.h"
|
2020-09-07 04:39:00 +00:00
|
|
|
|
#include "third_party/getopt/getopt.h"
|
2021-03-22 00:27:53 +00:00
|
|
|
|
#include "third_party/lua/lauxlib.h"
|
|
|
|
|
#include "third_party/lua/ltests.h"
|
|
|
|
|
#include "third_party/lua/lua.h"
|
|
|
|
|
#include "third_party/lua/lualib.h"
|
2021-06-24 19:31:26 +00:00
|
|
|
|
#include "third_party/mbedtls/asn1.h"
|
|
|
|
|
#include "third_party/mbedtls/asn1write.h"
|
|
|
|
|
#include "third_party/mbedtls/config.h"
|
|
|
|
|
#include "third_party/mbedtls/ctr_drbg.h"
|
|
|
|
|
#include "third_party/mbedtls/debug.h"
|
|
|
|
|
#include "third_party/mbedtls/ecp.h"
|
|
|
|
|
#include "third_party/mbedtls/entropy.h"
|
|
|
|
|
#include "third_party/mbedtls/entropy_poll.h"
|
2021-07-08 04:44:27 +00:00
|
|
|
|
#include "third_party/mbedtls/error.h"
|
2021-07-06 20:39:18 +00:00
|
|
|
|
#include "third_party/mbedtls/iana.h"
|
2021-07-05 01:36:47 +00:00
|
|
|
|
#include "third_party/mbedtls/md5.h"
|
2021-06-24 19:31:26 +00:00
|
|
|
|
#include "third_party/mbedtls/oid.h"
|
|
|
|
|
#include "third_party/mbedtls/pk.h"
|
|
|
|
|
#include "third_party/mbedtls/rsa.h"
|
|
|
|
|
#include "third_party/mbedtls/san.h"
|
2021-07-05 01:36:47 +00:00
|
|
|
|
#include "third_party/mbedtls/sha1.h"
|
2021-06-24 19:31:26 +00:00
|
|
|
|
#include "third_party/mbedtls/ssl.h"
|
|
|
|
|
#include "third_party/mbedtls/x509.h"
|
|
|
|
|
#include "third_party/mbedtls/x509_crt.h"
|
2021-04-21 02:14:21 +00:00
|
|
|
|
#include "third_party/regex/regex.h"
|
2020-10-06 06:11:49 +00:00
|
|
|
|
#include "third_party/zlib/zlib.h"
|
2020-09-07 04:39:00 +00:00
|
|
|
|
|
2021-06-24 19:31:26 +00:00
|
|
|
|
/**
|
|
|
|
|
* @fileoverview redbean - single-file distributable web server
|
|
|
|
|
*
|
|
|
|
|
* redbean makes it possible to share web applications that run offline
|
|
|
|
|
* as a single-file αcτµαlly pδrταblε εxεcµταblε zip archive which
|
|
|
|
|
* contains your assets. All you need to do is download the redbean.com
|
|
|
|
|
* program below, change the filename to .zip, add your content in a zip
|
|
|
|
|
* editing tool, and then change the extension back to .com.
|
|
|
|
|
*
|
|
|
|
|
* redbean can serve 1 million+ gzip encoded responses per second on a
|
|
|
|
|
* cheap personal computer. That performance is thanks to zip and gzip
|
|
|
|
|
* using the same compression format, which enables kernelspace copies.
|
|
|
|
|
* Another reason redbean goes fast is that it's a tiny static binary,
|
|
|
|
|
* which makes fork memory paging nearly free.
|
|
|
|
|
*
|
|
|
|
|
* redbean is also easy to modify to suit your own needs. The program
|
|
|
|
|
* itself is written as a single .c file. It embeds the Lua programming
|
|
|
|
|
* language and SQLite which let you write dynamic pages.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifndef REDBEAN
|
|
|
|
|
#define REDBEAN "redbean"
|
|
|
|
|
#endif
|
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
|
#define HASH_LOAD_FACTOR /* 1. / */ 4
|
2021-06-24 19:31:26 +00:00
|
|
|
|
#define read(F, P, N) readv(F, &(struct iovec){P, N}, 1)
|
2021-07-03 12:51:04 +00:00
|
|
|
|
#define write(F, P, N) writev(F, &(struct iovec){P, N}, 1)
|
2021-06-24 19:31:26 +00:00
|
|
|
|
#define LockInc(P) asm volatile("lock incq\t%0" : "=m"(*(P)))
|
|
|
|
|
#define AppendCrlf(P) mempcpy(P, "\r\n", 2)
|
|
|
|
|
#define HasHeader(H) (!!msg.headers[H].a)
|
|
|
|
|
#define HeaderData(H) (inbuf.p + msg.headers[H].a)
|
|
|
|
|
#define HeaderLength(H) (msg.headers[H].b - msg.headers[H].a)
|
2021-04-21 02:14:21 +00:00
|
|
|
|
#define HeaderEqualCase(H, S) \
|
|
|
|
|
SlicesEqualCase(S, strlen(S), HeaderData(H), HeaderLength(H))
|
2021-04-18 18:34:59 +00:00
|
|
|
|
|
2020-10-06 06:11:49 +00:00
|
|
|
|
static const uint8_t kGzipHeader[] = {
|
|
|
|
|
0x1F, // MAGNUM
|
|
|
|
|
0x8B, // MAGNUM
|
|
|
|
|
0x08, // CM: DEFLATE
|
|
|
|
|
0x00, // FLG: NONE
|
|
|
|
|
0x00, // MTIME: NONE
|
|
|
|
|
0x00, //
|
|
|
|
|
0x00, //
|
|
|
|
|
0x00, //
|
|
|
|
|
0x00, // XFL
|
|
|
|
|
kZipOsUnix, // OS
|
|
|
|
|
};
|
|
|
|
|
|
2021-04-21 02:14:21 +00:00
|
|
|
|
static const char *const kIndexPaths[] = {
|
|
|
|
|
#ifndef STATIC
|
|
|
|
|
"index.lua",
|
|
|
|
|
#endif
|
|
|
|
|
"index.html",
|
|
|
|
|
};
|
|
|
|
|
|
2021-05-01 12:11:35 +00:00
|
|
|
|
static const char kRegCode[][8] = {
|
|
|
|
|
"OK", "NOMATCH", "BADPAT", "COLLATE", "ECTYPE", "EESCAPE", "ESUBREG",
|
|
|
|
|
"EBRACK", "EPAREN", "EBRACE", "BADBR", "ERANGE", "ESPACE", "BADRPT",
|
2021-04-23 17:45:19 +00:00
|
|
|
|
};
|
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
|
struct Buffer {
|
2021-04-24 20:58:34 +00:00
|
|
|
|
size_t n, c;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
char *p;
|
|
|
|
|
};
|
|
|
|
|
|
2021-03-28 14:54:21 +00:00
|
|
|
|
struct Strings {
|
2021-05-14 12:36:58 +00:00
|
|
|
|
size_t n, c;
|
|
|
|
|
struct String {
|
|
|
|
|
size_t n;
|
|
|
|
|
const char *s;
|
|
|
|
|
} * p;
|
2021-03-28 14:54:21 +00:00
|
|
|
|
};
|
|
|
|
|
|
2021-06-24 19:31:26 +00:00
|
|
|
|
static struct Ips {
|
|
|
|
|
size_t n;
|
|
|
|
|
uint32_t *p;
|
|
|
|
|
} ips;
|
|
|
|
|
|
|
|
|
|
static struct Ports {
|
|
|
|
|
size_t n;
|
|
|
|
|
uint16_t *p;
|
|
|
|
|
} ports;
|
|
|
|
|
|
|
|
|
|
static struct Servers {
|
|
|
|
|
size_t n;
|
|
|
|
|
struct Server {
|
|
|
|
|
int fd;
|
|
|
|
|
struct sockaddr_in addr;
|
|
|
|
|
} * p;
|
|
|
|
|
} servers;
|
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
|
static struct Freelist {
|
2021-04-18 18:34:59 +00:00
|
|
|
|
size_t n, c;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
void **p;
|
|
|
|
|
} freelist;
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static struct Unmaplist {
|
|
|
|
|
size_t n, c;
|
|
|
|
|
struct Unmap {
|
|
|
|
|
int f;
|
|
|
|
|
void *p;
|
|
|
|
|
size_t n;
|
|
|
|
|
} * p;
|
|
|
|
|
} unmaplist;
|
|
|
|
|
|
2021-06-24 19:31:26 +00:00
|
|
|
|
static struct Certs {
|
|
|
|
|
size_t n;
|
|
|
|
|
struct Cert {
|
|
|
|
|
mbedtls_x509_crt *cert;
|
|
|
|
|
mbedtls_pk_context *key;
|
|
|
|
|
} * p;
|
|
|
|
|
} certs;
|
|
|
|
|
|
2020-10-06 06:11:49 +00:00
|
|
|
|
static struct Redirects {
|
2021-03-25 09:21:13 +00:00
|
|
|
|
size_t n;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
struct Redirect {
|
2021-03-29 08:22:49 +00:00
|
|
|
|
int code;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
struct String path;
|
|
|
|
|
struct String location;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
} * p;
|
|
|
|
|
} redirects;
|
|
|
|
|
|
|
|
|
|
static struct Assets {
|
|
|
|
|
uint32_t n;
|
|
|
|
|
struct Asset {
|
2021-04-18 18:34:59 +00:00
|
|
|
|
bool istext;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
uint32_t hash;
|
2021-04-18 18:34:59 +00:00
|
|
|
|
uint64_t cf;
|
|
|
|
|
uint64_t lf;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
int64_t lastmodified;
|
|
|
|
|
char *lastmodifiedstr;
|
2021-03-28 14:54:21 +00:00
|
|
|
|
struct File {
|
2021-05-14 12:36:58 +00:00
|
|
|
|
struct String path;
|
2021-03-28 14:54:21 +00:00
|
|
|
|
struct stat st;
|
|
|
|
|
} * file;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
} * p;
|
|
|
|
|
} assets;
|
|
|
|
|
|
2021-03-29 08:22:49 +00:00
|
|
|
|
static struct Shared {
|
2021-04-18 18:34:59 +00:00
|
|
|
|
int workers;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
long double nowish;
|
2021-05-02 18:50:43 +00:00
|
|
|
|
long double lastreindex;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
long double lastmeltdown;
|
|
|
|
|
char currentdate[32];
|
|
|
|
|
struct rusage server;
|
|
|
|
|
struct rusage children;
|
2021-05-03 19:09:35 +00:00
|
|
|
|
struct Counters {
|
|
|
|
|
#define C(x) long x;
|
|
|
|
|
#include "tool/net/counters.inc"
|
|
|
|
|
#undef C
|
|
|
|
|
} c;
|
2021-03-29 08:22:49 +00:00
|
|
|
|
} * shared;
|
|
|
|
|
|
2021-05-03 19:09:35 +00:00
|
|
|
|
static const char kCounterNames[] =
|
|
|
|
|
#define C(x) #x "\0"
|
|
|
|
|
#include "tool/net/counters.inc"
|
|
|
|
|
#undef C
|
|
|
|
|
;
|
|
|
|
|
|
2021-06-24 19:31:26 +00:00
|
|
|
|
typedef ssize_t (*reader_f)(int, void *, size_t);
|
|
|
|
|
typedef ssize_t (*writer_f)(int, struct iovec *, int);
|
|
|
|
|
|
|
|
|
|
static bool usessl;
|
|
|
|
|
static bool suiteb;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
static bool killed;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
static bool istext;
|
|
|
|
|
static bool zombied;
|
|
|
|
|
static bool gzipped;
|
|
|
|
|
static bool branded;
|
2021-04-24 20:58:34 +00:00
|
|
|
|
static bool funtrace;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
static bool meltdown;
|
2021-02-27 18:47:19 +00:00
|
|
|
|
static bool printport;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
static bool daemonize;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static bool logrusage;
|
2021-03-28 07:10:17 +00:00
|
|
|
|
static bool logbodies;
|
2021-07-08 04:44:27 +00:00
|
|
|
|
static bool sslcliused;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static bool loglatency;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
static bool terminated;
|
|
|
|
|
static bool uniprocess;
|
|
|
|
|
static bool invalidated;
|
|
|
|
|
static bool logmessages;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
static bool checkedmethod;
|
2021-07-08 04:44:27 +00:00
|
|
|
|
static bool sslfetchverify;
|
|
|
|
|
static bool sslclientverify;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
static bool connectionclose;
|
|
|
|
|
static bool keyboardinterrupt;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
static bool listeningonport443;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static bool loggednetworkorigin;
|
2021-04-25 00:09:01 +00:00
|
|
|
|
static bool hasluaglobalhandler;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
static bool upgradeinsecurerequests;
|
|
|
|
|
static bool dontupgradeinsecurerequests;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
|
2021-05-03 19:09:35 +00:00
|
|
|
|
static int zfd;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
static int frags;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
static int gmtoff;
|
|
|
|
|
static int client;
|
2021-07-08 22:56:23 +00:00
|
|
|
|
static int changeuid;
|
|
|
|
|
static int changegid;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
static int statuscode;
|
2021-07-08 22:56:23 +00:00
|
|
|
|
static int oldloglevel;
|
2021-05-01 12:11:35 +00:00
|
|
|
|
static int maxpayloadsize;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static int messageshandled;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
static uint32_t clientaddrsize;
|
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
|
static lua_State *L;
|
2021-03-28 14:54:21 +00:00
|
|
|
|
static size_t zsize;
|
2021-04-21 02:14:21 +00:00
|
|
|
|
static char *content;
|
2021-03-28 07:10:17 +00:00
|
|
|
|
static uint8_t *zmap;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
static uint8_t *zbase;
|
2021-05-02 18:50:43 +00:00
|
|
|
|
static uint8_t *zcdir;
|
2021-03-27 14:29:55 +00:00
|
|
|
|
static size_t hdrsize;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
static size_t msgsize;
|
|
|
|
|
static size_t amtread;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
static reader_f reader;
|
|
|
|
|
static writer_f writer;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static char *extrahdrs;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
static char *luaheaderp;
|
2021-05-02 18:50:43 +00:00
|
|
|
|
static const char *zpath;
|
2021-03-29 08:22:49 +00:00
|
|
|
|
static const char *brand;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
static char gzip_footer[8];
|
2020-10-06 06:11:49 +00:00
|
|
|
|
static const char *pidpath;
|
|
|
|
|
static const char *logpath;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
static const char *keypath;
|
|
|
|
|
static const char *certpath;
|
|
|
|
|
static const char *launchbrowser;
|
|
|
|
|
static struct pollfd *polls;
|
2021-04-25 00:09:01 +00:00
|
|
|
|
static struct Strings loops;
|
2021-07-05 08:03:45 +00:00
|
|
|
|
static size_t payloadlength;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
static size_t contentlength;
|
|
|
|
|
static int64_t cacheseconds;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
static const char *serverheader;
|
2021-03-29 09:39:20 +00:00
|
|
|
|
static struct Strings stagedirs;
|
|
|
|
|
static struct Strings hidepaths;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
|
|
|
|
|
static struct Buffer inbuf;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
static struct Buffer oldin;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
static struct Buffer hdrbuf;
|
|
|
|
|
static struct Buffer outbuf;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static struct timeval timeout;
|
|
|
|
|
static struct Buffer effectivepath;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
|
2021-04-21 02:14:21 +00:00
|
|
|
|
static struct Url url;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
static struct HttpMessage msg;
|
2021-04-21 02:14:21 +00:00
|
|
|
|
|
2021-05-02 18:50:43 +00:00
|
|
|
|
static struct stat zst;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
static long double startread;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static long double lastrefresh;
|
2021-04-18 18:34:59 +00:00
|
|
|
|
static long double startserver;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
static long double startrequest;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
static long double lastheartbeat;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
static long double startconnection;
|
|
|
|
|
static struct sockaddr_in clientaddr;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
static struct sockaddr_in *serveraddr;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
|
2021-07-08 04:44:27 +00:00
|
|
|
|
static mbedtls_ssl_config conf;
|
|
|
|
|
static mbedtls_ssl_context ssl;
|
|
|
|
|
static mbedtls_ctr_drbg_context rng;
|
|
|
|
|
|
|
|
|
|
static mbedtls_ssl_config confcli;
|
|
|
|
|
static mbedtls_ssl_context sslcli;
|
|
|
|
|
static mbedtls_ctr_drbg_context rngcli;
|
|
|
|
|
|
|
|
|
|
static char slashpath[PATH_MAX];
|
|
|
|
|
|
2021-05-03 19:09:35 +00:00
|
|
|
|
static char *Route(const char *, size_t, const char *, size_t);
|
|
|
|
|
static char *RouteHost(const char *, size_t, const char *, size_t);
|
|
|
|
|
static char *RoutePath(const char *, size_t);
|
|
|
|
|
static char *HandleAsset(struct Asset *, const char *, size_t);
|
|
|
|
|
static char *ServeAsset(struct Asset *, const char *, size_t);
|
|
|
|
|
static char *SetStatus(unsigned, const char *);
|
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
|
static void OnChld(void) {
|
|
|
|
|
zombied = true;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
|
static void OnUsr1(void) {
|
|
|
|
|
invalidated = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void OnUsr2(void) {
|
|
|
|
|
meltdown = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void OnTerm(void) {
|
|
|
|
|
if (!terminated) {
|
2020-10-06 06:11:49 +00:00
|
|
|
|
terminated = true;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
} else {
|
|
|
|
|
killed = true;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
|
static void OnInt(void) {
|
|
|
|
|
keyboardinterrupt = true;
|
|
|
|
|
OnTerm();
|
2020-10-06 06:11:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
|
static void OnHup(void) {
|
|
|
|
|
if (daemonize) {
|
|
|
|
|
OnUsr1();
|
|
|
|
|
} else {
|
|
|
|
|
OnTerm();
|
|
|
|
|
}
|
2020-10-06 06:11:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-03 19:09:35 +00:00
|
|
|
|
forceinline bool SlicesEqual(const char *a, size_t n, const char *b, size_t m) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
return n == m && !memcmp(a, b, n);
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-03 19:09:35 +00:00
|
|
|
|
forceinline bool SlicesEqualCase(const char *a, size_t n, const char *b,
|
|
|
|
|
size_t m) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
return n == m && !memcasecmp(a, b, n);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-27 14:29:55 +00:00
|
|
|
|
static int CompareSlices(const char *a, size_t n, const char *b, size_t m) {
|
2021-03-25 09:21:13 +00:00
|
|
|
|
int c;
|
|
|
|
|
if ((c = memcmp(a, b, MIN(n, m)))) return c;
|
|
|
|
|
if (n < m) return -1;
|
|
|
|
|
if (n > m) return +1;
|
|
|
|
|
return 0;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-27 14:29:55 +00:00
|
|
|
|
static int CompareSlicesCase(const char *a, size_t n, const char *b, size_t m) {
|
|
|
|
|
int c;
|
|
|
|
|
if ((c = memcasecmp(a, b, MIN(n, m)))) return c;
|
|
|
|
|
if (n < m) return -1;
|
|
|
|
|
if (n > m) return +1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static void *FreeLater(void *p) {
|
|
|
|
|
if (p) {
|
|
|
|
|
if (++freelist.n > freelist.c) {
|
2021-05-14 12:36:58 +00:00
|
|
|
|
freelist.c = freelist.n + (freelist.n >> 1);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
freelist.p = xrealloc(freelist.p, freelist.c * sizeof(*freelist.p));
|
|
|
|
|
}
|
|
|
|
|
freelist.p[freelist.n - 1] = p;
|
|
|
|
|
}
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void UnmapLater(int f, void *p, size_t n) {
|
|
|
|
|
if (++unmaplist.n > unmaplist.c) {
|
2021-05-14 12:36:58 +00:00
|
|
|
|
unmaplist.c = unmaplist.n + (unmaplist.n >> 1);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
unmaplist.p = xrealloc(unmaplist.p, unmaplist.c * sizeof(*unmaplist.p));
|
|
|
|
|
}
|
|
|
|
|
unmaplist.p[unmaplist.n - 1].f = f;
|
|
|
|
|
unmaplist.p[unmaplist.n - 1].p = p;
|
|
|
|
|
unmaplist.p[unmaplist.n - 1].n = n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void CollectGarbage(void) {
|
2021-07-08 22:56:23 +00:00
|
|
|
|
__log_level = oldloglevel;
|
2021-06-28 00:03:06 +00:00
|
|
|
|
DestroyHttpMessage(&msg);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
while (freelist.n) {
|
|
|
|
|
free(freelist.p[--freelist.n]);
|
|
|
|
|
}
|
|
|
|
|
while (unmaplist.n) {
|
|
|
|
|
--unmaplist.n;
|
|
|
|
|
LOGIFNEG1(munmap(unmaplist.p[unmaplist.n].p, unmaplist.p[unmaplist.n].n));
|
|
|
|
|
LOGIFNEG1(close(unmaplist.p[unmaplist.n].f));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void UseOutput(void) {
|
|
|
|
|
content = FreeLater(outbuf.p);
|
|
|
|
|
contentlength = outbuf.n;
|
|
|
|
|
outbuf.p = 0;
|
|
|
|
|
outbuf.n = 0;
|
2021-04-24 20:58:34 +00:00
|
|
|
|
outbuf.c = 0;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void DropOutput(void) {
|
2021-05-05 14:25:39 +00:00
|
|
|
|
FreeLater(outbuf.p);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
outbuf.p = 0;
|
|
|
|
|
outbuf.n = 0;
|
2021-04-24 20:58:34 +00:00
|
|
|
|
outbuf.c = 0;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ClearOutput(void) {
|
|
|
|
|
outbuf.n = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-24 20:58:34 +00:00
|
|
|
|
static void Grow(size_t n) {
|
|
|
|
|
do {
|
|
|
|
|
if (outbuf.c) {
|
|
|
|
|
outbuf.c += outbuf.c >> 1;
|
|
|
|
|
} else {
|
|
|
|
|
outbuf.c = 16 * 1024;
|
|
|
|
|
}
|
|
|
|
|
} while (n > outbuf.c);
|
|
|
|
|
outbuf.p = xrealloc(outbuf.p, outbuf.c);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-24 20:58:34 +00:00
|
|
|
|
static void AppendData(const char *data, size_t size) {
|
|
|
|
|
size_t n;
|
|
|
|
|
n = outbuf.n + size;
|
|
|
|
|
if (n > outbuf.c) Grow(n);
|
|
|
|
|
memcpy(outbuf.p + outbuf.n, data, size);
|
|
|
|
|
outbuf.n = n;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-24 20:58:34 +00:00
|
|
|
|
static void Append(const char *fmt, ...) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
int n;
|
|
|
|
|
char *p;
|
2021-04-24 20:58:34 +00:00
|
|
|
|
va_list va, vb;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
va_start(va, fmt);
|
2021-04-24 20:58:34 +00:00
|
|
|
|
va_copy(vb, va);
|
|
|
|
|
n = vsnprintf(outbuf.p + outbuf.n, outbuf.c - outbuf.n, fmt, va);
|
|
|
|
|
if (n >= outbuf.c - outbuf.n) {
|
|
|
|
|
Grow(outbuf.n + n + 1);
|
|
|
|
|
vsnprintf(outbuf.p + outbuf.n, outbuf.c - outbuf.n, fmt, vb);
|
|
|
|
|
}
|
|
|
|
|
va_end(vb);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
va_end(va);
|
2021-04-24 20:58:34 +00:00
|
|
|
|
outbuf.n += n;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-21 02:14:21 +00:00
|
|
|
|
static char *MergePaths(const char *p, size_t n, const char *q, size_t m,
|
|
|
|
|
size_t *z) {
|
|
|
|
|
char *r;
|
|
|
|
|
if (n && p[n - 1] == '/') --n;
|
|
|
|
|
if (m && q[0] == '/') ++q, --m;
|
|
|
|
|
r = xmalloc(n + 1 + m + 1);
|
|
|
|
|
mempcpy(mempcpy(mempcpy(mempcpy(r, p, n), "/", 1), q, m), "", 1);
|
|
|
|
|
if (z) *z = n + 1 + m;
|
|
|
|
|
return r;
|
2021-04-18 18:34:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-14 12:36:58 +00:00
|
|
|
|
static long FindRedirect(const char *s, size_t n) {
|
2020-10-06 06:11:49 +00:00
|
|
|
|
int c, m, l, r, z;
|
|
|
|
|
l = 0;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
r = redirects.n - 1;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
while (l <= r) {
|
|
|
|
|
m = (l + r) >> 1;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
c = CompareSlices(redirects.p[m].path.s, redirects.p[m].path.n, s, n);
|
2020-10-06 06:11:49 +00:00
|
|
|
|
if (c < 0) {
|
|
|
|
|
l = m + 1;
|
|
|
|
|
} else if (c > 0) {
|
|
|
|
|
r = m - 1;
|
|
|
|
|
} else {
|
2021-03-25 09:21:13 +00:00
|
|
|
|
return m;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-24 19:31:26 +00:00
|
|
|
|
static void LogCertificate(const char *msg, mbedtls_x509_crt *cert) {
|
|
|
|
|
char *s;
|
|
|
|
|
size_t n;
|
|
|
|
|
if (LOGGABLE(kLogDebug)) {
|
|
|
|
|
if ((s = gc(malloc((n = 15000))))) {
|
|
|
|
|
if (mbedtls_x509_crt_info(s, n, " ", cert) > 0) {
|
|
|
|
|
DEBUGF("%s\n%s", msg, chomp(s));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *FormatX509Name(mbedtls_x509_name *name) {
|
|
|
|
|
char *s = calloc(1, 1000);
|
|
|
|
|
CHECK_GT(mbedtls_x509_dn_gets(s, 1000, name), 0);
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool IsSelfSigned(mbedtls_x509_crt *cert) {
|
|
|
|
|
return !mbedtls_x509_name_cmp(&cert->issuer, &cert->subject);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static mbedtls_x509_crt *GetTrustedCertificate(mbedtls_x509_name *name) {
|
|
|
|
|
size_t i;
|
|
|
|
|
for (i = 0; i < certs.n; ++i) {
|
|
|
|
|
if (certs.p[i].cert &&
|
|
|
|
|
!mbedtls_x509_name_cmp(name, &certs.p[i].cert->subject)) {
|
|
|
|
|
return certs.p[i].cert;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool VerifyCertificate(mbedtls_x509_crt *cert, int depth) {
|
|
|
|
|
size_t i;
|
|
|
|
|
mbedtls_x509_crt *next;
|
|
|
|
|
if (depth < MBEDTLS_X509_MAX_INTERMEDIATE_CA) {
|
|
|
|
|
if ((next = cert->next)) {
|
|
|
|
|
if (!VerifyCertificate(next, depth + 1)) return false;
|
|
|
|
|
} else {
|
|
|
|
|
if (!(next = GetTrustedCertificate(&cert->issuer))) {
|
|
|
|
|
if (depth) {
|
|
|
|
|
WARNF("chain root %`'s isn't in your zip:usr/share/ssl/root folder",
|
|
|
|
|
gc(FormatX509Name(&cert->issuer)));
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!IsSelfSigned(cert) && !VerifyCertificate(next, depth + 1)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!mbedtls_x509_crt_check_parent(cert, next, 1) &&
|
|
|
|
|
!mbedtls_x509_crt_check_signature(cert, next, 0) &&
|
|
|
|
|
!mbedtls_x509_time_is_past(&cert->valid_to) &&
|
|
|
|
|
!mbedtls_x509_time_is_future(&cert->valid_from)) {
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
VERBOSEF("verification failed %`'s -> %`'s",
|
|
|
|
|
gc(FormatX509Name(&cert->subject)),
|
|
|
|
|
gc(FormatX509Name(&next->subject)));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
VERBOSEF("verification depth exceeded for %`'s",
|
|
|
|
|
gc(FormatX509Name(&cert->subject)));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void UseCertificate(mbedtls_x509_crt *cert, mbedtls_pk_context *key) {
|
|
|
|
|
if (VerifyCertificate(cert, 0)) {
|
|
|
|
|
if (!dontupgradeinsecurerequests) {
|
|
|
|
|
DEBUGF("enabling conditional https redirects");
|
|
|
|
|
upgradeinsecurerequests = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CHECK_EQ(0, mbedtls_ssl_conf_own_cert(&conf, cert, key));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool ChainCertificate(mbedtls_x509_crt *cert, mbedtls_x509_crt *parent) {
|
|
|
|
|
if (!mbedtls_x509_crt_check_signature(cert, parent, 0)) {
|
|
|
|
|
DEBUGF("chaining %`'s to %`'s", gc(FormatX509Name(&cert->subject)),
|
|
|
|
|
gc(FormatX509Name(&parent->subject)));
|
|
|
|
|
cert->next = parent;
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
WARNF("signature check failed for %`'s -> %`'s",
|
|
|
|
|
gc(FormatX509Name(&cert->subject)),
|
|
|
|
|
gc(FormatX509Name(&parent->subject)));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void InternCertificate(mbedtls_x509_crt *cert,
|
|
|
|
|
mbedtls_x509_crt *parent) {
|
|
|
|
|
int r;
|
|
|
|
|
size_t i;
|
|
|
|
|
if (cert->next) InternCertificate(cert->next, cert);
|
|
|
|
|
if (parent) {
|
|
|
|
|
if (mbedtls_x509_crt_check_parent(cert, parent, 1)) {
|
|
|
|
|
parent->next = 0; /* unchain ca root bundles */
|
|
|
|
|
} else if ((r = mbedtls_x509_crt_check_signature(cert, parent, 0))) {
|
|
|
|
|
WARNF("invalid signature for %`'s -> %`'s (-0x%04x)",
|
|
|
|
|
gc(FormatX509Name(&cert->subject)),
|
|
|
|
|
gc(FormatX509Name(&parent->subject)), -r);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (mbedtls_x509_time_is_past(&cert->valid_to)) {
|
|
|
|
|
WARNF("certificate is expired", gc(FormatX509Name(&cert->subject)));
|
|
|
|
|
} else if (mbedtls_x509_time_is_future(&cert->valid_from)) {
|
|
|
|
|
WARNF("certificate is from the future", gc(FormatX509Name(&cert->subject)));
|
|
|
|
|
}
|
|
|
|
|
for (i = 0; i < certs.n; ++i) {
|
|
|
|
|
if (!certs.p[i].cert) continue;
|
|
|
|
|
if (!mbedtls_x509_name_cmp(&cert->subject, &certs.p[i].cert->subject)) {
|
|
|
|
|
WARNF("certificate subject name %`'s is already loaded",
|
|
|
|
|
gc(FormatX509Name(&cert->subject)));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (i = 0; i < certs.n; ++i) {
|
|
|
|
|
if (!certs.p[i].cert && certs.p[i].key &&
|
|
|
|
|
!mbedtls_pk_check_pair(&cert->pk, certs.p[i].key)) {
|
|
|
|
|
certs.p[i].cert = cert;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
LogCertificate("loaded certificate", cert);
|
|
|
|
|
if (!cert->next && !IsSelfSigned(cert)) {
|
|
|
|
|
for (i = 0; i < certs.n; ++i) {
|
|
|
|
|
if (!certs.p[i].cert) continue;
|
|
|
|
|
if (!mbedtls_x509_crt_check_parent(cert, certs.p[i].cert, 1)) {
|
|
|
|
|
if (ChainCertificate(cert, certs.p[i].cert)) break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (i = 0; i < certs.n; ++i) {
|
|
|
|
|
if (!certs.p[i].cert) continue;
|
|
|
|
|
if (certs.p[i].cert->next) continue;
|
|
|
|
|
if (!mbedtls_x509_crt_check_parent(certs.p[i].cert, cert, 1)) {
|
|
|
|
|
ChainCertificate(certs.p[i].cert, cert);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
certs.p = realloc(certs.p, ++certs.n * sizeof(*certs.p));
|
|
|
|
|
certs.p[certs.n - 1].cert = cert;
|
|
|
|
|
certs.p[certs.n - 1].key = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ProgramCertificate(const char *p, size_t n) {
|
|
|
|
|
int rc;
|
|
|
|
|
unsigned char *waqapi;
|
|
|
|
|
mbedtls_x509_crt *cert;
|
|
|
|
|
waqapi = malloc(n + 1);
|
|
|
|
|
memcpy(waqapi, p, n);
|
|
|
|
|
waqapi[n] = 0;
|
|
|
|
|
cert = calloc(1, sizeof(mbedtls_x509_crt));
|
|
|
|
|
rc = mbedtls_x509_crt_parse(cert, waqapi, n + 1);
|
|
|
|
|
mbedtls_platform_zeroize(waqapi, n);
|
|
|
|
|
free(waqapi);
|
|
|
|
|
if (rc < 0) {
|
|
|
|
|
WARNF("failed to load certificate (grep -0x%04x)\n", rc);
|
|
|
|
|
return;
|
|
|
|
|
} else if (rc > 0) {
|
|
|
|
|
VERBOSEF("certificate bundle partially loaded");
|
|
|
|
|
}
|
|
|
|
|
InternCertificate(cert, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ProgramPrivateKey(const char *p, size_t n) {
|
|
|
|
|
int rc;
|
|
|
|
|
size_t i;
|
|
|
|
|
unsigned char *waqapi;
|
|
|
|
|
mbedtls_pk_context *key;
|
|
|
|
|
waqapi = malloc(n + 1);
|
|
|
|
|
memcpy(waqapi, p, n);
|
|
|
|
|
waqapi[n] = 0;
|
|
|
|
|
key = calloc(1, sizeof(mbedtls_pk_context));
|
|
|
|
|
rc = mbedtls_pk_parse_key(key, waqapi, n + 1, 0, 0);
|
|
|
|
|
mbedtls_platform_zeroize(waqapi, n);
|
|
|
|
|
free(waqapi);
|
|
|
|
|
if (rc != 0) {
|
|
|
|
|
fprintf(stderr, "error: load key (grep -0x%04x)\n", -rc);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
for (i = 0; i < certs.n; ++i) {
|
|
|
|
|
if (certs.p[i].cert && !certs.p[i].key &&
|
|
|
|
|
!mbedtls_pk_check_pair(&certs.p[i].cert->pk, key)) {
|
|
|
|
|
certs.p[i].key = key;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
VERBOSEF("loaded private key");
|
|
|
|
|
certs.p = realloc(certs.p, ++certs.n * sizeof(*certs.p));
|
|
|
|
|
certs.p[certs.n - 1].cert = 0;
|
|
|
|
|
certs.p[certs.n - 1].key = key;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ProgramFile(const char *path, void program(const char *, size_t)) {
|
|
|
|
|
char *p;
|
|
|
|
|
size_t n;
|
|
|
|
|
DEBUGF("ProgramFile(%`'s)", path);
|
|
|
|
|
if ((p = xslurp(path, &n))) {
|
|
|
|
|
program(p, n);
|
|
|
|
|
mbedtls_platform_zeroize(p, n);
|
|
|
|
|
free(p);
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(stderr, "error: failed to read file: %s\n", path);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ProgramPort(long port) {
|
|
|
|
|
if (!(0 <= port && port <= 65535)) {
|
|
|
|
|
fprintf(stderr, "error: bad port: %d\n", port);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
if (port == 443) listeningonport443 = true;
|
|
|
|
|
ports.p = realloc(ports.p, ++ports.n * sizeof(*ports.p));
|
|
|
|
|
ports.p[ports.n - 1] = port;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-04 19:21:29 +00:00
|
|
|
|
static uint32_t ResolveIp(const char *addr) {
|
2021-06-24 19:31:26 +00:00
|
|
|
|
ssize_t rc;
|
2021-07-04 19:21:29 +00:00
|
|
|
|
uint32_t ip;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
struct addrinfo *ai = NULL;
|
|
|
|
|
struct addrinfo hint = {AI_NUMERICSERV, AF_INET, SOCK_STREAM, IPPROTO_TCP};
|
|
|
|
|
if ((rc = getaddrinfo(addr, "0", &hint, &ai)) != EAI_SUCCESS) {
|
|
|
|
|
fprintf(stderr, "error: bad addr: %s (EAI_%s)\n", addr, gai_strerror(rc));
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
2021-07-04 19:21:29 +00:00
|
|
|
|
ip = ntohl(ai->ai_addr4->sin_addr.s_addr);
|
2021-06-24 19:31:26 +00:00
|
|
|
|
freeaddrinfo(ai);
|
2021-07-04 19:21:29 +00:00
|
|
|
|
return ip;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ProgramAddr(const char *addr) {
|
|
|
|
|
uint32_t ip;
|
|
|
|
|
if (IsTiny()) {
|
|
|
|
|
ip = ParseIp(addr, -1);
|
|
|
|
|
} else {
|
|
|
|
|
ip = ResolveIp(addr);
|
|
|
|
|
}
|
|
|
|
|
ips.p = realloc(ips.p, ++ips.n * sizeof(*ips.p));
|
|
|
|
|
ips.p[ips.n - 1] = ip;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-14 12:36:58 +00:00
|
|
|
|
static void ProgramRedirect(int code, const char *sp, size_t sn, const char *dp,
|
|
|
|
|
size_t dn) {
|
2021-03-25 09:21:13 +00:00
|
|
|
|
long i, j;
|
|
|
|
|
struct Redirect r;
|
2021-03-29 08:22:49 +00:00
|
|
|
|
if (code && code != 301 && code != 302 && code != 307 && code != 308) {
|
|
|
|
|
fprintf(stderr, "error: unsupported redirect code %d\n", code);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
r.code = code;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
r.path.s = sp;
|
|
|
|
|
r.path.n = sn;
|
|
|
|
|
r.location.s = dp;
|
|
|
|
|
r.location.n = dn;
|
|
|
|
|
if ((i = FindRedirect(r.path.s, r.path.n)) != -1) {
|
2021-03-25 09:21:13 +00:00
|
|
|
|
redirects.p[i] = r;
|
|
|
|
|
} else {
|
|
|
|
|
i = redirects.n;
|
|
|
|
|
redirects.p = xrealloc(redirects.p, (i + 1) * sizeof(*redirects.p));
|
2021-04-02 01:51:12 +00:00
|
|
|
|
for (j = i; j; --j) {
|
2021-05-14 12:36:58 +00:00
|
|
|
|
if (CompareSlices(r.path.s, r.path.n, redirects.p[j - 1].path.s,
|
|
|
|
|
redirects.p[j - 1].path.n) < 0) {
|
2021-03-25 09:21:13 +00:00
|
|
|
|
redirects.p[j] = redirects.p[j - 1];
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
redirects.p[j] = r;
|
|
|
|
|
++redirects.n;
|
|
|
|
|
}
|
2020-09-07 04:39:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-14 12:36:58 +00:00
|
|
|
|
static void ProgramRedirectArg(int code, const char *s) {
|
|
|
|
|
size_t n;
|
2021-03-29 08:22:49 +00:00
|
|
|
|
const char *p;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
n = strlen(s);
|
|
|
|
|
if (!(p = memchr(s, '=', n))) {
|
2021-03-29 08:22:49 +00:00
|
|
|
|
fprintf(stderr, "error: redirect arg missing '='\n");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
2021-05-14 12:36:58 +00:00
|
|
|
|
ProgramRedirect(code, s, p - s, p + 1, n - (p - s + 1));
|
2021-03-29 08:22:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static void DescribeAddress(char buf[32], uint32_t addr, uint16_t port) {
|
|
|
|
|
char *p;
|
|
|
|
|
const char *s;
|
|
|
|
|
p = buf;
|
|
|
|
|
p += uint64toarray_radix10((addr & 0xFF000000) >> 030, p), *p++ = '.';
|
|
|
|
|
p += uint64toarray_radix10((addr & 0x00FF0000) >> 020, p), *p++ = '.';
|
|
|
|
|
p += uint64toarray_radix10((addr & 0x0000FF00) >> 010, p), *p++ = '.';
|
|
|
|
|
p += uint64toarray_radix10((addr & 0x000000FF) >> 000, p), *p++ = ':';
|
|
|
|
|
p += uint64toarray_radix10(port, p);
|
|
|
|
|
*p++ = '\0';
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-03 19:09:35 +00:00
|
|
|
|
static inline void GetServerAddr(uint32_t *ip, uint16_t *port) {
|
2021-06-24 19:31:26 +00:00
|
|
|
|
*ip = ntohl(serveraddr->sin_addr.s_addr);
|
|
|
|
|
if (port) *port = ntohs(serveraddr->sin_port);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-03 19:09:35 +00:00
|
|
|
|
static inline void GetClientAddr(uint32_t *ip, uint16_t *port) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
*ip = ntohl(clientaddr.sin_addr.s_addr);
|
|
|
|
|
if (port) *port = ntohs(clientaddr.sin_port);
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-03 19:09:35 +00:00
|
|
|
|
static inline void GetRemoteAddr(uint32_t *ip, uint16_t *port) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
GetClientAddr(ip, port);
|
|
|
|
|
if (HasHeader(kHttpXForwardedFor) &&
|
|
|
|
|
(IsPrivateIp(*ip) || IsLoopbackIp(*ip))) {
|
|
|
|
|
ParseForwarded(HeaderData(kHttpXForwardedFor),
|
|
|
|
|
HeaderLength(kHttpXForwardedFor), ip, port);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *DescribeClient(void) {
|
|
|
|
|
uint32_t ip;
|
|
|
|
|
uint16_t port;
|
|
|
|
|
static char clientaddrstr[32];
|
|
|
|
|
GetRemoteAddr(&ip, &port);
|
|
|
|
|
DescribeAddress(clientaddrstr, ip, port);
|
|
|
|
|
return clientaddrstr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *DescribeServer(void) {
|
|
|
|
|
uint32_t ip;
|
|
|
|
|
uint16_t port;
|
|
|
|
|
static char serveraddrstr[32];
|
|
|
|
|
GetServerAddr(&ip, &port);
|
|
|
|
|
DescribeAddress(serveraddrstr, ip, port);
|
|
|
|
|
return serveraddrstr;
|
2020-09-07 04:39:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-29 08:22:49 +00:00
|
|
|
|
static void ProgramBrand(const char *s) {
|
2021-05-05 14:25:39 +00:00
|
|
|
|
char *p;
|
2021-03-29 08:22:49 +00:00
|
|
|
|
free(brand);
|
|
|
|
|
free(serverheader);
|
2021-05-05 14:25:39 +00:00
|
|
|
|
if (!(p = EncodeHttpHeaderValue(s, -1, 0))) {
|
|
|
|
|
fprintf(stderr, "error: brand isn't latin1 encodable: %`'s", s);
|
2021-03-29 08:22:49 +00:00
|
|
|
|
exit(1);
|
|
|
|
|
}
|
2021-05-05 14:25:39 +00:00
|
|
|
|
brand = strdup(s);
|
|
|
|
|
serverheader = xasprintf("Server: %s\r\n", p);
|
|
|
|
|
free(p);
|
2021-03-29 08:22:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-07-08 22:56:23 +00:00
|
|
|
|
static void ProgramUid(long x) {
|
|
|
|
|
changeuid = x;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ProgramGid(long x) {
|
|
|
|
|
changegid = x;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static void ProgramTimeout(long ms) {
|
|
|
|
|
ldiv_t d;
|
2021-07-08 04:44:27 +00:00
|
|
|
|
if (ms < 0) {
|
|
|
|
|
timeout.tv_sec = ms; /* -(keepalive seconds) */
|
|
|
|
|
timeout.tv_usec = 0;
|
|
|
|
|
} else {
|
|
|
|
|
if (ms <= 30) {
|
|
|
|
|
fprintf(stderr, "error: timeout needs to be 31ms or greater\n");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
d = ldiv(ms, 1000);
|
|
|
|
|
timeout.tv_sec = d.quot;
|
|
|
|
|
timeout.tv_usec = d.rem * 1000;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-29 08:22:49 +00:00
|
|
|
|
static void ProgramCache(long x) {
|
|
|
|
|
cacheseconds = x;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
|
static void SetDefaults(void) {
|
2021-07-03 12:58:47 +00:00
|
|
|
|
ProgramBrand(REDBEAN "/1.4");
|
2021-05-01 12:28:15 +00:00
|
|
|
|
__log_level = kLogInfo;
|
2021-05-01 12:11:35 +00:00
|
|
|
|
maxpayloadsize = 64 * 1024;
|
2021-03-29 08:22:49 +00:00
|
|
|
|
ProgramCache(-1);
|
2021-06-24 19:31:26 +00:00
|
|
|
|
ProgramTimeout(60 * 1000);
|
2021-07-08 04:44:27 +00:00
|
|
|
|
sslfetchverify = true;
|
2021-03-29 08:22:49 +00:00
|
|
|
|
if (IsWindows()) uniprocess = true;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-14 12:36:58 +00:00
|
|
|
|
static void AddString(struct Strings *l, const char *s, size_t n) {
|
|
|
|
|
if (++l->n > l->c) {
|
|
|
|
|
l->c = l->n + (l->n >> 1);
|
2021-06-24 19:31:26 +00:00
|
|
|
|
l->p = realloc(l->p, l->c * sizeof(*l->p));
|
2021-05-14 12:36:58 +00:00
|
|
|
|
}
|
|
|
|
|
l->p[l->n - 1].s = s;
|
|
|
|
|
l->p[l->n - 1].n = n;
|
2021-03-29 09:39:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-14 12:36:58 +00:00
|
|
|
|
static bool HasString(struct Strings *l, const char *s, size_t n) {
|
2021-04-25 00:09:01 +00:00
|
|
|
|
size_t i;
|
|
|
|
|
for (i = 0; i < l->n; ++i) {
|
2021-05-14 12:36:58 +00:00
|
|
|
|
if (SlicesEqual(l->p[i].s, l->p[i].n, s, n)) {
|
2021-04-25 00:09:01 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-08 22:56:23 +00:00
|
|
|
|
static void ProgramDirectory(const char *path) {
|
|
|
|
|
char *s;
|
|
|
|
|
size_t n;
|
|
|
|
|
s = strdup(path);
|
|
|
|
|
n = strlen(s);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
while (n && (s[n - 1] == '/' || s[n - 1] == '\\')) s[--n] = 0;
|
|
|
|
|
if (!n || !isdirectory(s)) {
|
2021-04-18 18:34:59 +00:00
|
|
|
|
fprintf(stderr, "error: not a directory: %`'s\n", s);
|
2021-03-28 14:54:21 +00:00
|
|
|
|
exit(1);
|
|
|
|
|
}
|
2021-05-14 12:36:58 +00:00
|
|
|
|
AddString(&stagedirs, s, n);
|
2021-03-28 14:54:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static void ProgramHeader(const char *s) {
|
|
|
|
|
char *p, *v, *h;
|
|
|
|
|
if ((p = strchr(s, ':')) && IsValidHttpToken(s, p - s) &&
|
|
|
|
|
(v = EncodeLatin1(p + 1, -1, 0, kControlC0 | kControlC1 | kControlWs))) {
|
|
|
|
|
switch (GetHttpHeader(s, p - s)) {
|
|
|
|
|
case kHttpDate:
|
|
|
|
|
case kHttpConnection:
|
|
|
|
|
case kHttpContentLength:
|
|
|
|
|
case kHttpContentEncoding:
|
|
|
|
|
case kHttpContentRange:
|
|
|
|
|
fprintf(stderr, "error: can't program header: %`'s\n", s);
|
|
|
|
|
exit(1);
|
|
|
|
|
case kHttpServer:
|
|
|
|
|
ProgramBrand(p + 1);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
p = xasprintf("%s%.*s:%s\r\n", extrahdrs ? extrahdrs : "", p - s, s, v);
|
|
|
|
|
free(extrahdrs);
|
|
|
|
|
extrahdrs = p;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
free(v);
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(stderr, "error: illegal header: %`'s\n", s);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-08 22:56:23 +00:00
|
|
|
|
static void ProgramLogPath(const char *s) {
|
|
|
|
|
logpath = strdup(s);
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-24 19:31:26 +00:00
|
|
|
|
static bool IsServerFd(int fd) {
|
|
|
|
|
size_t i;
|
|
|
|
|
for (i = 0; i < servers.n; ++i) {
|
|
|
|
|
if (servers.p[i].fd == fd) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-08 22:56:23 +00:00
|
|
|
|
static void ChangeUser(void) {
|
|
|
|
|
if (changegid) LOGIFNEG1(setgid(changegid));
|
|
|
|
|
if (changeuid) LOGIFNEG1(setuid(changeuid));
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-06 06:11:49 +00:00
|
|
|
|
static void Daemonize(void) {
|
|
|
|
|
char ibuf[21];
|
|
|
|
|
int i, fd, pid;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
for (i = 0; i < 256; ++i) {
|
|
|
|
|
if (!IsServerFd(i)) {
|
2021-04-25 02:49:49 +00:00
|
|
|
|
close(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-02 08:38:11 +00:00
|
|
|
|
if ((pid = fork()) > 0) exit(0);
|
|
|
|
|
setsid();
|
|
|
|
|
if ((pid = fork()) > 0) _exit(0);
|
|
|
|
|
umask(0);
|
2020-10-06 06:11:49 +00:00
|
|
|
|
if (pidpath) {
|
2021-05-02 18:11:26 +00:00
|
|
|
|
fd = open(pidpath, O_CREAT | O_WRONLY, 0644);
|
2021-03-02 08:38:11 +00:00
|
|
|
|
write(fd, ibuf, uint64toarray_radix10(getpid(), ibuf));
|
|
|
|
|
close(fd);
|
2020-10-06 06:11:49 +00:00
|
|
|
|
}
|
|
|
|
|
if (!logpath) logpath = "/dev/null";
|
2021-05-14 12:36:58 +00:00
|
|
|
|
open("/dev/null", O_RDONLY);
|
|
|
|
|
open(logpath, O_APPEND | O_WRONLY | O_CREAT, 0640);
|
|
|
|
|
dup2(1, 2);
|
2021-07-08 22:56:23 +00:00
|
|
|
|
ChangeUser();
|
2020-09-07 04:39:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-18 18:34:59 +00:00
|
|
|
|
static void ReportWorkerExit(int pid, int ws) {
|
|
|
|
|
--shared->workers;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
if (WIFEXITED(ws)) {
|
|
|
|
|
if (WEXITSTATUS(ws)) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.failedchildren);
|
2021-04-18 18:34:59 +00:00
|
|
|
|
WARNF("%d exited with %d (%,d workers remain)", pid, WEXITSTATUS(ws),
|
|
|
|
|
shared->workers);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
} else {
|
2021-04-18 18:34:59 +00:00
|
|
|
|
DEBUGF("%d exited (%,d workers remain)", pid, shared->workers);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
} else {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.terminatedchildren);
|
2021-04-18 18:34:59 +00:00
|
|
|
|
WARNF("%d terminated with %s (%,d workers remain)", pid,
|
|
|
|
|
strsignal(WTERMSIG(ws)), shared->workers);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static void AppendResourceReport(struct rusage *ru, const char *nl) {
|
2021-04-18 18:34:59 +00:00
|
|
|
|
long utime, stime;
|
|
|
|
|
long double ticks;
|
|
|
|
|
if (ru->ru_maxrss) {
|
2021-04-24 20:58:34 +00:00
|
|
|
|
Append("ballooned to %,ldkb in size%s", ru->ru_maxrss, nl);
|
2021-04-18 18:34:59 +00:00
|
|
|
|
}
|
|
|
|
|
if ((utime = ru->ru_utime.tv_sec * 1000000 + ru->ru_utime.tv_usec) |
|
|
|
|
|
(stime = ru->ru_stime.tv_sec * 1000000 + ru->ru_stime.tv_usec)) {
|
|
|
|
|
ticks = ceill((long double)(utime + stime) / (1000000.L / CLK_TCK));
|
2021-04-24 20:58:34 +00:00
|
|
|
|
Append("needed %,ldµs cpu (%d%% kernel)%s", utime + stime,
|
|
|
|
|
(int)((long double)stime / (utime + stime) * 100), nl);
|
2021-04-18 18:34:59 +00:00
|
|
|
|
if (ru->ru_idrss) {
|
2021-04-24 20:58:34 +00:00
|
|
|
|
Append("needed %,ldkb memory on average%s", lroundl(ru->ru_idrss / ticks),
|
|
|
|
|
nl);
|
2021-04-18 18:34:59 +00:00
|
|
|
|
}
|
|
|
|
|
if (ru->ru_isrss) {
|
2021-04-24 20:58:34 +00:00
|
|
|
|
Append("needed %,ldkb stack on average%s", lroundl(ru->ru_isrss / ticks),
|
|
|
|
|
nl);
|
2021-04-18 18:34:59 +00:00
|
|
|
|
}
|
|
|
|
|
if (ru->ru_ixrss) {
|
2021-04-24 20:58:34 +00:00
|
|
|
|
Append("mapped %,ldkb shared on average%s", lroundl(ru->ru_ixrss / ticks),
|
|
|
|
|
nl);
|
2021-04-18 18:34:59 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (ru->ru_minflt || ru->ru_majflt) {
|
2021-04-24 20:58:34 +00:00
|
|
|
|
Append("caused %,ld page faults (%d%% memcpy)%s",
|
|
|
|
|
ru->ru_minflt + ru->ru_majflt,
|
|
|
|
|
(int)((long double)ru->ru_minflt / (ru->ru_minflt + ru->ru_majflt) *
|
|
|
|
|
100),
|
|
|
|
|
nl);
|
2021-04-18 18:34:59 +00:00
|
|
|
|
}
|
|
|
|
|
if (ru->ru_nvcsw + ru->ru_nivcsw > 1) {
|
2021-04-24 20:58:34 +00:00
|
|
|
|
Append(
|
2021-04-23 17:45:19 +00:00
|
|
|
|
"%,ld context switches (%d%% consensual)%s",
|
|
|
|
|
ru->ru_nvcsw + ru->ru_nivcsw,
|
|
|
|
|
(int)((long double)ru->ru_nvcsw / (ru->ru_nvcsw + ru->ru_nivcsw) * 100),
|
|
|
|
|
nl);
|
2021-04-18 18:34:59 +00:00
|
|
|
|
}
|
|
|
|
|
if (ru->ru_msgrcv || ru->ru_msgsnd) {
|
2021-04-25 02:49:49 +00:00
|
|
|
|
Append("received %,ld message%s and sent %,ld%s", ru->ru_msgrcv,
|
|
|
|
|
ru->ru_msgrcv == 1 ? "" : "s", ru->ru_msgsnd, nl);
|
|
|
|
|
}
|
|
|
|
|
if (ru->ru_inblock || ru->ru_oublock) {
|
|
|
|
|
Append("performed %,ld read%s and %,ld write i/o operations%s",
|
|
|
|
|
ru->ru_inblock, ru->ru_inblock == 1 ? "" : "s", ru->ru_oublock, nl);
|
2021-04-18 18:34:59 +00:00
|
|
|
|
}
|
|
|
|
|
if (ru->ru_nsignals) {
|
2021-04-24 20:58:34 +00:00
|
|
|
|
Append("received %,ld signals%s", ru->ru_nsignals, nl);
|
2021-04-18 18:34:59 +00:00
|
|
|
|
}
|
|
|
|
|
if (ru->ru_nswap) {
|
2021-04-24 20:58:34 +00:00
|
|
|
|
Append("got swapped %,ld times%s", ru->ru_nswap, nl);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void AddTimeval(struct timeval *x, const struct timeval *y) {
|
|
|
|
|
x->tv_sec += y->tv_sec;
|
|
|
|
|
x->tv_usec += y->tv_usec;
|
|
|
|
|
if (x->tv_usec >= 1000000) {
|
|
|
|
|
x->tv_usec -= 1000000;
|
|
|
|
|
x->tv_sec += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void AddRusage(struct rusage *x, const struct rusage *y) {
|
|
|
|
|
AddTimeval(&x->ru_utime, &y->ru_utime);
|
|
|
|
|
AddTimeval(&x->ru_stime, &y->ru_stime);
|
|
|
|
|
x->ru_maxrss = MAX(x->ru_maxrss, y->ru_maxrss);
|
|
|
|
|
x->ru_ixrss += y->ru_ixrss;
|
|
|
|
|
x->ru_idrss += y->ru_idrss;
|
|
|
|
|
x->ru_isrss += y->ru_isrss;
|
|
|
|
|
x->ru_minflt += y->ru_minflt;
|
|
|
|
|
x->ru_majflt += y->ru_majflt;
|
|
|
|
|
x->ru_nswap += y->ru_nswap;
|
|
|
|
|
x->ru_inblock += y->ru_inblock;
|
|
|
|
|
x->ru_oublock += y->ru_oublock;
|
|
|
|
|
x->ru_msgsnd += y->ru_msgsnd;
|
|
|
|
|
x->ru_msgrcv += y->ru_msgrcv;
|
|
|
|
|
x->ru_nsignals += y->ru_nsignals;
|
|
|
|
|
x->ru_nvcsw += y->ru_nvcsw;
|
|
|
|
|
x->ru_nivcsw += y->ru_nivcsw;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ReportWorkerResources(int pid, struct rusage *ru) {
|
|
|
|
|
const char *s;
|
|
|
|
|
if (logrusage || LOGGABLE(kLogDebug)) {
|
|
|
|
|
AppendResourceReport(ru, "\n");
|
|
|
|
|
if (outbuf.n) {
|
|
|
|
|
if ((s = IndentLines(outbuf.p, outbuf.n - 1, 0, 1))) {
|
2021-05-03 08:21:50 +00:00
|
|
|
|
flogf(kLogInfo, __FILE__, __LINE__, NULL,
|
|
|
|
|
"resource report for pid %d\n%s", pid, s);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
free(s);
|
|
|
|
|
}
|
|
|
|
|
ClearOutput();
|
|
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
2020-10-06 06:11:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static void HandleWorkerExit(int pid, int ws, struct rusage *ru) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.connectionshandled);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
AddRusage(&shared->children, ru);
|
|
|
|
|
ReportWorkerExit(pid, ws);
|
|
|
|
|
ReportWorkerResources(pid, ru);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-06 06:11:49 +00:00
|
|
|
|
static void WaitAll(void) {
|
2021-03-25 09:21:13 +00:00
|
|
|
|
int ws, pid;
|
2021-04-18 18:34:59 +00:00
|
|
|
|
struct rusage ru;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
for (;;) {
|
2021-04-18 18:34:59 +00:00
|
|
|
|
if ((pid = wait4(-1, &ws, 0, &ru)) != -1) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
HandleWorkerExit(pid, ws, &ru);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
} else {
|
|
|
|
|
if (errno == ECHILD) break;
|
|
|
|
|
if (errno == EINTR) {
|
|
|
|
|
if (killed) {
|
2021-03-29 08:22:49 +00:00
|
|
|
|
killed = false;
|
|
|
|
|
terminated = false;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
WARNF("redbean shall terminate harder");
|
2021-03-25 09:21:13 +00:00
|
|
|
|
LOGIFNEG1(kill(0, SIGTERM));
|
|
|
|
|
}
|
|
|
|
|
continue;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
}
|
2021-06-24 19:31:26 +00:00
|
|
|
|
FATALF("wait error %s", strerror(errno));
|
2020-10-06 06:11:49 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
|
static void ReapZombies(void) {
|
|
|
|
|
int ws, pid;
|
2021-04-18 18:34:59 +00:00
|
|
|
|
struct rusage ru;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
do {
|
2021-04-18 18:34:59 +00:00
|
|
|
|
zombied = false;
|
|
|
|
|
if ((pid = wait4(-1, &ws, WNOHANG, &ru)) != -1) {
|
2021-03-25 09:21:13 +00:00
|
|
|
|
if (pid) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
HandleWorkerExit(pid, ws, &ru);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (errno == ECHILD) break;
|
|
|
|
|
if (errno == EINTR) continue;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
FATALF("wait error %s", strerror(errno));
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
} while (!terminated);
|
2020-10-06 06:11:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-05 14:25:39 +00:00
|
|
|
|
static ssize_t WritevAll(int fd, struct iovec *iov, int iovlen) {
|
2020-09-07 04:39:00 +00:00
|
|
|
|
ssize_t rc;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
size_t wrote;
|
2020-09-07 04:39:00 +00:00
|
|
|
|
do {
|
2020-10-06 06:11:49 +00:00
|
|
|
|
if ((rc = writev(fd, iov, iovlen)) != -1) {
|
2020-09-07 04:39:00 +00:00
|
|
|
|
wrote = rc;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
do {
|
|
|
|
|
if (wrote >= iov->iov_len) {
|
|
|
|
|
wrote -= iov->iov_len;
|
|
|
|
|
++iov;
|
|
|
|
|
--iovlen;
|
|
|
|
|
} else {
|
|
|
|
|
iov->iov_base = (char *)iov->iov_base + wrote;
|
|
|
|
|
iov->iov_len -= wrote;
|
|
|
|
|
wrote = 0;
|
|
|
|
|
}
|
|
|
|
|
} while (wrote);
|
|
|
|
|
} else if (errno == EINTR) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.writeinterruputs);
|
2021-03-28 14:54:21 +00:00
|
|
|
|
if (killed || (meltdown && nowl() - startread > 2)) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2020-09-07 04:39:00 +00:00
|
|
|
|
} else {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2020-10-06 06:11:49 +00:00
|
|
|
|
} while (iovlen);
|
2020-09-07 04:39:00 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-08 04:44:27 +00:00
|
|
|
|
static int TlsRecvImpl(void *ctx, unsigned char *buf, size_t len,
|
|
|
|
|
uint32_t tmo) {
|
2021-06-24 19:31:26 +00:00
|
|
|
|
int rc;
|
2021-07-08 04:44:27 +00:00
|
|
|
|
while ((rc = read(*(int *)ctx, buf, len)) == -1) {
|
2021-06-24 19:31:26 +00:00
|
|
|
|
if (errno == EINTR) {
|
|
|
|
|
return MBEDTLS_ERR_SSL_WANT_READ;
|
|
|
|
|
} else if (errno == EAGAIN) {
|
|
|
|
|
return MBEDTLS_ERR_SSL_TIMEOUT;
|
|
|
|
|
} else if (errno == EPIPE || errno == ECONNRESET || errno == ENETRESET) {
|
|
|
|
|
return MBEDTLS_ERR_NET_CONN_RESET;
|
|
|
|
|
} else {
|
2021-07-08 04:44:27 +00:00
|
|
|
|
WARNF("tls read() error %s", strerror(errno));
|
2021-06-24 19:31:26 +00:00
|
|
|
|
return MBEDTLS_ERR_NET_RECV_FAILED;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-08 04:44:27 +00:00
|
|
|
|
static int TlsRecv(void *ctx, unsigned char *buf, size_t len, uint32_t tmo) {
|
|
|
|
|
int rc;
|
|
|
|
|
if (oldin.n) {
|
|
|
|
|
rc = MIN(oldin.n, len);
|
|
|
|
|
memcpy(buf, oldin.p, rc);
|
|
|
|
|
oldin.p += rc;
|
|
|
|
|
oldin.n -= rc;
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
return TlsRecvImpl(ctx, buf, len, tmo);
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-24 19:31:26 +00:00
|
|
|
|
static void TlsDebug(void *ctx, int level, const char *file, int line,
|
|
|
|
|
const char *message) {
|
|
|
|
|
flogf(level, file, line, 0, "TLS %s", message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int TlsSend(void *ctx, const unsigned char *buf, size_t len) {
|
|
|
|
|
int rc;
|
2021-07-08 04:44:27 +00:00
|
|
|
|
while ((rc = write(*(int *)ctx, buf, len)) == -1) {
|
2021-06-24 19:31:26 +00:00
|
|
|
|
if (errno == EINTR) {
|
|
|
|
|
LockInc(&shared->c.writeinterruputs);
|
|
|
|
|
if (killed || (meltdown && nowl() - startread > 2)) {
|
|
|
|
|
return MBEDTLS_ERR_NET_CONN_RESET;
|
|
|
|
|
}
|
|
|
|
|
} else if (errno == EAGAIN) {
|
|
|
|
|
return MBEDTLS_ERR_SSL_TIMEOUT;
|
|
|
|
|
} else if (errno == EPIPE || errno == ECONNRESET || errno == ENETRESET) {
|
|
|
|
|
return MBEDTLS_ERR_NET_CONN_RESET;
|
|
|
|
|
} else {
|
2021-07-08 04:44:27 +00:00
|
|
|
|
WARNF("TlsSend error %s", strerror(errno));
|
2021-06-24 19:31:26 +00:00
|
|
|
|
return MBEDTLS_ERR_NET_SEND_FAILED;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ssize_t SslRead(int fd, void *buf, size_t size) {
|
|
|
|
|
int rc;
|
|
|
|
|
rc = mbedtls_ssl_read(&ssl, buf, size);
|
|
|
|
|
if (!rc) {
|
|
|
|
|
errno = ECONNRESET;
|
|
|
|
|
rc = -1;
|
|
|
|
|
} else if (rc < 0) {
|
|
|
|
|
if (rc == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
|
|
|
|
|
rc = 0;
|
|
|
|
|
} else if (rc == MBEDTLS_ERR_NET_CONN_RESET ||
|
|
|
|
|
rc == MBEDTLS_ERR_SSL_TIMEOUT) {
|
|
|
|
|
errno = ECONNRESET;
|
|
|
|
|
rc = -1;
|
|
|
|
|
} else if (rc == MBEDTLS_ERR_SSL_WANT_READ) {
|
|
|
|
|
errno = EINTR;
|
|
|
|
|
rc = -1;
|
|
|
|
|
} else {
|
|
|
|
|
WARNF("%s SslRead error -0x%04x", DescribeClient(), -rc);
|
|
|
|
|
errno = EIO;
|
|
|
|
|
rc = -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ssize_t SslWrite(int fd, struct iovec *iov, int iovlen) {
|
|
|
|
|
int i;
|
|
|
|
|
size_t n;
|
|
|
|
|
ssize_t rc;
|
|
|
|
|
const unsigned char *p;
|
|
|
|
|
for (i = 0; i < iovlen; ++i) {
|
|
|
|
|
p = iov[i].iov_base;
|
|
|
|
|
n = iov[i].iov_len;
|
|
|
|
|
while (n) {
|
|
|
|
|
if ((rc = mbedtls_ssl_write(&ssl, p, n)) > 0) {
|
|
|
|
|
p += rc;
|
|
|
|
|
n -= rc;
|
|
|
|
|
} else {
|
|
|
|
|
WARNF("%s SslWrite error -0x%04x", DescribeClient(), -rc);
|
|
|
|
|
errno = EIO;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-08 04:44:27 +00:00
|
|
|
|
static void NotifyClose(void) {
|
|
|
|
|
#ifndef UNSECURE
|
|
|
|
|
if (usessl) {
|
|
|
|
|
DEBUGF("SSL notifying close");
|
|
|
|
|
mbedtls_ssl_close_notify(&ssl);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-24 19:31:26 +00:00
|
|
|
|
static bool TlsSetup(void) {
|
|
|
|
|
int r;
|
|
|
|
|
oldin.p = inbuf.p;
|
|
|
|
|
oldin.n = amtread;
|
|
|
|
|
inbuf.p += amtread;
|
|
|
|
|
inbuf.n -= amtread;
|
|
|
|
|
inbuf.c = amtread;
|
|
|
|
|
amtread = 0;
|
|
|
|
|
for (;;) {
|
|
|
|
|
if (!(r = mbedtls_ssl_handshake(&ssl))) {
|
|
|
|
|
LockInc(&shared->c.sslhandshakes);
|
|
|
|
|
usessl = true;
|
|
|
|
|
reader = SslRead;
|
|
|
|
|
writer = SslWrite;
|
2021-07-08 04:44:27 +00:00
|
|
|
|
VERBOSEF("SHAKEN %s %s %s", DescribeClient(),
|
|
|
|
|
mbedtls_ssl_get_ciphersuite(&ssl),
|
|
|
|
|
mbedtls_ssl_get_version(&ssl));
|
2021-06-24 19:31:26 +00:00
|
|
|
|
return true;
|
|
|
|
|
} else if (r == MBEDTLS_ERR_SSL_WANT_READ) {
|
|
|
|
|
LockInc(&shared->c.handshakeinterrupts);
|
|
|
|
|
if (terminated || killed || (meltdown && nowl() - startread > 2)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
LockInc(&shared->c.sslhandshakefails);
|
|
|
|
|
switch (r) {
|
|
|
|
|
case MBEDTLS_ERR_SSL_CONN_EOF:
|
|
|
|
|
DEBUGF("%s SSL handshake EOF", DescribeClient());
|
|
|
|
|
return false;
|
|
|
|
|
case MBEDTLS_ERR_NET_CONN_RESET:
|
|
|
|
|
DEBUGF("%s SSL handshake reset", DescribeClient());
|
|
|
|
|
return false;
|
|
|
|
|
case MBEDTLS_ERR_SSL_TIMEOUT:
|
|
|
|
|
LockInc(&shared->c.ssltimeouts);
|
2021-07-08 04:44:27 +00:00
|
|
|
|
DEBUGF("%s %s", DescribeClient(), "ssltimeouts");
|
2021-06-24 19:31:26 +00:00
|
|
|
|
return false;
|
|
|
|
|
case MBEDTLS_ERR_SSL_NO_CIPHER_CHOSEN:
|
|
|
|
|
LockInc(&shared->c.sslnociphers);
|
2021-07-08 04:44:27 +00:00
|
|
|
|
WARNF("%s %s", DescribeClient(), "sslnociphers");
|
2021-06-24 19:31:26 +00:00
|
|
|
|
return false;
|
|
|
|
|
case MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE:
|
|
|
|
|
LockInc(&shared->c.sslcantciphers);
|
2021-07-08 04:44:27 +00:00
|
|
|
|
WARNF("%s %s", DescribeClient(), "sslcantciphers");
|
2021-06-24 19:31:26 +00:00
|
|
|
|
return false;
|
2021-06-24 22:22:24 +00:00
|
|
|
|
case MBEDTLS_ERR_SSL_BAD_HS_PROTOCOL_VERSION:
|
|
|
|
|
LockInc(&shared->c.sslnoversion);
|
2021-07-08 04:44:27 +00:00
|
|
|
|
WARNF("%s %s", DescribeClient(), "sslnoversion");
|
2021-06-24 22:22:24 +00:00
|
|
|
|
return false;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
case MBEDTLS_ERR_SSL_INVALID_MAC:
|
|
|
|
|
LockInc(&shared->c.sslshakemacs);
|
2021-07-08 04:44:27 +00:00
|
|
|
|
WARNF("%s %s", DescribeClient(), "sslshakemacs");
|
|
|
|
|
return false;
|
|
|
|
|
case MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE:
|
|
|
|
|
LockInc(&shared->c.sslnoclientcert);
|
|
|
|
|
WARNF("%s %s", DescribeClient(), "sslnoclientcert");
|
|
|
|
|
NotifyClose();
|
|
|
|
|
return false;
|
|
|
|
|
case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED:
|
|
|
|
|
LockInc(&shared->c.sslverifyfailed);
|
|
|
|
|
WARNF("%s SSL %s", DescribeClient(),
|
|
|
|
|
gc(DescribeSslVerifyFailure(
|
|
|
|
|
ssl.session_negotiate->verify_result)));
|
2021-06-24 19:31:26 +00:00
|
|
|
|
return false;
|
|
|
|
|
case MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE:
|
|
|
|
|
switch (ssl.fatal_alert) {
|
|
|
|
|
case MBEDTLS_SSL_ALERT_MSG_CERT_UNKNOWN:
|
|
|
|
|
LockInc(&shared->c.sslunknowncert);
|
2021-07-08 04:44:27 +00:00
|
|
|
|
DEBUGF("%s %s", DescribeClient(), "sslunknowncert");
|
2021-06-24 19:31:26 +00:00
|
|
|
|
return false;
|
|
|
|
|
case MBEDTLS_SSL_ALERT_MSG_UNKNOWN_CA:
|
|
|
|
|
LockInc(&shared->c.sslunknownca);
|
2021-07-08 04:44:27 +00:00
|
|
|
|
DEBUGF("%s %s", DescribeClient(), "sslunknownca");
|
2021-06-24 19:31:26 +00:00
|
|
|
|
return false;
|
|
|
|
|
default:
|
2021-07-06 20:39:18 +00:00
|
|
|
|
WARNF("%s SSL shakealert %s", DescribeClient(),
|
|
|
|
|
GetAlertDescription(ssl.fatal_alert));
|
2021-06-24 19:31:26 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
WARNF("%s SSL handshake failed -0x%04x", DescribeClient(), -r);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-03 12:51:04 +00:00
|
|
|
|
static int GetEntropy(void *c, unsigned char *p, size_t n) {
|
|
|
|
|
CHECK_EQ(n, getrandom(p, n, 0));
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-24 19:31:26 +00:00
|
|
|
|
static void InitializeRng(mbedtls_ctr_drbg_context *r) {
|
|
|
|
|
volatile unsigned char b[64];
|
|
|
|
|
mbedtls_ctr_drbg_init(r);
|
|
|
|
|
CHECK(getrandom(b, 64, 0) == 64);
|
2021-07-03 12:51:04 +00:00
|
|
|
|
CHECK(!mbedtls_ctr_drbg_seed(r, GetEntropy, 0, b, 64));
|
2021-06-24 19:31:26 +00:00
|
|
|
|
mbedtls_platform_zeroize(b, 64);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void GenerateSerial(mbedtls_x509write_cert *wcert,
|
|
|
|
|
mbedtls_ctr_drbg_context *kr) {
|
|
|
|
|
mbedtls_mpi x;
|
|
|
|
|
mbedtls_mpi_init(&x);
|
|
|
|
|
mbedtls_mpi_fill_random(&x, 128 / 8, mbedtls_ctr_drbg_random, kr);
|
|
|
|
|
mbedtls_x509write_crt_set_serial(wcert, &x);
|
|
|
|
|
mbedtls_mpi_free(&x);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ChooseCertificateLifetime(char notbefore[16], char notafter[16]) {
|
|
|
|
|
struct tm tm;
|
|
|
|
|
int64_t past, now, future, lifetime, tolerance;
|
|
|
|
|
tolerance = 60 * 60 * 24;
|
|
|
|
|
lifetime = 60 * 60 * 24 * 365;
|
|
|
|
|
now = nowl();
|
|
|
|
|
past = now - tolerance;
|
|
|
|
|
future = now + tolerance + lifetime;
|
2021-07-08 04:44:27 +00:00
|
|
|
|
FormatSslTime(notbefore, gmtime_r(&past, &tm));
|
|
|
|
|
FormatSslTime(notafter, gmtime_r(&future, &tm));
|
2021-06-24 19:31:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ConfigureCertificate(mbedtls_x509write_cert *cw, struct Cert *ca,
|
|
|
|
|
int usage, int type) {
|
|
|
|
|
int r;
|
|
|
|
|
const char *s;
|
|
|
|
|
bool isduplicate;
|
|
|
|
|
size_t i, j, k, nsan;
|
|
|
|
|
struct HostsTxt *htxt;
|
|
|
|
|
struct mbedtls_san *san;
|
|
|
|
|
const mbedtls_x509_name *xname;
|
|
|
|
|
char *name, *subject, *issuer, notbefore[16], notafter[16], hbuf[256];
|
|
|
|
|
san = 0;
|
|
|
|
|
nsan = 0;
|
|
|
|
|
name = 0;
|
|
|
|
|
htxt = GetHostsTxt();
|
|
|
|
|
strcpy(hbuf, "localhost");
|
|
|
|
|
gethostname(hbuf, sizeof(hbuf));
|
|
|
|
|
for (i = 0; i < htxt->entries.i; ++i) {
|
|
|
|
|
for (j = 0; j < ips.n; ++j) {
|
|
|
|
|
if (ips.p[j] == READ32BE(htxt->entries.p[i].ip)) {
|
|
|
|
|
isduplicate = false;
|
|
|
|
|
s = htxt->strings.p + htxt->entries.p[i].name;
|
|
|
|
|
if (!name) name = s;
|
|
|
|
|
for (k = 0; k < nsan; ++k) {
|
|
|
|
|
if (san[k].tag == MBEDTLS_X509_SAN_DNS_NAME &&
|
|
|
|
|
!strcasecmp(s, san[k].val)) {
|
|
|
|
|
isduplicate = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!isduplicate) {
|
|
|
|
|
san = realloc(san, ++nsan * sizeof(*san));
|
|
|
|
|
san[nsan - 1].tag = MBEDTLS_X509_SAN_DNS_NAME;
|
|
|
|
|
san[nsan - 1].val = s;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (i = 0; i < ips.n; ++i) {
|
|
|
|
|
san = realloc(san, ++nsan * sizeof(*san));
|
|
|
|
|
san[nsan - 1].tag = MBEDTLS_X509_SAN_IP_ADDRESS;
|
|
|
|
|
san[nsan - 1].ip4 = ips.p[i];
|
|
|
|
|
}
|
|
|
|
|
ChooseCertificateLifetime(notbefore, notafter);
|
|
|
|
|
subject = xasprintf("CN=%s", name ? name : hbuf);
|
|
|
|
|
if (ca) {
|
|
|
|
|
issuer = calloc(1, 1000);
|
|
|
|
|
CHECK_GT(mbedtls_x509_dn_gets(issuer, 1000, &ca->cert->subject), 0);
|
|
|
|
|
} else {
|
|
|
|
|
issuer = strdup(subject);
|
|
|
|
|
}
|
|
|
|
|
if ((r = mbedtls_x509write_crt_set_subject_alternative_name(cw, san, nsan)) ||
|
|
|
|
|
(r = mbedtls_x509write_crt_set_validity(cw, notbefore, notafter)) ||
|
|
|
|
|
(r = mbedtls_x509write_crt_set_basic_constraints(cw, false, -1)) ||
|
|
|
|
|
(r = mbedtls_x509write_crt_set_subject_key_identifier(cw)) ||
|
|
|
|
|
(r = mbedtls_x509write_crt_set_authority_key_identifier(cw)) ||
|
|
|
|
|
(r = mbedtls_x509write_crt_set_key_usage(cw, usage)) ||
|
|
|
|
|
(r = mbedtls_x509write_crt_set_ext_key_usage(cw, type)) ||
|
|
|
|
|
(r = mbedtls_x509write_crt_set_subject_name(cw, subject)) ||
|
|
|
|
|
(r = mbedtls_x509write_crt_set_issuer_name(cw, issuer))) {
|
|
|
|
|
fprintf(stderr, "error: configure certificate (grep -0x%04x)\n", -r);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
free(subject);
|
|
|
|
|
free(issuer);
|
|
|
|
|
free(san);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct Cert *GetKeySigningKey(void) {
|
|
|
|
|
size_t i;
|
|
|
|
|
for (i = 0; i < certs.n; ++i) {
|
|
|
|
|
if (!certs.p[i].key) 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,
|
|
|
|
|
MBEDTLS_X509_KU_KEY_CERT_SIGN)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
return certs.p + i;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-08 04:44:27 +00:00
|
|
|
|
static void WipeKeySigningKeys(void) {
|
|
|
|
|
size_t i;
|
|
|
|
|
for (i = 0; i < certs.n; ++i) {
|
|
|
|
|
if (!certs.p[i].key) continue;
|
|
|
|
|
if (!certs.p[i].cert) continue;
|
|
|
|
|
if (!certs.p[i].cert->ca_istrue) continue;
|
|
|
|
|
mbedtls_pk_free(certs.p[i].key);
|
|
|
|
|
certs.p[i].key = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static mbedtls_pk_context *InitializeKey(struct Cert *ca,
|
|
|
|
|
mbedtls_x509write_cert *wcert,
|
|
|
|
|
int type) {
|
|
|
|
|
mbedtls_pk_context *k;
|
|
|
|
|
mbedtls_ctr_drbg_context kr;
|
|
|
|
|
k = calloc(1, sizeof(mbedtls_pk_context));
|
|
|
|
|
mbedtls_x509write_crt_init(wcert);
|
|
|
|
|
mbedtls_x509write_crt_set_issuer_key(wcert, ca ? ca->key : k);
|
|
|
|
|
mbedtls_x509write_crt_set_subject_key(wcert, k);
|
|
|
|
|
mbedtls_x509write_crt_set_md_alg(
|
|
|
|
|
wcert, suiteb ? MBEDTLS_MD_SHA384 : MBEDTLS_MD_SHA256);
|
|
|
|
|
mbedtls_x509write_crt_set_version(wcert, MBEDTLS_X509_CRT_VERSION_3);
|
|
|
|
|
CHECK_EQ(0, mbedtls_pk_setup(k, mbedtls_pk_info_from_type(type)));
|
|
|
|
|
return k;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct Cert FinishCertificate(struct Cert *ca,
|
|
|
|
|
mbedtls_x509write_cert *wcert,
|
|
|
|
|
mbedtls_ctr_drbg_context *kr,
|
|
|
|
|
mbedtls_pk_context *key) {
|
|
|
|
|
int i, n, rc;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
unsigned char *p;
|
|
|
|
|
mbedtls_x509_crt *cert;
|
2021-07-08 04:44:27 +00:00
|
|
|
|
p = malloc((n = FRAMESIZE));
|
|
|
|
|
i = mbedtls_x509write_crt_der(wcert, p, n, mbedtls_ctr_drbg_random, kr);
|
|
|
|
|
if (i < 0) {
|
|
|
|
|
fprintf(stderr, "error: write key (grep -0x%04x)\n", -i);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
cert = calloc(1, sizeof(mbedtls_x509_crt));
|
|
|
|
|
mbedtls_x509_crt_parse_der(cert, p + n - i, i);
|
|
|
|
|
if (ca) cert->next = ca->cert;
|
|
|
|
|
mbedtls_x509write_crt_free(wcert);
|
|
|
|
|
mbedtls_ctr_drbg_free(kr);
|
|
|
|
|
free(p);
|
|
|
|
|
if ((rc = mbedtls_pk_check_pair(&cert->pk, key))) {
|
|
|
|
|
fprintf(stderr, "error: generate key (grep -0x%04x)\n", -rc);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
LogCertificate("generated certificate", cert);
|
|
|
|
|
UseCertificate(cert, key);
|
|
|
|
|
return (struct Cert){cert, key};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct Cert GenerateEcpCertificate(struct Cert *ca) {
|
2021-06-24 19:31:26 +00:00
|
|
|
|
mbedtls_pk_context *key;
|
|
|
|
|
mbedtls_ctr_drbg_context kr;
|
|
|
|
|
mbedtls_x509write_cert wcert;
|
|
|
|
|
InitializeRng(&kr);
|
2021-07-08 04:44:27 +00:00
|
|
|
|
key = InitializeKey(ca, &wcert, MBEDTLS_PK_ECKEY);
|
2021-06-24 19:31:26 +00:00
|
|
|
|
CHECK_EQ(0, mbedtls_ecp_gen_key(
|
|
|
|
|
suiteb ? MBEDTLS_ECP_DP_SECP384R1 : MBEDTLS_ECP_DP_SECP256R1,
|
|
|
|
|
mbedtls_pk_ec(*key), mbedtls_ctr_drbg_random, &kr));
|
|
|
|
|
GenerateSerial(&wcert, &kr);
|
|
|
|
|
ConfigureCertificate(&wcert, ca, MBEDTLS_X509_KU_DIGITAL_SIGNATURE,
|
2021-07-08 04:44:27 +00:00
|
|
|
|
MBEDTLS_X509_NS_CERT_TYPE_SSL_SERVER |
|
|
|
|
|
MBEDTLS_X509_NS_CERT_TYPE_SSL_CLIENT);
|
|
|
|
|
return FinishCertificate(ca, &wcert, &kr, key);
|
2021-06-24 19:31:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-06-24 22:22:24 +00:00
|
|
|
|
static struct Cert GenerateRsaCertificate(struct Cert *ca) {
|
2021-06-24 19:31:26 +00:00
|
|
|
|
mbedtls_pk_context *key;
|
|
|
|
|
mbedtls_ctr_drbg_context kr;
|
|
|
|
|
mbedtls_x509write_cert wcert;
|
|
|
|
|
InitializeRng(&kr);
|
2021-07-08 04:44:27 +00:00
|
|
|
|
key = InitializeKey(ca, &wcert, MBEDTLS_PK_RSA);
|
2021-06-24 19:31:26 +00:00
|
|
|
|
CHECK_EQ(0, mbedtls_rsa_gen_key(mbedtls_pk_rsa(*key), mbedtls_ctr_drbg_random,
|
|
|
|
|
&kr, suiteb ? 4096 : 2048, 65537));
|
|
|
|
|
GenerateSerial(&wcert, &kr);
|
|
|
|
|
ConfigureCertificate(
|
|
|
|
|
&wcert, ca,
|
|
|
|
|
MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_KEY_ENCIPHERMENT,
|
|
|
|
|
MBEDTLS_X509_NS_CERT_TYPE_SSL_SERVER |
|
|
|
|
|
MBEDTLS_X509_NS_CERT_TYPE_SSL_CLIENT);
|
2021-07-08 04:44:27 +00:00
|
|
|
|
return FinishCertificate(ca, &wcert, &kr, key);
|
2021-06-24 19:31:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void LoadCertificates(void) {
|
|
|
|
|
size_t i;
|
2021-06-24 22:22:24 +00:00
|
|
|
|
struct Cert *ksk, ecp, rsa;
|
2021-07-08 04:44:27 +00:00
|
|
|
|
bool havecert, haveclientcert;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
havecert = false;
|
2021-07-08 04:44:27 +00:00
|
|
|
|
haveclientcert = false;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
for (i = 0; i < certs.n; ++i) {
|
|
|
|
|
if (certs.p[i].key && certs.p[i].cert && !certs.p[i].cert->ca_istrue &&
|
|
|
|
|
!mbedtls_x509_crt_check_key_usage(certs.p[i].cert,
|
2021-07-08 04:44:27 +00:00
|
|
|
|
MBEDTLS_X509_KU_DIGITAL_SIGNATURE)) {
|
|
|
|
|
if (!mbedtls_x509_crt_check_extended_key_usage(
|
|
|
|
|
certs.p[i].cert, MBEDTLS_OID_SERVER_AUTH,
|
|
|
|
|
MBEDTLS_OID_SIZE(MBEDTLS_OID_SERVER_AUTH))) {
|
|
|
|
|
LogCertificate("using server certificate", certs.p[i].cert);
|
|
|
|
|
UseCertificate(certs.p[i].cert, certs.p[i].key);
|
|
|
|
|
havecert = true;
|
|
|
|
|
}
|
|
|
|
|
if (!mbedtls_x509_crt_check_extended_key_usage(
|
|
|
|
|
certs.p[i].cert, MBEDTLS_OID_CLIENT_AUTH,
|
|
|
|
|
MBEDTLS_OID_SIZE(MBEDTLS_OID_CLIENT_AUTH))) {
|
|
|
|
|
LogCertificate("using client certificate", certs.p[i].cert);
|
|
|
|
|
CHECK_EQ(0, mbedtls_ssl_conf_own_cert(&confcli, certs.p[i].cert,
|
|
|
|
|
certs.p[i].key));
|
|
|
|
|
haveclientcert = true;
|
|
|
|
|
}
|
2021-06-24 19:31:26 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!havecert) {
|
|
|
|
|
if ((ksk = GetKeySigningKey())) {
|
|
|
|
|
DEBUGF("generating ssl certificates using %`'s",
|
|
|
|
|
gc(FormatX509Name(&ksk->cert->subject)));
|
|
|
|
|
} else {
|
|
|
|
|
VERBOSEF("could not find non-CA SSL certificate key pair with"
|
|
|
|
|
" -addext keyUsage=digitalSignature"
|
|
|
|
|
" -addext extendedKeyUsage=serverAuth");
|
|
|
|
|
VERBOSEF("could not find CA key signing key pair with"
|
|
|
|
|
" -addext keyUsage=keyCertSign");
|
2021-07-03 17:16:38 +00:00
|
|
|
|
LOGF("generating self-signed ssl certificates");
|
2021-06-24 19:31:26 +00:00
|
|
|
|
}
|
|
|
|
|
#ifdef MBEDTLS_ECP_C
|
2021-06-24 22:22:24 +00:00
|
|
|
|
ecp = GenerateEcpCertificate(ksk);
|
2021-07-08 04:44:27 +00:00
|
|
|
|
if (!haveclientcert) {
|
|
|
|
|
CHECK_EQ(0, mbedtls_ssl_conf_own_cert(&confcli, ecp.cert, ecp.key));
|
|
|
|
|
}
|
2021-06-24 22:22:24 +00:00
|
|
|
|
#endif
|
|
|
|
|
#ifdef MBEDTLS_RSA_C
|
|
|
|
|
rsa = GenerateRsaCertificate(ksk);
|
2021-07-08 04:44:27 +00:00
|
|
|
|
if (!haveclientcert) {
|
|
|
|
|
CHECK_EQ(0, mbedtls_ssl_conf_own_cert(&confcli, rsa.cert, rsa.key));
|
|
|
|
|
}
|
2021-06-24 22:22:24 +00:00
|
|
|
|
#endif
|
|
|
|
|
#ifdef MBEDTLS_ECP_C
|
|
|
|
|
certs.p = realloc(certs.p, ++certs.n * sizeof(*certs.p));
|
|
|
|
|
certs.p[certs.n - 1] = ecp;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
#endif
|
|
|
|
|
#ifdef MBEDTLS_RSA_C
|
2021-06-24 22:22:24 +00:00
|
|
|
|
certs.p = realloc(certs.p, ++certs.n * sizeof(*certs.p));
|
|
|
|
|
certs.p[certs.n - 1] = rsa;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
#endif
|
|
|
|
|
}
|
2021-07-08 04:44:27 +00:00
|
|
|
|
WipeKeySigningKeys();
|
2021-06-24 19:31:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void LoadSslRoots(void) {
|
2021-07-08 04:44:27 +00:00
|
|
|
|
InternCertificate(GetSslRoots(), 0);
|
2021-06-24 19:31:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
|
static bool ClientAcceptsGzip(void) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
return msg.version >= 10 && /* RFC1945 § 3.5 */
|
2021-04-23 17:45:19 +00:00
|
|
|
|
HeaderHas(&msg, inbuf.p, kHttpAcceptEncoding, "gzip", 4);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void UpdateCurrentDate(long double now) {
|
|
|
|
|
int64_t t;
|
|
|
|
|
struct tm tm;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
t = now;
|
|
|
|
|
shared->nowish = now;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
gmtime_r(&t, &tm);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
FormatHttpDateTime(shared->currentdate, &tm);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static int64_t GetGmtOffset(int64_t t) {
|
2020-10-06 06:11:49 +00:00
|
|
|
|
struct tm tm;
|
|
|
|
|
localtime_r(&t, &tm);
|
|
|
|
|
return tm.tm_gmtoff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int64_t LocoTimeToZulu(int64_t x) {
|
|
|
|
|
return x - gmtoff;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-18 18:34:59 +00:00
|
|
|
|
static int64_t GetZipCfileLastModified(const uint8_t *zcf) {
|
2021-03-25 09:21:13 +00:00
|
|
|
|
const uint8_t *p, *pe;
|
2021-04-18 18:34:59 +00:00
|
|
|
|
for (p = ZIP_CFILE_EXTRA(zcf), pe = p + ZIP_CFILE_EXTRASIZE(zcf); p + 4 <= pe;
|
|
|
|
|
p += ZIP_EXTRA_SIZE(p)) {
|
|
|
|
|
if (ZIP_EXTRA_HEADERID(p) == kZipExtraNtfs &&
|
2021-05-14 12:36:58 +00:00
|
|
|
|
ZIP_EXTRA_CONTENTSIZE(p) >= 4 + 4 + 8 &&
|
2021-04-18 18:34:59 +00:00
|
|
|
|
READ16LE(ZIP_EXTRA_CONTENT(p) + 4) == 1 &&
|
2021-05-14 12:36:58 +00:00
|
|
|
|
READ16LE(ZIP_EXTRA_CONTENT(p) + 6) >= 8) {
|
2021-04-18 18:34:59 +00:00
|
|
|
|
return READ64LE(ZIP_EXTRA_CONTENT(p) + 8) / HECTONANOSECONDS -
|
2021-05-14 12:36:58 +00:00
|
|
|
|
MODERNITYSECONDS; /* TODO(jart): update access time */
|
2021-04-18 18:34:59 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (p = ZIP_CFILE_EXTRA(zcf), pe = p + ZIP_CFILE_EXTRASIZE(zcf); p + 4 <= pe;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
p += ZIP_EXTRA_SIZE(p)) {
|
2021-04-18 18:34:59 +00:00
|
|
|
|
if (ZIP_EXTRA_HEADERID(p) == kZipExtraExtendedTimestamp &&
|
|
|
|
|
ZIP_EXTRA_CONTENTSIZE(p) >= 1 + 4 && (*ZIP_EXTRA_CONTENT(p) & 1)) {
|
|
|
|
|
return (int32_t)READ32LE(ZIP_EXTRA_CONTENT(p) + 1);
|
2020-10-06 06:11:49 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-18 18:34:59 +00:00
|
|
|
|
for (p = ZIP_CFILE_EXTRA(zcf), pe = p + ZIP_CFILE_EXTRASIZE(zcf); p + 4 <= pe;
|
|
|
|
|
p += ZIP_EXTRA_SIZE(p)) {
|
|
|
|
|
if (ZIP_EXTRA_HEADERID(p) == kZipExtraUnix &&
|
|
|
|
|
ZIP_EXTRA_CONTENTSIZE(p) >= 4 + 4) {
|
|
|
|
|
return (int32_t)READ32LE(ZIP_EXTRA_CONTENT(p) + 4);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return LocoTimeToZulu(DosDateTimeToUnix(ZIP_CFILE_LASTMODIFIEDDATE(zcf),
|
|
|
|
|
ZIP_CFILE_LASTMODIFIEDTIME(zcf)));
|
2020-10-06 06:11:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-14 12:36:58 +00:00
|
|
|
|
static int64_t GetZipCfileCreation(const uint8_t *zcf) {
|
|
|
|
|
const uint8_t *p, *pe;
|
|
|
|
|
for (p = ZIP_CFILE_EXTRA(zcf), pe = p + ZIP_CFILE_EXTRASIZE(zcf); p + 4 <= pe;
|
|
|
|
|
p += ZIP_EXTRA_SIZE(p)) {
|
|
|
|
|
if (ZIP_EXTRA_HEADERID(p) == kZipExtraNtfs &&
|
|
|
|
|
ZIP_EXTRA_CONTENTSIZE(p) >= 4 + 4 + 8 * 3 &&
|
|
|
|
|
READ16LE(ZIP_EXTRA_CONTENT(p) + 4) == 1 &&
|
|
|
|
|
READ16LE(ZIP_EXTRA_CONTENT(p) + 6) >= 24) {
|
|
|
|
|
return READ64LE(ZIP_EXTRA_CONTENT(p) + 8 + 8 + 8) / HECTONANOSECONDS -
|
|
|
|
|
MODERNITYSECONDS;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (p = ZIP_CFILE_EXTRA(zcf), pe = p + ZIP_CFILE_EXTRASIZE(zcf); p + 4 <= pe;
|
|
|
|
|
p += ZIP_EXTRA_SIZE(p)) {
|
|
|
|
|
if (ZIP_EXTRA_HEADERID(p) == kZipExtraExtendedTimestamp &&
|
|
|
|
|
ZIP_EXTRA_CONTENTSIZE(p) >= 1 && (*ZIP_EXTRA_CONTENT(p) & 4) &&
|
|
|
|
|
ZIP_EXTRA_CONTENTSIZE(p) >=
|
|
|
|
|
1 + popcnt((*ZIP_EXTRA_CONTENT(p) & 7)) * 4) {
|
|
|
|
|
return (int32_t)READ32LE(ZIP_EXTRA_CONTENT(p) + 1 +
|
|
|
|
|
popcnt((*ZIP_EXTRA_CONTENT(p) & 3)) * 4);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return GetZipCfileLastModified(zcf);
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-03 19:09:35 +00:00
|
|
|
|
forceinline bool IsCompressed(struct Asset *a) {
|
2021-03-28 14:54:21 +00:00
|
|
|
|
return !a->file &&
|
2021-05-14 12:36:58 +00:00
|
|
|
|
ZIP_LFILE_COMPRESSIONMETHOD(zbase + a->lf) == kZipCompressionDeflate;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-03 19:09:35 +00:00
|
|
|
|
forceinline int GetMode(struct Asset *a) {
|
2021-05-14 12:36:58 +00:00
|
|
|
|
return a->file ? a->file->st.st_mode : GetZipCfileMode(zbase + a->cf);
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-10-06 06:11:49 +00:00
|
|
|
|
static char *FormatUnixHttpDateTime(char *s, int64_t t) {
|
|
|
|
|
struct tm tm;
|
|
|
|
|
gmtime_r(&t, &tm);
|
|
|
|
|
FormatHttpDateTime(s, &tm);
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-03 19:09:35 +00:00
|
|
|
|
forceinline bool IsCompressionMethodSupported(int method) {
|
2021-03-29 08:22:49 +00:00
|
|
|
|
return method == kZipCompressionNone || method == kZipCompressionDeflate;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-05 14:25:39 +00:00
|
|
|
|
static inline unsigned Hash(const void *p, unsigned long n) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
unsigned h, i;
|
|
|
|
|
for (h = i = 0; i < n; i++) {
|
|
|
|
|
h += ((unsigned char *)p)[i];
|
|
|
|
|
h *= 0x9e3779b1;
|
|
|
|
|
}
|
|
|
|
|
return MAX(1, h);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-27 14:29:55 +00:00
|
|
|
|
static void IndexAssets(void) {
|
2020-10-06 06:11:49 +00:00
|
|
|
|
int64_t lm;
|
2021-05-03 08:21:50 +00:00
|
|
|
|
uint64_t cf;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
struct Asset *p;
|
2021-04-18 18:34:59 +00:00
|
|
|
|
uint32_t i, n, m, step, hash;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
DEBUGF("indexing assets (inode %#lx)", zst.st_ino);
|
2021-03-27 14:29:55 +00:00
|
|
|
|
CHECK_GE(HASH_LOAD_FACTOR, 2);
|
2021-05-02 18:50:43 +00:00
|
|
|
|
CHECK(READ32LE(zcdir) == kZipCdir64HdrMagic ||
|
|
|
|
|
READ32LE(zcdir) == kZipCdirHdrMagic);
|
|
|
|
|
n = GetZipCdirRecords(zcdir);
|
2020-10-06 06:11:49 +00:00
|
|
|
|
m = roundup2pow(MAX(1, n) * HASH_LOAD_FACTOR);
|
2021-03-27 14:29:55 +00:00
|
|
|
|
p = xcalloc(m, sizeof(struct Asset));
|
2021-05-14 12:36:58 +00:00
|
|
|
|
for (cf = GetZipCdirOffset(zcdir); n--; cf += ZIP_CFILE_HDRSIZE(zbase + cf)) {
|
|
|
|
|
CHECK_EQ(kZipCfileHdrMagic, ZIP_CFILE_MAGIC(zbase + cf));
|
|
|
|
|
if (!IsCompressionMethodSupported(
|
|
|
|
|
ZIP_CFILE_COMPRESSIONMETHOD(zbase + cf))) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
LOGF("don't understand zip compression method %d used by %`'.*s",
|
2021-05-14 12:36:58 +00:00
|
|
|
|
ZIP_CFILE_COMPRESSIONMETHOD(zbase + cf),
|
|
|
|
|
ZIP_CFILE_NAMESIZE(zbase + cf), ZIP_CFILE_NAME(zbase + cf));
|
2021-03-29 08:22:49 +00:00
|
|
|
|
continue;
|
|
|
|
|
}
|
2021-05-14 12:36:58 +00:00
|
|
|
|
hash = Hash(ZIP_CFILE_NAME(zbase + cf), ZIP_CFILE_NAMESIZE(zbase + cf));
|
2021-03-27 14:29:55 +00:00
|
|
|
|
step = 0;
|
|
|
|
|
do {
|
|
|
|
|
i = (hash + (step * (step + 1)) >> 1) & (m - 1);
|
|
|
|
|
++step;
|
|
|
|
|
} while (p[i].hash);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
lm = GetZipCfileLastModified(zbase + cf);
|
2021-03-27 14:29:55 +00:00
|
|
|
|
p[i].hash = hash;
|
2021-04-18 18:34:59 +00:00
|
|
|
|
p[i].cf = cf;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
p[i].lf = GetZipCfileOffset(zbase + cf);
|
|
|
|
|
p[i].istext = !!(ZIP_CFILE_INTERNALATTRIBUTES(zbase + cf) & kZipIattrText);
|
2021-03-27 14:29:55 +00:00
|
|
|
|
p[i].lastmodified = lm;
|
|
|
|
|
p[i].lastmodifiedstr = FormatUnixHttpDateTime(xmalloc(30), lm);
|
|
|
|
|
}
|
|
|
|
|
assets.p = p;
|
|
|
|
|
assets.n = m;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-14 12:36:58 +00:00
|
|
|
|
static bool OpenZip(bool force) {
|
|
|
|
|
int fd;
|
|
|
|
|
size_t n;
|
2021-05-03 19:09:35 +00:00
|
|
|
|
struct stat st;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
uint8_t *m, *b, *d, *p;
|
|
|
|
|
if (stat(zpath, &st) != -1) {
|
|
|
|
|
if (force || st.st_ino != zst.st_ino || st.st_size > zst.st_size) {
|
|
|
|
|
if (st.st_ino == zst.st_ino) {
|
|
|
|
|
fd = zfd;
|
|
|
|
|
} else if ((fd = open(zpath, O_RDWR)) == -1) {
|
|
|
|
|
WARNF("open() failed w/ %m");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if ((m = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) !=
|
|
|
|
|
MAP_FAILED) {
|
|
|
|
|
n = st.st_size;
|
|
|
|
|
if (endswith(zpath, ".com.dbg") && (p = memmem(m, n, "MZqFpD", 6))) {
|
|
|
|
|
b = p;
|
|
|
|
|
n -= p - m;
|
|
|
|
|
} else {
|
|
|
|
|
b = m;
|
|
|
|
|
}
|
|
|
|
|
if ((d = GetZipCdir(b, n))) {
|
|
|
|
|
if (zmap) {
|
|
|
|
|
LOGIFNEG1(munmap(zmap, zbase + zsize - zmap));
|
|
|
|
|
}
|
|
|
|
|
zmap = m;
|
|
|
|
|
zbase = b;
|
|
|
|
|
zsize = n;
|
|
|
|
|
zcdir = d;
|
|
|
|
|
DCHECK(IsZipCdir32(zbase, zsize, zcdir - zbase) ||
|
|
|
|
|
IsZipCdir64(zbase, zsize, zcdir - zbase));
|
|
|
|
|
memcpy(&zst, &st, sizeof(st));
|
|
|
|
|
IndexAssets();
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
WARNF("couldn't locate central directory");
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
WARNF("mmap() failed w/ %m");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
WARNF("stat() failed w/ %m");
|
2021-05-03 19:09:35 +00:00
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-21 02:14:21 +00:00
|
|
|
|
static struct Asset *GetAssetZip(const char *path, size_t pathlen) {
|
2020-10-06 06:11:49 +00:00
|
|
|
|
uint32_t i, step, hash;
|
2021-04-21 02:14:21 +00:00
|
|
|
|
if (pathlen > 1 && path[0] == '/') ++path, --pathlen;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
hash = Hash(path, pathlen);
|
|
|
|
|
for (step = 0;; ++step) {
|
|
|
|
|
i = (hash + (step * (step + 1)) >> 1) & (assets.n - 1);
|
|
|
|
|
if (!assets.p[i].hash) return NULL;
|
|
|
|
|
if (hash == assets.p[i].hash &&
|
2021-05-14 12:36:58 +00:00
|
|
|
|
pathlen == ZIP_CFILE_NAMESIZE(zbase + assets.p[i].cf) &&
|
|
|
|
|
memcmp(path, ZIP_CFILE_NAME(zbase + assets.p[i].cf), pathlen) == 0) {
|
2020-10-06 06:11:49 +00:00
|
|
|
|
return &assets.p[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-21 02:14:21 +00:00
|
|
|
|
static struct Asset *GetAssetFile(const char *path, size_t pathlen) {
|
2021-03-28 14:54:21 +00:00
|
|
|
|
size_t i;
|
|
|
|
|
struct Asset *a;
|
|
|
|
|
if (stagedirs.n) {
|
|
|
|
|
a = FreeLater(xcalloc(1, sizeof(struct Asset)));
|
|
|
|
|
a->file = FreeLater(xmalloc(sizeof(struct File)));
|
|
|
|
|
for (i = 0; i < stagedirs.n; ++i) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.stats);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
a->file->path.s = FreeLater(MergePaths(stagedirs.p[i].s, stagedirs.p[i].n,
|
|
|
|
|
path, pathlen, &a->file->path.n));
|
|
|
|
|
if (stat(a->file->path.s, &a->file->st) != -1) {
|
2021-03-28 14:54:21 +00:00
|
|
|
|
a->lastmodifiedstr = FormatUnixHttpDateTime(
|
|
|
|
|
FreeLater(xmalloc(30)),
|
|
|
|
|
(a->lastmodified = a->file->st.st_mtim.tv_sec));
|
|
|
|
|
return a;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
} else {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.statfails);
|
2021-03-28 14:54:21 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
2021-03-28 14:54:21 +00:00
|
|
|
|
return NULL;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-21 02:14:21 +00:00
|
|
|
|
static struct Asset *GetAsset(const char *path, size_t pathlen) {
|
|
|
|
|
char *path2;
|
2021-03-28 14:54:21 +00:00
|
|
|
|
struct Asset *a;
|
2021-04-21 02:14:21 +00:00
|
|
|
|
if (!(a = GetAssetFile(path, pathlen))) {
|
|
|
|
|
if (!(a = GetAssetZip(path, pathlen))) {
|
2021-04-24 20:58:34 +00:00
|
|
|
|
if (pathlen > 1 && path[pathlen - 1] != '/' &&
|
|
|
|
|
pathlen + 1 <= sizeof(slashpath)) {
|
|
|
|
|
memcpy(mempcpy(slashpath, path, pathlen), "/", 1);
|
|
|
|
|
a = GetAssetZip(slashpath, pathlen + 1);
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-28 14:54:21 +00:00
|
|
|
|
}
|
|
|
|
|
return a;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *AppendHeader(char *p, const char *k, const char *v) {
|
2021-03-28 07:10:17 +00:00
|
|
|
|
if (!v) return p;
|
2021-04-06 19:46:52 +00:00
|
|
|
|
return AppendCrlf(stpcpy(stpcpy(stpcpy(p, k), ": "), v));
|
2020-10-06 06:11:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *AppendContentType(char *p, const char *ct) {
|
2021-04-06 19:46:52 +00:00
|
|
|
|
p = stpcpy(p, "Content-Type: ");
|
2020-10-06 06:11:49 +00:00
|
|
|
|
p = stpcpy(p, ct);
|
2021-04-18 18:34:59 +00:00
|
|
|
|
if (startswith(ct, "text/")) {
|
2021-03-25 09:21:13 +00:00
|
|
|
|
istext = true;
|
2021-04-18 18:34:59 +00:00
|
|
|
|
if (!strchr(ct, ';')) {
|
|
|
|
|
p = stpcpy(p, "; charset=utf-8");
|
|
|
|
|
}
|
2020-10-06 06:11:49 +00:00
|
|
|
|
}
|
|
|
|
|
return AppendCrlf(p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *AppendExpires(char *p, int64_t t) {
|
|
|
|
|
struct tm tm;
|
|
|
|
|
gmtime_r(&t, &tm);
|
2021-04-06 19:46:52 +00:00
|
|
|
|
p = stpcpy(p, "Expires: ");
|
2021-03-28 14:54:21 +00:00
|
|
|
|
p = FormatHttpDateTime(p, &tm);
|
2020-10-06 06:11:49 +00:00
|
|
|
|
return AppendCrlf(p);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
|
static char *AppendCache(char *p, int64_t seconds) {
|
|
|
|
|
if (seconds < 0) return p;
|
2021-04-18 18:34:59 +00:00
|
|
|
|
p = stpcpy(p, "Cache-Control: max-age=");
|
2021-03-25 09:21:13 +00:00
|
|
|
|
p += uint64toarray_radix10(seconds, p);
|
2021-04-18 18:34:59 +00:00
|
|
|
|
if (seconds) {
|
|
|
|
|
p = stpcpy(p, ", public");
|
|
|
|
|
} else {
|
|
|
|
|
p = stpcpy(p, ", no-store");
|
|
|
|
|
}
|
2020-10-06 06:11:49 +00:00
|
|
|
|
p = AppendCrlf(p);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
return AppendExpires(p, (int64_t)shared->nowish + seconds);
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-03 19:09:35 +00:00
|
|
|
|
static inline char *AppendContentLength(char *p, size_t n) {
|
2021-04-06 19:46:52 +00:00
|
|
|
|
p = stpcpy(p, "Content-Length: ");
|
2020-10-06 06:11:49 +00:00
|
|
|
|
p += uint64toarray_radix10(n, p);
|
|
|
|
|
return AppendCrlf(p);
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-21 02:14:21 +00:00
|
|
|
|
static char *AppendContentRange(char *p, long a, long b, long c) {
|
2021-04-06 19:46:52 +00:00
|
|
|
|
p = stpcpy(p, "Content-Range: bytes ");
|
2021-04-21 02:14:21 +00:00
|
|
|
|
if (a >= 0 && b > 0) {
|
|
|
|
|
p += uint64toarray_radix10(a, p);
|
|
|
|
|
*p++ = '-';
|
|
|
|
|
p += uint64toarray_radix10(a + b - 1, p);
|
|
|
|
|
} else {
|
|
|
|
|
*p++ = '*';
|
|
|
|
|
}
|
2020-10-06 06:11:49 +00:00
|
|
|
|
*p++ = '/';
|
2021-04-21 02:14:21 +00:00
|
|
|
|
p += uint64toarray_radix10(c, p);
|
2020-10-06 06:11:49 +00:00
|
|
|
|
return AppendCrlf(p);
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-18 18:34:59 +00:00
|
|
|
|
static bool Inflate(void *dp, size_t dn, const void *sp, size_t sn) {
|
2021-05-14 12:36:58 +00:00
|
|
|
|
int rc;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
z_stream zs;
|
2021-07-04 19:21:29 +00:00
|
|
|
|
struct DeflateState ds;
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.inflates);
|
2021-07-04 19:21:29 +00:00
|
|
|
|
if (IsTiny()) {
|
|
|
|
|
return undeflate(dp, dn, sp, sn, &ds) != -1;
|
|
|
|
|
} else {
|
|
|
|
|
zs.next_in = sp;
|
|
|
|
|
zs.avail_in = sn;
|
|
|
|
|
zs.total_in = sn;
|
|
|
|
|
zs.next_out = dp;
|
|
|
|
|
zs.avail_out = dn;
|
|
|
|
|
zs.total_out = dn;
|
|
|
|
|
zs.zfree = Z_NULL;
|
|
|
|
|
zs.zalloc = Z_NULL;
|
|
|
|
|
CHECK_EQ(Z_OK, inflateInit2(&zs, -MAX_WBITS));
|
|
|
|
|
switch ((rc = inflate(&zs, Z_NO_FLUSH))) {
|
|
|
|
|
case Z_STREAM_END:
|
|
|
|
|
CHECK_EQ(Z_OK, inflateEnd(&zs));
|
|
|
|
|
return true;
|
|
|
|
|
case Z_DATA_ERROR:
|
|
|
|
|
inflateEnd(&zs);
|
|
|
|
|
WARNF("Z_DATA_ERROR");
|
|
|
|
|
return false;
|
|
|
|
|
case Z_NEED_DICT:
|
|
|
|
|
inflateEnd(&zs);
|
|
|
|
|
WARNF("Z_NEED_DICT");
|
|
|
|
|
return false;
|
|
|
|
|
case Z_MEM_ERROR:
|
|
|
|
|
FATALF("Z_MEM_ERROR");
|
|
|
|
|
default:
|
|
|
|
|
FATALF("inflate()→%d dn=%ld sn=%ld "
|
|
|
|
|
"next_in=%ld avail_in=%ld next_out=%ld avail_out=%ld",
|
|
|
|
|
rc, dn, sn, (char *)zs.next_in - (char *)sp, zs.avail_in,
|
|
|
|
|
(char *)zs.next_out - (char *)dp, zs.avail_out);
|
|
|
|
|
}
|
2021-04-18 18:34:59 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool Verify(void *data, size_t size, uint32_t crc) {
|
|
|
|
|
uint32_t got;
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.verifies);
|
2021-04-18 18:34:59 +00:00
|
|
|
|
if (crc == (got = crc32_z(0, data, size))) {
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.thiscorruption);
|
2021-04-18 18:34:59 +00:00
|
|
|
|
WARNF("corrupt zip file at %`'.*s had crc 0x%08x but expected 0x%08x",
|
|
|
|
|
msg.uri.b - msg.uri.a, inbuf.p + msg.uri.a, got, crc);
|
|
|
|
|
return false;
|
2020-09-07 04:39:00 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
|
static void *Deflate(const void *data, size_t size, size_t *out_size) {
|
|
|
|
|
void *res;
|
|
|
|
|
z_stream zs;
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.deflates);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
CHECK_EQ(Z_OK, deflateInit2(memset(&zs, 0, sizeof(zs)), 4, Z_DEFLATED,
|
|
|
|
|
-MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY));
|
|
|
|
|
zs.next_in = data;
|
|
|
|
|
zs.avail_in = size;
|
|
|
|
|
zs.avail_out = compressBound(size);
|
|
|
|
|
zs.next_out = res = xmalloc(zs.avail_out);
|
|
|
|
|
CHECK_EQ(Z_STREAM_END, deflate(&zs, Z_FINISH));
|
|
|
|
|
CHECK_EQ(Z_OK, deflateEnd(&zs));
|
|
|
|
|
*out_size = zs.total_out;
|
|
|
|
|
return xrealloc(res, zs.total_out);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void *LoadAsset(struct Asset *a, size_t *out_size) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
int mode;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
size_t size;
|
|
|
|
|
uint8_t *data;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
if (S_ISDIR(GetMode(a))) {
|
|
|
|
|
WARNF("can't load directory");
|
2021-04-21 02:14:21 +00:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2021-04-23 17:45:19 +00:00
|
|
|
|
if (!a->file) {
|
2021-05-14 12:36:58 +00:00
|
|
|
|
size = GetZipLfileUncompressedSize(zbase + a->lf);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
if (size == SIZE_MAX || !(data = malloc(size + 1))) return NULL;
|
|
|
|
|
if (IsCompressed(a)) {
|
2021-05-14 12:36:58 +00:00
|
|
|
|
if (!Inflate(data, size, ZIP_LFILE_CONTENT(zbase + a->lf),
|
|
|
|
|
GetZipCfileCompressedSize(zbase + a->cf))) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
free(data);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2021-05-14 12:36:58 +00:00
|
|
|
|
memcpy(data, ZIP_LFILE_CONTENT(zbase + a->lf), size);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
}
|
2021-05-14 12:36:58 +00:00
|
|
|
|
if (!Verify(data, size, ZIP_LFILE_CRC32(zbase + a->lf))) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
free(data);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
data[size] = '\0';
|
|
|
|
|
if (out_size) *out_size = size;
|
|
|
|
|
return data;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
} else {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.slurps);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
return xslurp(a->file->path.s, out_size);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static wontreturn void PrintUsage(FILE *f, int rc) {
|
|
|
|
|
size_t n;
|
|
|
|
|
const char *p;
|
|
|
|
|
struct Asset *a;
|
2021-07-04 19:21:29 +00:00
|
|
|
|
if ((a = GetAssetZip("/help.txt", 10)) && (p = LoadAsset(a, &n))) {
|
2021-05-14 12:36:58 +00:00
|
|
|
|
fwrite(p, 1, n, f);
|
|
|
|
|
free(p);
|
|
|
|
|
}
|
|
|
|
|
exit(rc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void GetOpts(int argc, char *argv[]) {
|
|
|
|
|
int opt;
|
|
|
|
|
while ((opt = getopt(argc, argv,
|
2021-07-08 04:44:27 +00:00
|
|
|
|
"jkazhdugvVsmbfyl:p:r:R:H:c:L:P:U:G:BD:t:M:C:K:F:")) !=
|
2021-06-24 19:31:26 +00:00
|
|
|
|
-1) {
|
2021-05-14 12:36:58 +00:00
|
|
|
|
switch (opt) {
|
|
|
|
|
case 'v':
|
|
|
|
|
__log_level++;
|
|
|
|
|
break;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
case 'V':
|
|
|
|
|
mbedtls_debug_threshold++;
|
|
|
|
|
break;
|
|
|
|
|
case 'y':
|
|
|
|
|
dontupgradeinsecurerequests = true;
|
|
|
|
|
break;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
case 's':
|
|
|
|
|
__log_level--;
|
|
|
|
|
break;
|
|
|
|
|
case 'd':
|
|
|
|
|
daemonize = true;
|
|
|
|
|
break;
|
|
|
|
|
case 'a':
|
|
|
|
|
logrusage = true;
|
|
|
|
|
break;
|
|
|
|
|
case 'u':
|
|
|
|
|
uniprocess = true;
|
|
|
|
|
break;
|
|
|
|
|
case 'g':
|
|
|
|
|
loglatency = true;
|
|
|
|
|
break;
|
|
|
|
|
case 'm':
|
|
|
|
|
logmessages = true;
|
|
|
|
|
break;
|
|
|
|
|
case 'b':
|
|
|
|
|
logbodies = true;
|
|
|
|
|
break;
|
|
|
|
|
case 'z':
|
|
|
|
|
printport = true;
|
|
|
|
|
break;
|
|
|
|
|
case 'f':
|
|
|
|
|
funtrace = true;
|
|
|
|
|
break;
|
2021-07-08 04:44:27 +00:00
|
|
|
|
case 'j':
|
|
|
|
|
sslclientverify = true;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
break;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
case 'k':
|
2021-07-08 04:44:27 +00:00
|
|
|
|
sslfetchverify = false;
|
|
|
|
|
break;
|
|
|
|
|
case 'B':
|
|
|
|
|
suiteb = true;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
break;
|
|
|
|
|
case 't':
|
|
|
|
|
ProgramTimeout(strtol(optarg, NULL, 0));
|
|
|
|
|
break;
|
|
|
|
|
case 'r':
|
|
|
|
|
ProgramRedirectArg(307, optarg);
|
|
|
|
|
break;
|
|
|
|
|
case 'R':
|
|
|
|
|
ProgramRedirectArg(0, optarg);
|
|
|
|
|
break;
|
|
|
|
|
case 'D':
|
2021-07-08 22:56:23 +00:00
|
|
|
|
ProgramDirectory(optarg);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
break;
|
|
|
|
|
case 'c':
|
|
|
|
|
ProgramCache(strtol(optarg, NULL, 0));
|
|
|
|
|
break;
|
|
|
|
|
case 'p':
|
|
|
|
|
ProgramPort(strtol(optarg, NULL, 0));
|
|
|
|
|
break;
|
|
|
|
|
case 'M':
|
|
|
|
|
maxpayloadsize = atoi(optarg);
|
|
|
|
|
maxpayloadsize = MAX(1450, maxpayloadsize);
|
|
|
|
|
break;
|
|
|
|
|
case 'l':
|
2021-06-24 19:31:26 +00:00
|
|
|
|
ProgramAddr(optarg);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
break;
|
|
|
|
|
case 'H':
|
|
|
|
|
ProgramHeader(optarg);
|
|
|
|
|
break;
|
|
|
|
|
case 'L':
|
2021-07-08 22:56:23 +00:00
|
|
|
|
ProgramLogPath(optarg);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
break;
|
|
|
|
|
case 'P':
|
|
|
|
|
pidpath = optarg;
|
|
|
|
|
break;
|
|
|
|
|
case 'U':
|
2021-07-08 22:56:23 +00:00
|
|
|
|
ProgramUid(atoi(optarg));
|
2021-05-14 12:36:58 +00:00
|
|
|
|
break;
|
|
|
|
|
case 'G':
|
2021-07-08 22:56:23 +00:00
|
|
|
|
ProgramGid(atoi(optarg));
|
2021-05-14 12:36:58 +00:00
|
|
|
|
break;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
#ifndef UNSECURE
|
|
|
|
|
case 'C':
|
|
|
|
|
ProgramFile(optarg, ProgramCertificate);
|
|
|
|
|
break;
|
|
|
|
|
case 'K':
|
|
|
|
|
ProgramFile(optarg, ProgramPrivateKey);
|
|
|
|
|
break;
|
|
|
|
|
#endif
|
2021-05-14 12:36:58 +00:00
|
|
|
|
case 'h':
|
|
|
|
|
PrintUsage(stdout, EXIT_SUCCESS);
|
|
|
|
|
default:
|
|
|
|
|
PrintUsage(stderr, EX_USAGE);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-06-24 19:31:26 +00:00
|
|
|
|
if (!!keypath ^ !!certpath) {
|
|
|
|
|
fprintf(stderr, "error: the -C and -K flags need to be passed together\n");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
2020-10-06 06:11:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-21 02:14:21 +00:00
|
|
|
|
static void AppendLogo(void) {
|
|
|
|
|
size_t n;
|
|
|
|
|
char *p, *q;
|
|
|
|
|
struct Asset *a;
|
|
|
|
|
if ((a = GetAsset("/redbean.png", 12)) && (p = LoadAsset(a, &n))) {
|
|
|
|
|
q = EncodeBase64(p, n, &n);
|
2021-05-02 18:11:26 +00:00
|
|
|
|
Append("<img alt=\"[logo]\" src=\"data:image/png;base64,");
|
2021-04-21 02:14:21 +00:00
|
|
|
|
AppendData(q, n);
|
2021-04-24 20:58:34 +00:00
|
|
|
|
Append("\">\r\n");
|
2021-04-21 02:14:21 +00:00
|
|
|
|
free(q);
|
|
|
|
|
free(p);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-24 19:31:26 +00:00
|
|
|
|
static ssize_t Send(struct iovec *iov, int iovlen) {
|
2021-03-25 09:21:13 +00:00
|
|
|
|
ssize_t rc;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
if ((rc = writer(client, iov, iovlen)) == -1) {
|
2021-03-25 09:21:13 +00:00
|
|
|
|
if (errno == ECONNRESET) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.writeresets);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
DEBUGF("%s write reset", DescribeClient());
|
|
|
|
|
} else if (errno == EAGAIN) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.writetimeouts);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
WARNF("%s write timeout", DescribeClient());
|
2021-03-25 09:21:13 +00:00
|
|
|
|
} else {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.writeerrors);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
WARNF("%s write error %s", DescribeClient(), strerror(errno));
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
connectionclose = true;
|
|
|
|
|
}
|
|
|
|
|
return rc;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-21 02:14:21 +00:00
|
|
|
|
static char *CommitOutput(char *p) {
|
2021-04-18 18:34:59 +00:00
|
|
|
|
uint32_t crc;
|
2021-04-21 02:14:21 +00:00
|
|
|
|
if (!contentlength) {
|
|
|
|
|
if (istext && outbuf.n >= 100) {
|
|
|
|
|
p = stpcpy(p, "Vary: Accept-Encoding\r\n");
|
2021-07-04 19:21:29 +00:00
|
|
|
|
if (!IsTiny() && ClientAcceptsGzip()) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
gzipped = true;
|
|
|
|
|
crc = crc32_z(0, outbuf.p, outbuf.n);
|
|
|
|
|
WRITE32LE(gzip_footer + 0, crc);
|
|
|
|
|
WRITE32LE(gzip_footer + 4, outbuf.n);
|
|
|
|
|
content = FreeLater(Deflate(outbuf.p, outbuf.n, &contentlength));
|
|
|
|
|
DropOutput();
|
2021-04-18 18:34:59 +00:00
|
|
|
|
} else {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
UseOutput();
|
2021-04-18 18:34:59 +00:00
|
|
|
|
}
|
2021-03-28 14:54:21 +00:00
|
|
|
|
} else {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
UseOutput();
|
2021-03-28 14:54:21 +00:00
|
|
|
|
}
|
2021-04-21 02:14:21 +00:00
|
|
|
|
} else {
|
|
|
|
|
DropOutput();
|
|
|
|
|
}
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *ServeDefaultErrorPage(char *p, unsigned code, const char *reason) {
|
|
|
|
|
p = AppendContentType(p, "text/html; charset=ISO-8859-1");
|
2021-04-23 17:45:19 +00:00
|
|
|
|
reason = FreeLater(EscapeHtml(reason, -1, 0));
|
2021-04-24 20:58:34 +00:00
|
|
|
|
Append("\
|
2021-04-21 02:14:21 +00:00
|
|
|
|
<!doctype html>\r\n\
|
|
|
|
|
<title>");
|
2021-04-24 20:58:34 +00:00
|
|
|
|
Append("%d %s", code, reason);
|
|
|
|
|
Append("\
|
2021-04-21 02:14:21 +00:00
|
|
|
|
</title>\r\n\
|
|
|
|
|
<style>\r\n\
|
|
|
|
|
html { color: #111; font-family: sans-serif; }\r\n\
|
|
|
|
|
img { vertical-align: middle; }\r\n\
|
|
|
|
|
</style>\r\n\
|
|
|
|
|
<h1>\r\n");
|
|
|
|
|
AppendLogo();
|
2021-04-24 20:58:34 +00:00
|
|
|
|
Append("%d %s\r\n", code, reason);
|
|
|
|
|
Append("</h1>\r\n");
|
2021-04-21 02:14:21 +00:00
|
|
|
|
UseOutput();
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static char *ServeErrorImpl(unsigned code, const char *reason) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
size_t n;
|
|
|
|
|
char *p, *s;
|
|
|
|
|
struct Asset *a;
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.errors);
|
2021-04-24 20:58:34 +00:00
|
|
|
|
ClearOutput();
|
2021-04-21 02:14:21 +00:00
|
|
|
|
p = SetStatus(code, reason);
|
|
|
|
|
s = xasprintf("/%d.html", code);
|
|
|
|
|
a = GetAsset(s, strlen(s));
|
|
|
|
|
free(s);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
if (!a) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
return ServeDefaultErrorPage(p, code, reason);
|
|
|
|
|
} else if (a->file) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.slurps);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
content = FreeLater(xslurp(a->file->path.s, &contentlength));
|
2021-04-21 02:14:21 +00:00
|
|
|
|
return AppendContentType(p, "text/html; charset=utf-8");
|
|
|
|
|
} else {
|
2021-05-14 12:36:58 +00:00
|
|
|
|
content = (char *)ZIP_LFILE_CONTENT(zbase + a->lf);
|
|
|
|
|
contentlength = GetZipCfileCompressedSize(zbase + a->cf);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
if (IsCompressed(a)) {
|
2021-05-14 12:36:58 +00:00
|
|
|
|
n = GetZipLfileUncompressedSize(zbase + a->lf);
|
2021-04-21 02:14:21 +00:00
|
|
|
|
if ((s = FreeLater(malloc(n))) && Inflate(s, n, content, contentlength)) {
|
|
|
|
|
content = s;
|
|
|
|
|
contentlength = n;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
} else {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
return ServeDefaultErrorPage(p, code, reason);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
2021-05-14 12:36:58 +00:00
|
|
|
|
if (Verify(content, contentlength, ZIP_LFILE_CRC32(zbase + a->lf))) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
return AppendContentType(p, "text/html; charset=utf-8");
|
|
|
|
|
} else {
|
|
|
|
|
return ServeDefaultErrorPage(p, code, reason);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static char *ServeError(unsigned code, const char *reason) {
|
|
|
|
|
LOGF("ERROR %d %s", code, reason);
|
|
|
|
|
return ServeErrorImpl(code, reason);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *ServeFailure(unsigned code, const char *reason) {
|
|
|
|
|
LOGF("FAILURE %d %s %s HTTP%02d %.*s %`'.*s %`'.*s %`'.*s %`'.*s", code,
|
|
|
|
|
reason, DescribeClient(), msg.version, msg.xmethod.b - msg.xmethod.a,
|
|
|
|
|
inbuf.p + msg.xmethod.a, HeaderLength(kHttpHost), HeaderData(kHttpHost),
|
|
|
|
|
msg.uri.b - msg.uri.a, inbuf.p + msg.uri.a, HeaderLength(kHttpReferer),
|
|
|
|
|
HeaderData(kHttpReferer), HeaderLength(kHttpUserAgent),
|
|
|
|
|
HeaderData(kHttpUserAgent));
|
|
|
|
|
return ServeErrorImpl(code, reason);
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-21 02:14:21 +00:00
|
|
|
|
static char *ServeAssetCompressed(struct Asset *a) {
|
|
|
|
|
uint32_t crc;
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.compressedresponses);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
DEBUGF("ServeAssetCompressed()");
|
2021-04-21 02:14:21 +00:00
|
|
|
|
gzipped = true;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
if (msg.method == kHttpHead) {
|
|
|
|
|
content = 0;
|
|
|
|
|
} else {
|
|
|
|
|
crc = crc32_z(0, content, contentlength);
|
|
|
|
|
WRITE32LE(gzip_footer + 0, crc);
|
|
|
|
|
WRITE32LE(gzip_footer + 4, contentlength);
|
|
|
|
|
content = FreeLater(Deflate(content, contentlength, &contentlength));
|
|
|
|
|
}
|
2021-04-21 02:14:21 +00:00
|
|
|
|
return SetStatus(200, "OK");
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-03 19:09:35 +00:00
|
|
|
|
static char *ServeAssetDecompressed(struct Asset *a) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
char *buf;
|
|
|
|
|
size_t size;
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.decompressedresponses);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
size = GetZipCfileUncompressedSize(zbase + a->cf);
|
|
|
|
|
DEBUGF("ServeAssetDecompressed(%ld) -> %ld", contentlength, size);
|
|
|
|
|
if (msg.method == kHttpHead) {
|
|
|
|
|
content = 0;
|
2021-05-03 19:09:35 +00:00
|
|
|
|
contentlength = size;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
return SetStatus(200, "OK");
|
|
|
|
|
} else {
|
2021-05-14 12:36:58 +00:00
|
|
|
|
if ((buf = FreeLater(malloc(size))) &&
|
|
|
|
|
Inflate(buf, size, content, contentlength) &&
|
|
|
|
|
Verify(buf, size, ZIP_CFILE_CRC32(zbase + a->cf))) {
|
|
|
|
|
content = buf;
|
|
|
|
|
contentlength = size;
|
|
|
|
|
return SetStatus(200, "OK");
|
|
|
|
|
} else {
|
|
|
|
|
return ServeError(500, "Internal Server Error");
|
|
|
|
|
}
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-03 19:09:35 +00:00
|
|
|
|
static inline char *ServeAssetIdentity(struct Asset *a, const char *ct) {
|
|
|
|
|
LockInc(&shared->c.identityresponses);
|
|
|
|
|
DEBUGF("ServeAssetIdentity(%`'s)", ct);
|
|
|
|
|
return SetStatus(200, "OK");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline char *ServeAssetPrecompressed(struct Asset *a) {
|
|
|
|
|
size_t size;
|
|
|
|
|
uint32_t crc;
|
|
|
|
|
DEBUGF("ServeAssetPrecompressed()");
|
|
|
|
|
LockInc(&shared->c.precompressedresponses);
|
|
|
|
|
gzipped = true;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
crc = ZIP_CFILE_CRC32(zbase + a->cf);
|
|
|
|
|
size = GetZipCfileUncompressedSize(zbase + a->cf);
|
2021-05-03 19:09:35 +00:00
|
|
|
|
WRITE32LE(gzip_footer + 0, crc);
|
|
|
|
|
WRITE32LE(gzip_footer + 4, size);
|
|
|
|
|
return SetStatus(200, "OK");
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-21 02:14:21 +00:00
|
|
|
|
static char *ServeAssetRange(struct Asset *a) {
|
|
|
|
|
char *p;
|
|
|
|
|
long rangestart, rangelength;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
DEBUGF("ServeAssetRange()");
|
2021-04-21 02:14:21 +00:00
|
|
|
|
if (ParseHttpRange(HeaderData(kHttpRange), HeaderLength(kHttpRange),
|
|
|
|
|
contentlength, &rangestart, &rangelength) &&
|
|
|
|
|
rangestart >= 0 && rangelength >= 0 && rangestart < contentlength &&
|
|
|
|
|
rangestart + rangelength <= contentlength) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.partialresponses);
|
2021-04-21 02:14:21 +00:00
|
|
|
|
p = SetStatus(206, "Partial Content");
|
|
|
|
|
p = AppendContentRange(p, rangestart, rangelength, contentlength);
|
|
|
|
|
content += rangestart;
|
|
|
|
|
contentlength = rangelength;
|
|
|
|
|
return p;
|
|
|
|
|
} else {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.badranges);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
LOGF("bad range %`'.*s", HeaderLength(kHttpRange), HeaderData(kHttpRange));
|
2021-04-21 02:14:21 +00:00
|
|
|
|
p = SetStatus(416, "Range Not Satisfiable");
|
|
|
|
|
p = AppendContentRange(p, -1, -1, contentlength);
|
|
|
|
|
content = "";
|
|
|
|
|
contentlength = 0;
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-14 12:36:58 +00:00
|
|
|
|
static char *GetAssetPath(uint8_t *zcf, size_t *out_size) {
|
2021-03-29 09:39:20 +00:00
|
|
|
|
char *p1, *p2;
|
|
|
|
|
size_t n1, n2;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
p1 = ZIP_CFILE_NAME(zcf);
|
|
|
|
|
n1 = ZIP_CFILE_NAMESIZE(zcf);
|
2021-04-18 18:34:59 +00:00
|
|
|
|
p2 = xmalloc(1 + n1 + 1);
|
2021-03-29 09:39:20 +00:00
|
|
|
|
n2 = 1 + n1 + 1;
|
|
|
|
|
p2[0] = '/';
|
|
|
|
|
memcpy(p2 + 1, p1, n1);
|
|
|
|
|
p2[1 + n1] = '\0';
|
|
|
|
|
if (out_size) *out_size = 1 + n1;
|
|
|
|
|
return p2;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-14 12:36:58 +00:00
|
|
|
|
static bool IsHiddenPath(const char *s, size_t n) {
|
2021-03-29 09:39:20 +00:00
|
|
|
|
size_t i;
|
|
|
|
|
for (i = 0; i < hidepaths.n; ++i) {
|
2021-05-14 12:36:58 +00:00
|
|
|
|
if (n >= hidepaths.p[i].n &&
|
|
|
|
|
!memcmp(s, hidepaths.p[i].s, hidepaths.p[i].n)) {
|
2021-03-29 09:39:20 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static char *GetBasicAuthorization(size_t *z) {
|
|
|
|
|
size_t n;
|
|
|
|
|
const char *p, *q;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
struct HttpSlice *g;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
g = msg.headers + (HasHeader(kHttpProxyAuthorization)
|
|
|
|
|
? kHttpProxyAuthorization
|
|
|
|
|
: kHttpAuthorization);
|
|
|
|
|
p = inbuf.p + g->a;
|
|
|
|
|
n = g->b - g->a;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
if ((q = memchr(p, ' ', n)) && SlicesEqualCase(p, q - p, "Basic", 5)) {
|
|
|
|
|
return DecodeBase64(q + 1, n - (q + 1 - p), z);
|
|
|
|
|
} else {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-03 18:52:24 +00:00
|
|
|
|
static const char *GetSystemUrlLauncherCommand(void) {
|
2021-04-18 14:24:36 +00:00
|
|
|
|
if (IsWindows()) {
|
2021-05-03 18:52:24 +00:00
|
|
|
|
return "explorer";
|
2021-04-18 14:24:36 +00:00
|
|
|
|
} else if (IsXnu()) {
|
2021-05-03 18:52:24 +00:00
|
|
|
|
return "open";
|
2021-04-18 14:24:36 +00:00
|
|
|
|
} else {
|
2021-05-03 18:52:24 +00:00
|
|
|
|
return "xdg-open";
|
2021-04-18 14:24:36 +00:00
|
|
|
|
}
|
2021-05-03 18:52:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void LaunchBrowser(const char *path) {
|
|
|
|
|
int pid, ws;
|
|
|
|
|
struct in_addr addr;
|
|
|
|
|
const char *u, *prog;
|
|
|
|
|
sigset_t chldmask, savemask;
|
|
|
|
|
struct sigaction ignore, saveint, savequit;
|
|
|
|
|
path = firstnonnull(path, "/");
|
2021-06-24 19:31:26 +00:00
|
|
|
|
addr = serveraddr->sin_addr;
|
2021-05-03 18:52:24 +00:00
|
|
|
|
if (!addr.s_addr) addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
|
|
|
if (*path != '/') path = gc(xasprintf("/%s", path));
|
|
|
|
|
if ((prog = commandv(GetSystemUrlLauncherCommand(), gc(malloc(PATH_MAX))))) {
|
|
|
|
|
u = gc(xasprintf("http://%s:%d%s", inet_ntoa(addr),
|
2021-06-24 19:31:26 +00:00
|
|
|
|
ntohs(serveraddr->sin_port), gc(EscapePath(path, -1, 0))));
|
2021-05-03 18:52:24 +00:00
|
|
|
|
DEBUGF("opening browser with command %s %s\n", prog, u);
|
|
|
|
|
ignore.sa_flags = 0;
|
|
|
|
|
ignore.sa_handler = SIG_IGN;
|
|
|
|
|
sigemptyset(&ignore.sa_mask);
|
|
|
|
|
sigaction(SIGINT, &ignore, &saveint);
|
|
|
|
|
sigaction(SIGQUIT, &ignore, &savequit);
|
|
|
|
|
sigemptyset(&chldmask);
|
|
|
|
|
sigaddset(&chldmask, SIGCHLD);
|
|
|
|
|
sigprocmask(SIG_BLOCK, &chldmask, &savemask);
|
|
|
|
|
CHECK_NE(-1, (pid = fork()));
|
|
|
|
|
if (!pid) {
|
|
|
|
|
setpgid(getpid(), getpid());
|
|
|
|
|
sigaction(SIGINT, &saveint, 0);
|
|
|
|
|
sigaction(SIGQUIT, &savequit, 0);
|
|
|
|
|
sigprocmask(SIG_SETMASK, &savemask, 0);
|
|
|
|
|
execv(prog, (char *const[]){prog, u, 0});
|
|
|
|
|
_exit(127);
|
|
|
|
|
}
|
|
|
|
|
while (wait4(pid, &ws, 0, 0) == -1) {
|
|
|
|
|
CHECK_EQ(EINTR, errno);
|
|
|
|
|
}
|
|
|
|
|
sigaction(SIGINT, &saveint, 0);
|
|
|
|
|
sigaction(SIGQUIT, &savequit, 0);
|
|
|
|
|
sigprocmask(SIG_SETMASK, &savemask, 0);
|
|
|
|
|
if (!(WIFEXITED(ws) && WEXITSTATUS(ws) == 0)) {
|
|
|
|
|
WARNF("%s failed with %d", GetSystemUrlLauncherCommand(),
|
|
|
|
|
WIFEXITED(ws) ? WEXITSTATUS(ws) : 128 + WEXITSTATUS(ws));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
WARNF("can't launch browser because %s isn't installed",
|
|
|
|
|
GetSystemUrlLauncherCommand());
|
2021-05-03 17:55:21 +00:00
|
|
|
|
}
|
2021-04-18 14:24:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-25 00:09:01 +00:00
|
|
|
|
static char *BadMethod(void) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.badmethods);
|
2021-04-25 00:09:01 +00:00
|
|
|
|
return stpcpy(ServeError(405, "Method Not Allowed"), "Allow: GET, HEAD\r\n");
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-14 12:36:58 +00:00
|
|
|
|
static int GetDecimalWidth(long x) {
|
2021-04-25 00:09:01 +00:00
|
|
|
|
int w = x ? ceil(log10(x)) : 1;
|
|
|
|
|
return w + (w - 1) / 3;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-25 00:09:01 +00:00
|
|
|
|
static int GetOctalWidth(int x) {
|
|
|
|
|
return !x ? 1 : x < 8 ? 2 : 1 + bsr(x) / 3;
|
2021-03-27 14:29:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-14 12:36:58 +00:00
|
|
|
|
static const char *DescribeCompressionRatio(char rb[8], uint8_t *zcf) {
|
2021-04-25 00:09:01 +00:00
|
|
|
|
long percent;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
if (ZIP_CFILE_COMPRESSIONMETHOD(zcf) == kZipCompressionNone) {
|
2021-04-25 00:09:01 +00:00
|
|
|
|
return "n/a";
|
|
|
|
|
} else {
|
2021-05-14 12:36:58 +00:00
|
|
|
|
percent = lround(100 - (double)GetZipCfileCompressedSize(zcf) /
|
|
|
|
|
GetZipCfileUncompressedSize(zcf) * 100);
|
2021-04-25 00:09:01 +00:00
|
|
|
|
sprintf(rb, "%ld%%", MIN(999, MAX(-999, percent)));
|
|
|
|
|
return rb;
|
|
|
|
|
}
|
2021-03-27 14:29:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-25 00:09:01 +00:00
|
|
|
|
static char *ServeListing(void) {
|
|
|
|
|
long x;
|
|
|
|
|
ldiv_t y;
|
|
|
|
|
int w[3];
|
2021-05-14 12:36:58 +00:00
|
|
|
|
uint8_t *zcf;
|
2021-04-25 00:09:01 +00:00
|
|
|
|
struct tm tm;
|
|
|
|
|
const char *and;
|
|
|
|
|
int64_t lastmod;
|
|
|
|
|
struct rusage ru;
|
|
|
|
|
char *p, *q, *path;
|
2021-07-04 19:21:29 +00:00
|
|
|
|
char rb[8], tb[20], *rp[6];
|
2021-04-25 00:09:01 +00:00
|
|
|
|
size_t i, n, pathlen, rn[6];
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.listingrequests);
|
2021-04-25 00:09:01 +00:00
|
|
|
|
if (msg.method != kHttpGet && msg.method != kHttpHead) return BadMethod();
|
|
|
|
|
Append("\
|
|
|
|
|
<!doctype html>\r\n\
|
|
|
|
|
<meta charset=\"utf-8\">\r\n\
|
|
|
|
|
<title>redbean zip listing</title>\r\n\
|
|
|
|
|
<style>\r\n\
|
|
|
|
|
html { color: #111; font-family: sans-serif; }\r\n\
|
|
|
|
|
a { text-decoration: none; }\r\n\
|
2021-04-25 02:49:49 +00:00
|
|
|
|
pre a:hover { color: #00e; border-bottom: 1px solid #ccc; }\r\n\
|
|
|
|
|
h1 a { color: #111; }\r\n\
|
2021-04-25 00:09:01 +00:00
|
|
|
|
img { vertical-align: middle; }\r\n\
|
|
|
|
|
footer { color: #555; font-size: 10pt; }\r\n\
|
|
|
|
|
td { padding-right: 3em; }\r\n\
|
2021-04-25 02:49:49 +00:00
|
|
|
|
.eocdcomment { max-width: 800px; color: #333; font-size: 11pt; }\r\n\
|
2021-04-25 00:09:01 +00:00
|
|
|
|
</style>\r\n\
|
|
|
|
|
<header><h1>\r\n");
|
|
|
|
|
AppendLogo();
|
|
|
|
|
rp[0] = EscapeHtml(brand, -1, &rn[0]);
|
|
|
|
|
AppendData(rp[0], rn[0]);
|
|
|
|
|
free(rp[0]);
|
2021-04-25 02:49:49 +00:00
|
|
|
|
Append("</h1>\r\n"
|
|
|
|
|
"<div class=\"eocdcomment\">%.*s</div>\r\n"
|
|
|
|
|
"<hr>\r\n"
|
|
|
|
|
"</header>\r\n"
|
|
|
|
|
"<pre>\r\n",
|
2021-05-02 18:50:43 +00:00
|
|
|
|
strnlen(GetZipCdirComment(zcdir), GetZipCdirCommentSize(zcdir)),
|
|
|
|
|
GetZipCdirComment(zcdir));
|
2021-04-25 00:09:01 +00:00
|
|
|
|
memset(w, 0, sizeof(w));
|
2021-05-02 18:50:43 +00:00
|
|
|
|
n = GetZipCdirRecords(zcdir);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
for (zcf = zbase + GetZipCdirOffset(zcdir); n--;
|
|
|
|
|
zcf += ZIP_CFILE_HDRSIZE(zcf)) {
|
|
|
|
|
CHECK_EQ(kZipCfileHdrMagic, ZIP_CFILE_MAGIC(zcf));
|
|
|
|
|
path = GetAssetPath(zcf, &pathlen);
|
|
|
|
|
if (!IsHiddenPath(path, pathlen)) {
|
2021-04-25 00:09:01 +00:00
|
|
|
|
w[0] = min(80, max(w[0], strwidth(path, 0) + 2));
|
2021-05-14 12:36:58 +00:00
|
|
|
|
w[1] = max(w[1], GetOctalWidth(GetZipCfileMode(zcf)));
|
|
|
|
|
w[2] = max(w[2], GetDecimalWidth(GetZipCfileUncompressedSize(zcf)));
|
2021-04-25 00:09:01 +00:00
|
|
|
|
}
|
|
|
|
|
free(path);
|
|
|
|
|
}
|
2021-05-02 18:50:43 +00:00
|
|
|
|
n = GetZipCdirRecords(zcdir);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
for (zcf = zbase + GetZipCdirOffset(zcdir); n--;
|
|
|
|
|
zcf += ZIP_CFILE_HDRSIZE(zcf)) {
|
|
|
|
|
CHECK_EQ(kZipCfileHdrMagic, ZIP_CFILE_MAGIC(zcf));
|
|
|
|
|
path = GetAssetPath(zcf, &pathlen);
|
|
|
|
|
if (!IsHiddenPath(path, pathlen)) {
|
2021-04-25 00:09:01 +00:00
|
|
|
|
rp[0] = VisualizeControlCodes(path, pathlen, &rn[0]);
|
|
|
|
|
rp[1] = EscapePath(path, pathlen, &rn[1]);
|
|
|
|
|
rp[2] = EscapeHtml(rp[1], rn[1], &rn[2]);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
rp[3] = VisualizeControlCodes(
|
|
|
|
|
ZIP_CFILE_COMMENT(zcf),
|
|
|
|
|
strnlen(ZIP_CFILE_COMMENT(zcf), ZIP_CFILE_COMMENTSIZE(zcf)), &rn[3]);
|
2021-04-25 00:09:01 +00:00
|
|
|
|
rp[4] = EscapeHtml(rp[0], rn[0], &rn[4]);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
lastmod = GetZipCfileLastModified(zcf);
|
2021-04-25 00:09:01 +00:00
|
|
|
|
localtime_r(&lastmod, &tm);
|
2021-07-04 19:21:29 +00:00
|
|
|
|
iso8601(tb, &tm);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
if (IsCompressionMethodSupported(ZIP_CFILE_COMPRESSIONMETHOD(zcf)) &&
|
2021-04-25 00:09:01 +00:00
|
|
|
|
IsAcceptablePath(path, pathlen)) {
|
|
|
|
|
Append("<a href=\"%.*s\">%-*.*s</a> %s %0*o %4s %,*ld %'s\r\n",
|
2021-05-14 12:36:58 +00:00
|
|
|
|
rn[2], rp[2], w[0], rn[4], rp[4], tb, w[1], GetZipCfileMode(zcf),
|
|
|
|
|
DescribeCompressionRatio(rb, zcf), w[2],
|
|
|
|
|
GetZipCfileUncompressedSize(zcf), rp[3]);
|
2021-04-25 00:09:01 +00:00
|
|
|
|
} else {
|
|
|
|
|
Append("%-*.*s %s %0*o %4s %,*ld %'s\r\n", w[0], rn[4], rp[4], tb,
|
2021-05-14 12:36:58 +00:00
|
|
|
|
w[1], GetZipCfileMode(zcf), DescribeCompressionRatio(rb, zcf),
|
|
|
|
|
w[2], GetZipCfileUncompressedSize(zcf), rp[3]);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
}
|
2021-04-25 00:09:01 +00:00
|
|
|
|
free(rp[4]);
|
|
|
|
|
free(rp[3]);
|
|
|
|
|
free(rp[2]);
|
|
|
|
|
free(rp[1]);
|
|
|
|
|
free(rp[0]);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
}
|
2021-04-25 00:09:01 +00:00
|
|
|
|
free(path);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
2021-04-25 00:09:01 +00:00
|
|
|
|
Append("</pre><footer><hr>\r\n");
|
2021-04-25 02:49:49 +00:00
|
|
|
|
Append("<table border=\"0\"><tr>\r\n");
|
|
|
|
|
Append("<td valign=\"top\">\r\n");
|
|
|
|
|
Append("<a href=\"/statusz\">/statusz</a>\r\n");
|
2021-05-03 19:09:35 +00:00
|
|
|
|
if (shared->c.connectionshandled) {
|
2021-04-25 02:49:49 +00:00
|
|
|
|
Append("says your redbean<br>\r\n");
|
|
|
|
|
AppendResourceReport(&shared->children, "<br>\r\n");
|
|
|
|
|
}
|
2021-04-25 00:09:01 +00:00
|
|
|
|
Append("<td valign=\"top\">\r\n");
|
|
|
|
|
and = "";
|
|
|
|
|
x = nowl() - startserver;
|
|
|
|
|
y = ldiv(x, 24L * 60 * 60);
|
|
|
|
|
if (y.quot) {
|
|
|
|
|
Append("%,ld day%s ", y.quot, y.quot == 1 ? "" : "s");
|
|
|
|
|
and = "and ";
|
|
|
|
|
}
|
|
|
|
|
y = ldiv(y.rem, 60 * 60);
|
|
|
|
|
if (y.quot) {
|
|
|
|
|
Append("%,ld hour%s ", y.quot, y.quot == 1 ? "" : "s");
|
|
|
|
|
and = "and ";
|
|
|
|
|
}
|
|
|
|
|
y = ldiv(y.rem, 60);
|
|
|
|
|
if (y.quot) {
|
|
|
|
|
Append("%,ld minute%s ", y.quot, y.quot == 1 ? "" : "s");
|
|
|
|
|
and = "and ";
|
|
|
|
|
}
|
|
|
|
|
Append("%s%,ld second%s of operation<br>\r\n", and, y.rem,
|
|
|
|
|
y.rem == 1 ? "" : "s");
|
2021-05-03 19:09:35 +00:00
|
|
|
|
x = shared->c.messageshandled;
|
2021-04-25 00:09:01 +00:00
|
|
|
|
Append("%,ld message%s handled<br>\r\n", x, x == 1 ? "" : "s");
|
2021-05-03 19:09:35 +00:00
|
|
|
|
x = shared->c.connectionshandled;
|
2021-04-25 00:09:01 +00:00
|
|
|
|
Append("%,ld connection%s handled<br>\r\n", x, x == 1 ? "" : "s");
|
|
|
|
|
x = shared->workers;
|
|
|
|
|
Append("%,ld connection%s active<br>\r\n", x, x == 1 ? "" : "s");
|
|
|
|
|
Append("</table>\r\n");
|
|
|
|
|
Append("</footer>\r\n");
|
|
|
|
|
p = SetStatus(200, "OK");
|
|
|
|
|
p = AppendContentType(p, "text/html");
|
|
|
|
|
p = AppendCache(p, 0);
|
|
|
|
|
return CommitOutput(p);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-25 00:09:01 +00:00
|
|
|
|
static const char *MergeNames(const char *a, const char *b) {
|
2021-05-16 04:53:26 +00:00
|
|
|
|
return FreeLater(xasprintf("%s.%s", a, b));
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-25 00:09:01 +00:00
|
|
|
|
static void AppendLong1(const char *a, long x) {
|
|
|
|
|
if (x) Append("%s: %ld\r\n", a, x);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-25 00:09:01 +00:00
|
|
|
|
static void AppendLong2(const char *a, const char *b, long x) {
|
|
|
|
|
if (x) Append("%s.%s: %ld\r\n", a, b, x);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-25 00:09:01 +00:00
|
|
|
|
static void AppendTimeval(const char *a, struct timeval *tv) {
|
|
|
|
|
AppendLong2(a, "tv_sec", tv->tv_sec);
|
|
|
|
|
AppendLong2(a, "tv_usec", tv->tv_usec);
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-25 00:09:01 +00:00
|
|
|
|
static void AppendRusage(const char *a, struct rusage *ru) {
|
|
|
|
|
AppendTimeval(MergeNames(a, "ru_utime"), &ru->ru_utime);
|
|
|
|
|
AppendTimeval(MergeNames(a, "ru_stime"), &ru->ru_stime);
|
|
|
|
|
AppendLong2(a, "ru_maxrss", ru->ru_maxrss);
|
|
|
|
|
AppendLong2(a, "ru_ixrss", ru->ru_ixrss);
|
|
|
|
|
AppendLong2(a, "ru_idrss", ru->ru_idrss);
|
|
|
|
|
AppendLong2(a, "ru_isrss", ru->ru_isrss);
|
|
|
|
|
AppendLong2(a, "ru_minflt", ru->ru_minflt);
|
|
|
|
|
AppendLong2(a, "ru_majflt", ru->ru_majflt);
|
|
|
|
|
AppendLong2(a, "ru_nswap", ru->ru_nswap);
|
|
|
|
|
AppendLong2(a, "ru_inblock", ru->ru_inblock);
|
|
|
|
|
AppendLong2(a, "ru_oublock", ru->ru_oublock);
|
|
|
|
|
AppendLong2(a, "ru_msgsnd", ru->ru_msgsnd);
|
|
|
|
|
AppendLong2(a, "ru_msgrcv", ru->ru_msgrcv);
|
|
|
|
|
AppendLong2(a, "ru_nsignals", ru->ru_nsignals);
|
|
|
|
|
AppendLong2(a, "ru_nvcsw", ru->ru_nvcsw);
|
|
|
|
|
AppendLong2(a, "ru_nivcsw", ru->ru_nivcsw);
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-03 19:09:35 +00:00
|
|
|
|
static void ServeCounters(void) {
|
|
|
|
|
const long *c;
|
|
|
|
|
const char *s;
|
|
|
|
|
for (c = (const long *)&shared->c, s = kCounterNames; *s;
|
|
|
|
|
++c, s += strlen(s) + 1) {
|
|
|
|
|
AppendLong1(s, *c);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-25 00:09:01 +00:00
|
|
|
|
static char *ServeStatusz(void) {
|
|
|
|
|
char *p;
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.statuszrequests);
|
2021-04-25 00:09:01 +00:00
|
|
|
|
if (msg.method != kHttpGet && msg.method != kHttpHead) return BadMethod();
|
|
|
|
|
AppendLong1("pid", getpid());
|
|
|
|
|
AppendLong1("ppid", getppid());
|
|
|
|
|
AppendLong1("now", nowl());
|
|
|
|
|
AppendLong1("nowish", shared->nowish);
|
|
|
|
|
AppendLong1("gmtoff", gmtoff);
|
|
|
|
|
AppendLong1("CLK_TCK", CLK_TCK);
|
|
|
|
|
AppendLong1("startserver", startserver);
|
|
|
|
|
AppendLong1("lastmeltdown", shared->lastmeltdown);
|
|
|
|
|
AppendLong1("workers", shared->workers);
|
|
|
|
|
AppendLong1("assets.n", assets.n);
|
2021-05-03 19:09:35 +00:00
|
|
|
|
ServeCounters();
|
2021-04-25 00:09:01 +00:00
|
|
|
|
AppendRusage("server", &shared->server);
|
|
|
|
|
AppendRusage("children", &shared->children);
|
|
|
|
|
p = SetStatus(200, "OK");
|
|
|
|
|
p = AppendContentType(p, "text/plain");
|
|
|
|
|
p = AppendCache(p, 0);
|
|
|
|
|
return CommitOutput(p);
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-25 00:09:01 +00:00
|
|
|
|
static char *RedirectSlash(void) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
size_t n;
|
2021-04-25 00:09:01 +00:00
|
|
|
|
char *p, *e;
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.redirects);
|
2021-04-25 00:09:01 +00:00
|
|
|
|
p = SetStatus(307, "Temporary Redirect");
|
|
|
|
|
p = stpcpy(p, "Location: ");
|
|
|
|
|
e = EscapePath(url.path.p, url.path.n, &n);
|
|
|
|
|
p = mempcpy(p, e, n);
|
|
|
|
|
p = stpcpy(p, "/\r\n");
|
|
|
|
|
free(e);
|
|
|
|
|
return p;
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-25 00:09:01 +00:00
|
|
|
|
static char *ServeIndex(const char *path, size_t pathlen) {
|
|
|
|
|
size_t i, n;
|
|
|
|
|
char *p, *q;
|
2021-05-17 01:22:39 +00:00
|
|
|
|
for (p = 0, i = 0; !p && i < ARRAYLEN(kIndexPaths); ++i) {
|
2021-04-25 00:09:01 +00:00
|
|
|
|
q = MergePaths(path, pathlen, kIndexPaths[i], strlen(kIndexPaths[i]), &n);
|
|
|
|
|
p = RoutePath(q, n);
|
|
|
|
|
free(q);
|
|
|
|
|
}
|
|
|
|
|
return p;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-25 00:09:01 +00:00
|
|
|
|
static char *GetLuaResponse(void) {
|
|
|
|
|
char *p;
|
|
|
|
|
if (!(p = luaheaderp)) {
|
|
|
|
|
p = SetStatus(200, "OK");
|
|
|
|
|
p = AppendContentType(p, "text/html");
|
|
|
|
|
}
|
|
|
|
|
return p;
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-03 19:09:35 +00:00
|
|
|
|
static char *LuaOnHttpRequest(void) {
|
|
|
|
|
effectivepath.p = url.path.p;
|
|
|
|
|
effectivepath.n = url.path.n;
|
|
|
|
|
lua_getglobal(L, "OnHttpRequest");
|
|
|
|
|
if (lua_pcall(L, 0, 0, 0) == LUA_OK) {
|
|
|
|
|
return CommitOutput(GetLuaResponse());
|
|
|
|
|
} else {
|
|
|
|
|
WARNF("%s", lua_tostring(L, -1));
|
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
return ServeError(500, "Internal Server Error");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *ServeLua(struct Asset *a, const char *s, size_t n) {
|
2021-04-25 00:09:01 +00:00
|
|
|
|
char *code;
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.dynamicrequests);
|
|
|
|
|
effectivepath.p = s;
|
|
|
|
|
effectivepath.n = n;
|
2021-04-25 00:09:01 +00:00
|
|
|
|
if ((code = FreeLater(LoadAsset(a, NULL)))) {
|
|
|
|
|
if (luaL_dostring(L, code) == LUA_OK) {
|
|
|
|
|
return CommitOutput(GetLuaResponse());
|
|
|
|
|
} else {
|
|
|
|
|
WARNF("%s", lua_tostring(L, -1));
|
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ServeError(500, "Internal Server Error");
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-25 00:09:01 +00:00
|
|
|
|
static char *HandleRedirect(struct Redirect *r) {
|
|
|
|
|
int code;
|
|
|
|
|
struct Asset *a;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
if (!r->code && (a = GetAsset(r->location.s, r->location.n))) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.rewrites);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
DEBUGF("rewriting to %`'s", r->location.s);
|
|
|
|
|
if (!HasString(&loops, r->location.s, r->location.n)) {
|
|
|
|
|
AddString(&loops, r->location.s, r->location.n);
|
|
|
|
|
return RoutePath(r->location.s, r->location.n);
|
2021-04-25 00:09:01 +00:00
|
|
|
|
} else {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.loops);
|
2021-04-25 00:09:01 +00:00
|
|
|
|
return SetStatus(508, "Loop Detected");
|
|
|
|
|
}
|
|
|
|
|
} else if (msg.version < 10) {
|
|
|
|
|
return ServeError(505, "HTTP Version Not Supported");
|
|
|
|
|
} else {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.redirects);
|
2021-04-25 00:09:01 +00:00
|
|
|
|
code = r->code;
|
|
|
|
|
if (!code) code = 307;
|
|
|
|
|
DEBUGF("%d redirecting %`'s", code, r->location);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
return AppendHeader(
|
|
|
|
|
SetStatus(code, GetHttpReason(code)), "Location",
|
|
|
|
|
FreeLater(EncodeHttpHeaderValue(r->location.s, r->location.n, 0)));
|
2021-04-25 00:09:01 +00:00
|
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-25 00:09:01 +00:00
|
|
|
|
static char *HandleFolder(const char *path, size_t pathlen) {
|
|
|
|
|
char *p;
|
|
|
|
|
if (url.path.n && url.path.p[url.path.n - 1] != '/' &&
|
|
|
|
|
SlicesEqual(path, pathlen, url.path.p, url.path.n)) {
|
|
|
|
|
return RedirectSlash();
|
|
|
|
|
}
|
|
|
|
|
if ((p = ServeIndex(path, pathlen))) {
|
|
|
|
|
return p;
|
2021-04-21 02:14:21 +00:00
|
|
|
|
} else {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.forbiddens);
|
2021-04-25 00:09:01 +00:00
|
|
|
|
LOGF("directory %`'.*s lacks index page", pathlen, path);
|
|
|
|
|
return ServeError(403, "Forbidden");
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-14 12:36:58 +00:00
|
|
|
|
static bool Reindex(void) {
|
|
|
|
|
if (OpenZip(false)) {
|
|
|
|
|
LockInc(&shared->c.reindexes);
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-05-02 18:50:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-25 00:09:01 +00:00
|
|
|
|
static const char *LuaCheckPath(lua_State *L, int idx, size_t *pathlen) {
|
|
|
|
|
const char *path;
|
|
|
|
|
if (lua_isnoneornil(L, idx)) {
|
|
|
|
|
path = url.path.p;
|
|
|
|
|
*pathlen = url.path.n;
|
|
|
|
|
} else {
|
|
|
|
|
path = luaL_checklstring(L, idx, pathlen);
|
|
|
|
|
if (!IsReasonablePath(path, *pathlen)) {
|
|
|
|
|
WARNF("bad path %`'.*s", *pathlen, path);
|
|
|
|
|
luaL_argerror(L, idx, "bad path");
|
|
|
|
|
unreachable;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *LuaCheckHost(lua_State *L, int idx, size_t *hostlen) {
|
|
|
|
|
const char *host;
|
|
|
|
|
if (lua_isnoneornil(L, idx)) {
|
|
|
|
|
host = url.host.p;
|
|
|
|
|
*hostlen = url.host.n;
|
|
|
|
|
} else {
|
|
|
|
|
host = luaL_checklstring(L, idx, hostlen);
|
|
|
|
|
if (!IsAcceptableHost(host, *hostlen)) {
|
|
|
|
|
WARNF("bad host %`'.*s", *hostlen, host);
|
|
|
|
|
luaL_argerror(L, idx, "bad host");
|
|
|
|
|
unreachable;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return host;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaServeListing(lua_State *L) {
|
|
|
|
|
luaheaderp = ServeListing();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaServeStatusz(lua_State *L) {
|
|
|
|
|
luaheaderp = ServeStatusz();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaServeAsset(lua_State *L) {
|
|
|
|
|
size_t pathlen;
|
|
|
|
|
struct Asset *a;
|
|
|
|
|
const char *path;
|
|
|
|
|
path = LuaCheckPath(L, 1, &pathlen);
|
|
|
|
|
if ((a = GetAsset(path, pathlen)) && !S_ISDIR(GetMode(a))) {
|
|
|
|
|
luaheaderp = ServeAsset(a, path, pathlen);
|
|
|
|
|
lua_pushboolean(L, true);
|
|
|
|
|
} else {
|
|
|
|
|
lua_pushboolean(L, false);
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaServeIndex(lua_State *L) {
|
|
|
|
|
size_t pathlen;
|
|
|
|
|
const char *path;
|
|
|
|
|
path = LuaCheckPath(L, 1, &pathlen);
|
|
|
|
|
lua_pushboolean(L, !!(luaheaderp = ServeIndex(path, pathlen)));
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaRoutePath(lua_State *L) {
|
|
|
|
|
size_t pathlen;
|
|
|
|
|
const char *path;
|
|
|
|
|
path = LuaCheckPath(L, 1, &pathlen);
|
|
|
|
|
lua_pushboolean(L, !!(luaheaderp = RoutePath(path, pathlen)));
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaRouteHost(lua_State *L) {
|
|
|
|
|
size_t hostlen, pathlen;
|
|
|
|
|
const char *host, *path;
|
|
|
|
|
host = LuaCheckHost(L, 1, &hostlen);
|
|
|
|
|
path = LuaCheckPath(L, 2, &pathlen);
|
|
|
|
|
lua_pushboolean(L, !!(luaheaderp = RouteHost(host, hostlen, path, pathlen)));
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaRoute(lua_State *L) {
|
|
|
|
|
size_t hostlen, pathlen;
|
|
|
|
|
const char *host, *path;
|
|
|
|
|
host = LuaCheckHost(L, 1, &hostlen);
|
|
|
|
|
path = LuaCheckPath(L, 2, &pathlen);
|
|
|
|
|
lua_pushboolean(L, !!(luaheaderp = Route(host, hostlen, path, pathlen)));
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-16 04:53:26 +00:00
|
|
|
|
static int LuaRespond(lua_State *L, char *R(unsigned, const char *)) {
|
2021-04-25 00:09:01 +00:00
|
|
|
|
char *p;
|
|
|
|
|
int code;
|
|
|
|
|
size_t reasonlen;
|
|
|
|
|
const char *reason;
|
|
|
|
|
code = luaL_checkinteger(L, 1);
|
|
|
|
|
if (!(100 <= code && code <= 999)) {
|
|
|
|
|
luaL_argerror(L, 1, "bad status code");
|
|
|
|
|
unreachable;
|
|
|
|
|
}
|
|
|
|
|
if (lua_isnoneornil(L, 2)) {
|
2021-05-16 04:53:26 +00:00
|
|
|
|
luaheaderp = R(code, GetHttpReason(code));
|
2021-04-25 00:09:01 +00:00
|
|
|
|
} else {
|
|
|
|
|
reason = lua_tolstring(L, 2, &reasonlen);
|
|
|
|
|
if (reasonlen < 128 && (p = EncodeHttpHeaderValue(reason, reasonlen, 0))) {
|
2021-05-16 04:53:26 +00:00
|
|
|
|
luaheaderp = R(code, p);
|
2021-04-25 00:09:01 +00:00
|
|
|
|
free(p);
|
|
|
|
|
} else {
|
|
|
|
|
luaL_argerror(L, 2, "invalid");
|
|
|
|
|
unreachable;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaSetStatus(lua_State *L) {
|
|
|
|
|
return LuaRespond(L, SetStatus);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaServeError(lua_State *L) {
|
|
|
|
|
return LuaRespond(L, ServeError);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaLoadAsset(lua_State *L) {
|
|
|
|
|
void *data;
|
|
|
|
|
struct Asset *a;
|
|
|
|
|
const char *path;
|
|
|
|
|
size_t size, pathlen;
|
|
|
|
|
path = LuaCheckPath(L, 1, &pathlen);
|
|
|
|
|
if ((a = GetAsset(path, pathlen))) {
|
|
|
|
|
if (!a->file && !IsCompressed(a)) {
|
|
|
|
|
/* fast path: this avoids extra copy */
|
2021-05-14 12:36:58 +00:00
|
|
|
|
data = ZIP_LFILE_CONTENT(zbase + a->lf);
|
|
|
|
|
size = GetZipLfileUncompressedSize(zbase + a->lf);
|
|
|
|
|
if (Verify(data, size, ZIP_LFILE_CRC32(zbase + a->lf))) {
|
2021-04-25 00:09:01 +00:00
|
|
|
|
lua_pushlstring(L, data, size);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
} else if ((data = LoadAsset(a, &size))) {
|
|
|
|
|
lua_pushlstring(L, data, size);
|
|
|
|
|
free(data);
|
|
|
|
|
return 1;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
} else {
|
|
|
|
|
DEBUGF("could not load asset: %`'.*s", pathlen, path);
|
2021-04-25 00:09:01 +00:00
|
|
|
|
}
|
2021-06-24 19:31:26 +00:00
|
|
|
|
} else {
|
|
|
|
|
DEBUGF("could not find asset: %`'.*s", pathlen, path);
|
2021-04-25 00:09:01 +00:00
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-03 20:06:43 +00:00
|
|
|
|
static void GetDosLocalTime(int64_t utcunixts, uint16_t *out_time,
|
|
|
|
|
uint16_t *out_date) {
|
|
|
|
|
struct tm tm;
|
|
|
|
|
CHECK_NOTNULL(localtime_r(&utcunixts, &tm));
|
|
|
|
|
*out_time = DOS_TIME(tm.tm_hour, tm.tm_min, tm.tm_sec);
|
|
|
|
|
*out_date = DOS_DATE(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday + 1);
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-03 19:09:35 +00:00
|
|
|
|
static bool IsUtf8(const void *data, size_t size) {
|
|
|
|
|
const unsigned char *p, *pe;
|
|
|
|
|
for (p = data, pe = p + size; p + 2 <= pe; ++p) {
|
2021-06-28 00:03:06 +00:00
|
|
|
|
if (p[0] >= 0300) {
|
|
|
|
|
if (p[1] >= 0200 && p[1] < 0300) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool IsText(const void *data, size_t size) {
|
|
|
|
|
const unsigned char *p, *pe;
|
|
|
|
|
for (p = data, pe = p + size; p < pe; ++p) {
|
|
|
|
|
if (*p <= 3) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-24 19:31:26 +00:00
|
|
|
|
static int LuaStoreAsset(lua_State *L) {
|
2021-05-03 20:06:43 +00:00
|
|
|
|
int64_t ft;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
int i, mode;
|
2021-05-03 20:06:43 +00:00
|
|
|
|
uint32_t crc;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
char *comp, *p;
|
2021-05-03 20:06:43 +00:00
|
|
|
|
long double now;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
struct Asset *a;
|
|
|
|
|
struct iovec v[13];
|
|
|
|
|
uint8_t *cdir, era;
|
2021-05-03 20:06:43 +00:00
|
|
|
|
const char *path, *data, *use;
|
|
|
|
|
uint16_t gflags, iattrs, mtime, mdate, dosmode, method, disk;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
size_t oldcdirsize, oldcdiroffset, records, cdiroffset, cdirsize, pathlen,
|
|
|
|
|
datalen, complen, uselen;
|
2021-05-03 20:06:43 +00:00
|
|
|
|
if (IsOpenbsd() || IsNetbsd() || IsWindows()) {
|
|
|
|
|
luaL_error(L, "StoreAsset() not available on Windows/NetBSD/OpenBSD yet");
|
|
|
|
|
unreachable;
|
|
|
|
|
}
|
|
|
|
|
path = LuaCheckPath(L, 1, &pathlen);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
if (pathlen > 0xffff) {
|
|
|
|
|
luaL_argerror(L, 1, "path too long");
|
|
|
|
|
unreachable;
|
|
|
|
|
}
|
2021-05-03 20:06:43 +00:00
|
|
|
|
data = luaL_checklstring(L, 2, &datalen);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
disk = gflags = iattrs = 0;
|
|
|
|
|
if (IsUtf8(path, pathlen)) gflags |= kZipGflagUtf8;
|
|
|
|
|
if (IsText(data, datalen)) iattrs |= kZipIattrText;
|
2021-05-03 20:06:43 +00:00
|
|
|
|
crc = crc32_z(0, data, datalen);
|
|
|
|
|
if (datalen < 100) {
|
|
|
|
|
method = kZipCompressionNone;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
comp = 0;
|
2021-05-03 20:06:43 +00:00
|
|
|
|
use = data;
|
|
|
|
|
uselen = datalen;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
era = kZipEra1989;
|
2021-05-03 20:06:43 +00:00
|
|
|
|
} else {
|
|
|
|
|
comp = Deflate(data, datalen, &complen);
|
|
|
|
|
if (complen < datalen) {
|
|
|
|
|
method = kZipCompressionDeflate;
|
|
|
|
|
use = comp;
|
|
|
|
|
uselen = complen;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
era = kZipEra1993;
|
2021-05-03 20:06:43 +00:00
|
|
|
|
} else {
|
|
|
|
|
method = kZipCompressionNone;
|
|
|
|
|
use = data;
|
|
|
|
|
uselen = datalen;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
era = kZipEra1989;
|
2021-05-03 20:06:43 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-05-14 12:36:58 +00:00
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
CHECK_NE(-1, fcntl(zfd, F_SETLKW, &(struct flock){F_WRLCK}));
|
|
|
|
|
OpenZip(false);
|
|
|
|
|
now = nowl();
|
|
|
|
|
a = GetAsset(path, pathlen);
|
|
|
|
|
mode = luaL_optinteger(L, 3, a ? GetMode(a) : 0644);
|
|
|
|
|
if (!(mode & S_IFMT)) mode |= S_IFREG;
|
|
|
|
|
if (pathlen > 1 && path[0] == '/') ++path, --pathlen;
|
2021-05-03 20:06:43 +00:00
|
|
|
|
dosmode = !(mode & 0200) ? kNtFileAttributeReadonly : 0;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
ft = (now + MODERNITYSECONDS) * HECTONANOSECONDS;
|
2021-05-03 20:06:43 +00:00
|
|
|
|
GetDosLocalTime(now, &mtime, &mdate);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
// local file header
|
|
|
|
|
if (uselen >= 0xffffffff || datalen >= 0xffffffff) {
|
|
|
|
|
era = kZipEra2001;
|
|
|
|
|
v[2].iov_base = p = alloca((v[2].iov_len = 2 + 2 + 8 + 8));
|
|
|
|
|
p = WRITE16LE(p, kZipExtraZip64);
|
|
|
|
|
p = WRITE16LE(p, 8 + 8);
|
|
|
|
|
p = WRITE64LE(p, uselen);
|
|
|
|
|
p = WRITE64LE(p, datalen);
|
|
|
|
|
} else {
|
|
|
|
|
v[2].iov_len = 0;
|
|
|
|
|
v[2].iov_base = 0;
|
|
|
|
|
}
|
|
|
|
|
v[0].iov_base = p = alloca((v[0].iov_len = kZipLfileHdrMinSize));
|
2021-05-03 20:06:43 +00:00
|
|
|
|
p = WRITE32LE(p, kZipLfileHdrMagic);
|
|
|
|
|
*p++ = era;
|
|
|
|
|
*p++ = kZipOsDos;
|
|
|
|
|
p = WRITE16LE(p, gflags);
|
|
|
|
|
p = WRITE16LE(p, method);
|
|
|
|
|
p = WRITE16LE(p, mtime);
|
|
|
|
|
p = WRITE16LE(p, mdate);
|
|
|
|
|
p = WRITE32LE(p, crc);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
p = WRITE32LE(p, MIN(uselen, 0xffffffff));
|
|
|
|
|
p = WRITE32LE(p, MIN(datalen, 0xffffffff));
|
2021-05-03 20:06:43 +00:00
|
|
|
|
p = WRITE16LE(p, pathlen);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
p = WRITE16LE(p, v[2].iov_len);
|
|
|
|
|
v[1].iov_len = pathlen;
|
|
|
|
|
v[1].iov_base = path;
|
|
|
|
|
// file data
|
|
|
|
|
v[3].iov_len = datalen;
|
|
|
|
|
v[3].iov_base = data;
|
|
|
|
|
// old central directory entries
|
|
|
|
|
oldcdirsize = GetZipCdirSize(zcdir);
|
|
|
|
|
oldcdiroffset = GetZipCdirOffset(zcdir);
|
|
|
|
|
if (a) {
|
|
|
|
|
v[4].iov_base = zbase + oldcdiroffset;
|
|
|
|
|
v[4].iov_len = a->cf - oldcdiroffset;
|
|
|
|
|
v[5].iov_base = zbase + oldcdiroffset + ZIP_CFILE_HDRSIZE(zbase + a->cf);
|
|
|
|
|
v[5].iov_len =
|
|
|
|
|
oldcdirsize - v[4].iov_len - ZIP_CFILE_HDRSIZE(zbase + a->cf);
|
|
|
|
|
} else {
|
|
|
|
|
v[4].iov_base = zbase + oldcdiroffset;
|
|
|
|
|
v[4].iov_len = oldcdirsize;
|
|
|
|
|
v[5].iov_base = 0;
|
|
|
|
|
v[5].iov_len = 0;
|
|
|
|
|
}
|
|
|
|
|
// new central directory entry
|
|
|
|
|
if (uselen >= 0xffffffff || datalen >= 0xffffffff || zsize >= 0xffffffff) {
|
|
|
|
|
v[8].iov_base = p = alloca((v[8].iov_len = 2 + 2 + 8 + 8 + 8));
|
|
|
|
|
p = WRITE16LE(p, kZipExtraZip64);
|
|
|
|
|
p = WRITE16LE(p, 8 + 8 + 8);
|
|
|
|
|
p = WRITE64LE(p, uselen);
|
|
|
|
|
p = WRITE64LE(p, datalen);
|
|
|
|
|
p = WRITE64LE(p, zsize);
|
|
|
|
|
} else {
|
|
|
|
|
v[8].iov_len = 0;
|
|
|
|
|
v[8].iov_base = 0;
|
|
|
|
|
}
|
|
|
|
|
v[9].iov_base = p = alloca((v[9].iov_len = 2 + 2 + 4 + 2 + 2 + 8 + 8 + 8));
|
|
|
|
|
p = WRITE16LE(p, kZipExtraNtfs);
|
|
|
|
|
p = WRITE16LE(p, 4 + 2 + 2 + 8 + 8 + 8);
|
|
|
|
|
p = WRITE32LE(p, 0);
|
|
|
|
|
p = WRITE16LE(p, 1);
|
|
|
|
|
p = WRITE16LE(p, 8 + 8 + 8);
|
|
|
|
|
p = WRITE64LE(p, ft);
|
|
|
|
|
p = WRITE64LE(p, ft);
|
|
|
|
|
p = WRITE64LE(p, ft);
|
|
|
|
|
v[6].iov_base = p = alloca((v[6].iov_len = kZipCfileHdrMinSize));
|
2021-05-03 20:06:43 +00:00
|
|
|
|
p = WRITE32LE(p, kZipCfileHdrMagic);
|
|
|
|
|
*p++ = kZipCosmopolitanVersion;
|
|
|
|
|
*p++ = kZipOsUnix;
|
|
|
|
|
*p++ = era;
|
|
|
|
|
*p++ = kZipOsDos;
|
|
|
|
|
p = WRITE16LE(p, gflags);
|
|
|
|
|
p = WRITE16LE(p, method);
|
|
|
|
|
p = WRITE16LE(p, mtime);
|
|
|
|
|
p = WRITE16LE(p, mdate);
|
|
|
|
|
p = WRITE32LE(p, crc);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
p = WRITE32LE(p, MIN(uselen, 0xffffffff));
|
|
|
|
|
p = WRITE32LE(p, MIN(datalen, 0xffffffff));
|
2021-05-03 20:06:43 +00:00
|
|
|
|
p = WRITE16LE(p, pathlen);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
p = WRITE16LE(p, v[8].iov_len + v[9].iov_len);
|
2021-05-03 20:06:43 +00:00
|
|
|
|
p = WRITE16LE(p, 0);
|
|
|
|
|
p = WRITE16LE(p, disk);
|
|
|
|
|
p = WRITE16LE(p, iattrs);
|
|
|
|
|
p = WRITE16LE(p, dosmode);
|
|
|
|
|
p = WRITE16LE(p, mode);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
p = WRITE32LE(p, MIN(zsize, 0xffffffff));
|
|
|
|
|
v[7].iov_len = pathlen;
|
|
|
|
|
v[7].iov_base = path;
|
|
|
|
|
// zip64 end of central directory
|
|
|
|
|
cdiroffset =
|
|
|
|
|
zsize + v[0].iov_len + v[1].iov_len + v[2].iov_len + v[3].iov_len;
|
|
|
|
|
cdirsize = v[4].iov_len + v[5].iov_len + v[6].iov_len + v[7].iov_len +
|
|
|
|
|
v[8].iov_len + v[9].iov_len;
|
|
|
|
|
records = GetZipCdirRecords(zcdir) + !a;
|
|
|
|
|
if (records >= 0xffff || cdiroffset >= 0xffffffff || cdirsize >= 0xffffffff) {
|
|
|
|
|
v[10].iov_base = p =
|
|
|
|
|
alloca((v[10].iov_len = kZipCdir64HdrMinSize + kZipCdir64LocatorSize));
|
|
|
|
|
p = WRITE32LE(p, kZipCdir64HdrMagic);
|
|
|
|
|
p = WRITE64LE(p, 2 + 2 + 4 + 4 + 8 + 8 + 8 + 8);
|
|
|
|
|
p = WRITE16LE(p, kZipCosmopolitanVersion);
|
|
|
|
|
p = WRITE16LE(p, kZipEra2001);
|
|
|
|
|
p = WRITE32LE(p, disk);
|
|
|
|
|
p = WRITE32LE(p, disk);
|
|
|
|
|
p = WRITE64LE(p, records);
|
|
|
|
|
p = WRITE64LE(p, records);
|
|
|
|
|
p = WRITE64LE(p, cdirsize);
|
|
|
|
|
p = WRITE64LE(p, cdiroffset);
|
|
|
|
|
p = WRITE32LE(p, kZipCdir64LocatorMagic);
|
|
|
|
|
p = WRITE32LE(p, disk);
|
|
|
|
|
p = WRITE64LE(p, cdiroffset + cdirsize);
|
|
|
|
|
p = WRITE32LE(p, disk);
|
|
|
|
|
} else {
|
|
|
|
|
v[10].iov_len = 0;
|
|
|
|
|
v[10].iov_base = 0;
|
|
|
|
|
}
|
|
|
|
|
// end of central directory
|
|
|
|
|
v[12].iov_base = GetZipCdirComment(zcdir);
|
|
|
|
|
v[12].iov_len = GetZipCdirCommentSize(zcdir);
|
|
|
|
|
v[11].iov_base = p = alloca((v[11].iov_len = kZipCdirHdrMinSize));
|
2021-05-03 20:06:43 +00:00
|
|
|
|
p = WRITE32LE(p, kZipCdirHdrMagic);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
p = WRITE16LE(p, disk);
|
|
|
|
|
p = WRITE16LE(p, disk);
|
|
|
|
|
p = WRITE16LE(p, MIN(records, 0xffff));
|
|
|
|
|
p = WRITE16LE(p, MIN(records, 0xffff));
|
|
|
|
|
p = WRITE32LE(p, MIN(cdirsize, 0xffffffff));
|
|
|
|
|
p = WRITE32LE(p, MIN(cdiroffset, 0xffffffff));
|
|
|
|
|
p = WRITE16LE(p, v[12].iov_len);
|
|
|
|
|
CHECK_NE(-1, lseek(zfd, zbase + zsize - zmap, SEEK_SET));
|
|
|
|
|
CHECK_NE(-1, WritevAll(zfd, v, 13));
|
|
|
|
|
CHECK_NE(-1, fcntl(zfd, F_SETLK, &(struct flock){F_UNLCK}));
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
OpenZip(false);
|
2021-05-03 20:06:43 +00:00
|
|
|
|
free(comp);
|
|
|
|
|
return 0;
|
2021-05-03 19:09:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-07-08 04:44:27 +00:00
|
|
|
|
static void ReseedRng(mbedtls_ctr_drbg_context *r, const char *s) {
|
|
|
|
|
#ifndef UNSECURE
|
|
|
|
|
CHECK_EQ(0, mbedtls_ctr_drbg_reseed(r, (void *)s, strlen(s)));
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *TlsError(int r) {
|
|
|
|
|
static char b[128];
|
|
|
|
|
mbedtls_strerror(r, b, sizeof(b));
|
|
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static wontreturn void LuaThrowTlsError(lua_State *L, const char *s, int r) {
|
|
|
|
|
const char *code;
|
|
|
|
|
code = gc(xasprintf("-0x%04x", -r));
|
|
|
|
|
if (!IsTiny()) {
|
|
|
|
|
luaL_error(L, "tls %s failed (%s %s)", s, code, TlsError(r));
|
|
|
|
|
} else {
|
|
|
|
|
luaL_error(L, "tls %s failed (grep %s)", s, code);
|
|
|
|
|
}
|
|
|
|
|
unreachable;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool Tune(int fd, int a, int b, int x) {
|
|
|
|
|
if (!b) return false;
|
|
|
|
|
return setsockopt(fd, a, b, &x, sizeof(x)) != -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int Socket(int family, int type, int protocol, bool isserver) {
|
|
|
|
|
int fd;
|
|
|
|
|
if ((fd = socket(family, type, protocol)) != -1) {
|
|
|
|
|
if (isserver) {
|
|
|
|
|
Tune(fd, SOL_TCP, TCP_FASTOPEN, 100);
|
|
|
|
|
Tune(fd, SOL_SOCKET, SO_REUSEADDR, 1);
|
|
|
|
|
} else {
|
|
|
|
|
Tune(fd, SOL_TCP, TCP_FASTOPEN_CONNECT, 1);
|
|
|
|
|
}
|
|
|
|
|
if (!Tune(fd, SOL_TCP, TCP_QUICKACK, 1)) {
|
|
|
|
|
Tune(fd, SOL_TCP, TCP_NODELAY, 1);
|
|
|
|
|
}
|
|
|
|
|
if (timeout.tv_sec < 0) {
|
|
|
|
|
Tune(fd, SOL_SOCKET, SO_KEEPALIVE, 1);
|
|
|
|
|
Tune(fd, SOL_TCP, TCP_KEEPIDLE, -timeout.tv_sec);
|
|
|
|
|
Tune(fd, SOL_TCP, TCP_KEEPINTVL, -timeout.tv_sec);
|
|
|
|
|
} else {
|
|
|
|
|
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
|
|
|
|
|
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return fd;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *FoldHeader(struct HttpMessage *msg, char *b, int h, size_t *z) {
|
|
|
|
|
char *p;
|
|
|
|
|
size_t i, n, m;
|
|
|
|
|
struct HttpHeader *x;
|
|
|
|
|
n = msg->headers[h].b - msg->headers[h].a;
|
|
|
|
|
p = xmalloc(n);
|
|
|
|
|
memcpy(p, b + msg->headers[h].a, n);
|
|
|
|
|
for (i = 0; i < msg->xheaders.n; ++i) {
|
|
|
|
|
x = msg->xheaders.p + i;
|
|
|
|
|
if (GetHttpHeader(b + x->k.a, x->k.b - x->k.a) == h) {
|
|
|
|
|
m = x->v.b - x->v.a;
|
|
|
|
|
p = xrealloc(p, n + 2 + m);
|
|
|
|
|
memcpy(mempcpy(p + n, ", ", 2), b + x->v.a, m);
|
|
|
|
|
n += 2 + m;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*z = n;
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void LuaPushLatin1(lua_State *L, const char *s, size_t n) {
|
|
|
|
|
char *t;
|
|
|
|
|
size_t m;
|
|
|
|
|
t = DecodeLatin1(s, n, &m);
|
|
|
|
|
lua_pushlstring(L, t, m);
|
|
|
|
|
free(t);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaPushHeader(lua_State *L, struct HttpMessage *m, char *b, int h) {
|
|
|
|
|
char *val;
|
|
|
|
|
size_t vallen;
|
|
|
|
|
if (!kHttpRepeatable[h]) {
|
|
|
|
|
LuaPushLatin1(L, b + m->headers[h].a, m->headers[h].b - m->headers[h].a);
|
|
|
|
|
} else {
|
|
|
|
|
val = FoldHeader(m, b, h, &vallen);
|
|
|
|
|
LuaPushLatin1(L, val, vallen);
|
|
|
|
|
free(val);
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaPushHeaders(lua_State *L, struct HttpMessage *m, const char *b) {
|
|
|
|
|
char *s;
|
|
|
|
|
size_t i, h;
|
|
|
|
|
struct HttpHeader *x;
|
|
|
|
|
lua_newtable(L);
|
|
|
|
|
for (h = 0; h < kHttpHeadersMax; ++h) {
|
|
|
|
|
if (m->headers[h].a) {
|
|
|
|
|
LuaPushHeader(L, m, b, h);
|
|
|
|
|
lua_setfield(L, -2, GetHttpHeaderName(h));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (i = 0; i < m->xheaders.n; ++i) {
|
|
|
|
|
x = m->xheaders.p + i;
|
|
|
|
|
if ((h = GetHttpHeader(b + x->v.a, x->v.b - x->v.a)) == -1) {
|
|
|
|
|
LuaPushLatin1(L, b + x->v.a, x->v.b - x->v.a);
|
|
|
|
|
lua_setfield(L, -2, (s = DecodeLatin1(b + x->k.a, x->k.b - x->k.a, 0)));
|
|
|
|
|
free(s);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void LogMessage(const char *d, const char *s, size_t n) {
|
|
|
|
|
size_t n2, n3;
|
|
|
|
|
char *s2, *s3;
|
2021-07-08 22:56:23 +00:00
|
|
|
|
if (!LOGGABLE(kLogInfo)) return;
|
2021-07-08 04:44:27 +00:00
|
|
|
|
while (n && (s[n - 1] == '\r' || s[n - 1] == '\n')) --n;
|
|
|
|
|
if ((s2 = DecodeLatin1(s, n, &n2))) {
|
|
|
|
|
if ((s3 = IndentLines(s2, n2, &n3, 1))) {
|
|
|
|
|
LOGF("%s %,ld byte message\n%.*s", d, n, n3, s3);
|
|
|
|
|
free(s3);
|
|
|
|
|
}
|
|
|
|
|
free(s2);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void LogBody(const char *d, const char *s, size_t n) {
|
|
|
|
|
char *s2, *s3;
|
|
|
|
|
size_t n2, n3;
|
|
|
|
|
if (!n) return;
|
2021-07-08 22:56:23 +00:00
|
|
|
|
if (!LOGGABLE(kLogInfo)) return;
|
2021-07-08 04:44:27 +00:00
|
|
|
|
while (n && (s[n - 1] == '\r' || s[n - 1] == '\n')) --n;
|
|
|
|
|
if ((s2 = VisualizeControlCodes(s, n, &n2))) {
|
|
|
|
|
if ((s3 = IndentLines(s2, n2, &n3, 1))) {
|
|
|
|
|
LOGF("%s %,ld byte payload\n%.*s", d, n, n3, s3);
|
|
|
|
|
free(s3);
|
|
|
|
|
}
|
|
|
|
|
free(s2);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaFetch(lua_State *L) {
|
|
|
|
|
char *p;
|
|
|
|
|
ssize_t rc;
|
|
|
|
|
bool usessl;
|
|
|
|
|
uint32_t ip;
|
|
|
|
|
struct Url url;
|
|
|
|
|
int t, ret, sock;
|
|
|
|
|
char *host, *port;
|
|
|
|
|
struct Buffer inbuf;
|
|
|
|
|
struct addrinfo *addr;
|
|
|
|
|
struct HttpMessage msg;
|
|
|
|
|
struct HttpUnchunker u;
|
|
|
|
|
const char *urlarg, *request;
|
|
|
|
|
size_t urlarglen, requestlen;
|
|
|
|
|
size_t g, i, n, hdrsize, paylen;
|
|
|
|
|
struct addrinfo hints = {.ai_family = AF_INET,
|
|
|
|
|
.ai_socktype = SOCK_STREAM,
|
|
|
|
|
.ai_protocol = IPPROTO_TCP,
|
|
|
|
|
.ai_flags = AI_NUMERICSERV};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Get args.
|
|
|
|
|
*/
|
|
|
|
|
urlarg = luaL_checklstring(L, 1, &urlarglen);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Parse URL.
|
|
|
|
|
*/
|
|
|
|
|
gc(ParseUrl(urlarg, urlarglen, &url));
|
|
|
|
|
gc(url.params.p);
|
|
|
|
|
usessl = false;
|
|
|
|
|
if (url.scheme.n) {
|
|
|
|
|
#ifndef UNSECURE
|
|
|
|
|
if (url.scheme.n == 5 && !memcasecmp(url.scheme.p, "https", 5)) {
|
|
|
|
|
usessl = true;
|
|
|
|
|
} else
|
|
|
|
|
#endif
|
|
|
|
|
if (!(url.scheme.n == 4 && !memcasecmp(url.scheme.p, "http", 4))) {
|
|
|
|
|
luaL_argerror(L, 1, "bad scheme");
|
|
|
|
|
unreachable;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (url.host.n) {
|
|
|
|
|
host = gc(strndup(url.host.p, url.host.n));
|
|
|
|
|
if (url.port.n) {
|
|
|
|
|
port = gc(strndup(url.port.p, url.port.n));
|
|
|
|
|
} else {
|
|
|
|
|
port = usessl ? "443" : "80";
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ip = ntohl(servers.p[0].addr.sin_addr.s_addr);
|
|
|
|
|
host =
|
|
|
|
|
gc(xasprintf("%hhu.%hhu.%hhu.%hhu", ip >> 24, ip >> 16, ip >> 8, ip));
|
|
|
|
|
port = gc(xasprintf("%d", ntohs(servers.p[0].addr.sin_port)));
|
|
|
|
|
}
|
|
|
|
|
if (!IsAcceptableHost(host, -1)) {
|
|
|
|
|
luaL_argerror(L, 1, "invalid host");
|
|
|
|
|
unreachable;
|
|
|
|
|
}
|
|
|
|
|
url.fragment.p = 0, url.fragment.n = 0;
|
|
|
|
|
url.scheme.p = 0, url.scheme.n = 0;
|
|
|
|
|
url.user.p = 0, url.user.n = 0;
|
|
|
|
|
url.pass.p = 0, url.pass.n = 0;
|
|
|
|
|
url.host.p = 0, url.host.n = 0;
|
|
|
|
|
url.port.p = 0, url.port.n = 0;
|
|
|
|
|
if (!url.path.n || url.path.p[0] != '/') {
|
|
|
|
|
p = gc(xmalloc(1 + url.path.n));
|
|
|
|
|
mempcpy(mempcpy(p, "/", 1), url.path.p, url.path.n);
|
|
|
|
|
url.path.p = p;
|
|
|
|
|
++url.path.n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Create HTTP message.
|
|
|
|
|
*/
|
|
|
|
|
request = gc(xasprintf("GET %s HTTP/1.1\r\n"
|
|
|
|
|
"Host: %s:%s\r\n"
|
|
|
|
|
"Connection: close\r\n"
|
|
|
|
|
"User-Agent: %s\r\n"
|
|
|
|
|
"\r\n",
|
|
|
|
|
gc(EncodeUrl(&url, 0)), host, port, brand));
|
|
|
|
|
requestlen = strlen(request);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Perform DNS lookup.
|
|
|
|
|
*/
|
|
|
|
|
DEBUGF("client resolving %s", host);
|
|
|
|
|
if ((rc = getaddrinfo(host, port, &hints, &addr)) != EAI_SUCCESS) {
|
|
|
|
|
luaL_error(L, "getaddrinfo(%s:%s) failed", host, port);
|
|
|
|
|
unreachable;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Connect to server.
|
|
|
|
|
*/
|
|
|
|
|
ip = ntohl(((struct sockaddr_in *)addr->ai_addr)->sin_addr.s_addr);
|
|
|
|
|
DEBUGF("client connecting %hhu.%hhu.%hhu.%hhu:%d", ip >> 24, ip >> 16,
|
|
|
|
|
ip >> 8, ip, ntohs(((struct sockaddr_in *)addr->ai_addr)->sin_port));
|
|
|
|
|
CHECK_NE(-1, (sock = Socket(addr->ai_family, addr->ai_socktype,
|
|
|
|
|
addr->ai_protocol, false)));
|
|
|
|
|
if (connect(sock, addr->ai_addr, addr->ai_addrlen) == -1) {
|
|
|
|
|
close(sock);
|
|
|
|
|
luaL_error(L, "connect(%s:%s) failed: %s", host, port, strerror(errno));
|
|
|
|
|
unreachable;
|
|
|
|
|
}
|
|
|
|
|
if (usessl) {
|
|
|
|
|
if (sslcliused) {
|
|
|
|
|
mbedtls_ssl_session_reset(&sslcli);
|
|
|
|
|
} else {
|
|
|
|
|
ReseedRng(&rngcli, "child");
|
|
|
|
|
}
|
|
|
|
|
sslcliused = true;
|
|
|
|
|
DEBUGF("client handshaking %`'s", host);
|
|
|
|
|
mbedtls_ssl_set_hostname(&sslcli, host);
|
|
|
|
|
mbedtls_ssl_set_bio(&sslcli, &sock, TlsSend, 0, TlsRecvImpl);
|
|
|
|
|
while ((ret = mbedtls_ssl_handshake(&ssl))) {
|
|
|
|
|
switch (ret) {
|
|
|
|
|
case MBEDTLS_ERR_SSL_WANT_READ:
|
|
|
|
|
break;
|
|
|
|
|
case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED:
|
|
|
|
|
goto VerifyFailed;
|
|
|
|
|
default:
|
|
|
|
|
close(sock);
|
|
|
|
|
LuaThrowTlsError(L, "handshake", ret);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
LockInc(&shared->c.sslhandshakes);
|
|
|
|
|
VERBOSEF("SHAKEN %s:%s %s %s", host, port,
|
|
|
|
|
mbedtls_ssl_get_ciphersuite(&ssl), mbedtls_ssl_get_version(&ssl));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Send HTTP Message.
|
|
|
|
|
*/
|
|
|
|
|
DEBUGF("client sending");
|
|
|
|
|
if (usessl) {
|
|
|
|
|
ret = mbedtls_ssl_write(&sslcli, request, requestlen);
|
|
|
|
|
if (ret != requestlen) {
|
|
|
|
|
if (ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) goto VerifyFailed;
|
|
|
|
|
close(sock);
|
|
|
|
|
LuaThrowTlsError(L, "write", ret);
|
|
|
|
|
}
|
|
|
|
|
} else if (write(sock, request, requestlen) != requestlen) {
|
|
|
|
|
close(sock);
|
|
|
|
|
luaL_error(L, "write failed: %s", strerror(errno));
|
|
|
|
|
unreachable;
|
|
|
|
|
}
|
2021-07-08 22:56:23 +00:00
|
|
|
|
if (logmessages) {
|
|
|
|
|
LogMessage("sent", request, requestlen);
|
|
|
|
|
}
|
2021-07-08 04:44:27 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Handle response.
|
|
|
|
|
*/
|
|
|
|
|
memset(&inbuf, 0, sizeof(inbuf));
|
|
|
|
|
InitHttpMessage(&msg, kHttpResponse);
|
|
|
|
|
for (hdrsize = paylen = t = 0;;) {
|
|
|
|
|
if (inbuf.n == inbuf.c) {
|
|
|
|
|
inbuf.c += 1000;
|
|
|
|
|
inbuf.c += inbuf.c >> 1;
|
|
|
|
|
inbuf.p = realloc(inbuf.p, inbuf.c);
|
|
|
|
|
}
|
|
|
|
|
DEBUGF("client reading");
|
|
|
|
|
if (usessl) {
|
|
|
|
|
if ((rc = mbedtls_ssl_read(&sslcli, inbuf.p + inbuf.n,
|
|
|
|
|
inbuf.c - inbuf.n)) < 0) {
|
|
|
|
|
if (rc == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
|
|
|
|
|
rc = 0;
|
|
|
|
|
} else {
|
|
|
|
|
close(sock);
|
|
|
|
|
free(inbuf.p);
|
|
|
|
|
DestroyHttpMessage(&msg);
|
|
|
|
|
LuaThrowTlsError(L, "read", rc);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if ((rc = read(sock, inbuf.p + inbuf.n, inbuf.c - inbuf.n)) == -1) {
|
|
|
|
|
close(sock);
|
|
|
|
|
free(inbuf.p);
|
|
|
|
|
DestroyHttpMessage(&msg);
|
|
|
|
|
luaL_error(L, "read failed: %s", strerror(errno));
|
|
|
|
|
unreachable;
|
|
|
|
|
}
|
|
|
|
|
g = rc;
|
|
|
|
|
inbuf.n += g;
|
|
|
|
|
switch (t) {
|
|
|
|
|
case kHttpClientStateHeaders:
|
|
|
|
|
if (!g) goto TransportError;
|
|
|
|
|
rc = ParseHttpMessage(&msg, inbuf.p, inbuf.n);
|
|
|
|
|
if (rc == -1) goto TransportError;
|
|
|
|
|
if (rc) {
|
|
|
|
|
hdrsize = rc;
|
2021-07-08 22:56:23 +00:00
|
|
|
|
if (logmessages) {
|
|
|
|
|
LogMessage("received", inbuf.p, hdrsize);
|
|
|
|
|
}
|
2021-07-08 04:44:27 +00:00
|
|
|
|
if (100 <= msg.status && msg.status <= 199) {
|
|
|
|
|
if ((HasHeader(kHttpContentLength) &&
|
|
|
|
|
!HeaderEqualCase(kHttpContentLength, "0")) ||
|
|
|
|
|
(HasHeader(kHttpTransferEncoding) &&
|
|
|
|
|
!HeaderEqualCase(kHttpTransferEncoding, "identity"))) {
|
|
|
|
|
goto TransportError;
|
|
|
|
|
}
|
|
|
|
|
DestroyHttpMessage(&msg);
|
|
|
|
|
InitHttpMessage(&msg, kHttpResponse);
|
|
|
|
|
memmove(inbuf.p, inbuf.p + hdrsize, inbuf.n - hdrsize);
|
|
|
|
|
inbuf.n -= hdrsize;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (msg.status == 204 || msg.status == 304) {
|
|
|
|
|
goto Finished;
|
|
|
|
|
}
|
|
|
|
|
if (HasHeader(kHttpTransferEncoding) &&
|
|
|
|
|
!HeaderEqualCase(kHttpTransferEncoding, "identity")) {
|
|
|
|
|
if (HeaderEqualCase(kHttpTransferEncoding, "chunked")) {
|
|
|
|
|
t = kHttpClientStateBodyChunked;
|
|
|
|
|
memset(&u, 0, sizeof(u));
|
|
|
|
|
goto Chunked;
|
|
|
|
|
} else {
|
|
|
|
|
goto TransportError;
|
|
|
|
|
}
|
|
|
|
|
} else if (HasHeader(kHttpContentLength)) {
|
|
|
|
|
rc = ParseContentLength(HeaderData(kHttpContentLength),
|
|
|
|
|
HeaderLength(kHttpContentLength));
|
|
|
|
|
if (rc == -1) goto TransportError;
|
|
|
|
|
if ((paylen = rc) <= inbuf.n - hdrsize) {
|
|
|
|
|
goto Finished;
|
|
|
|
|
} else {
|
|
|
|
|
t = kHttpClientStateBodyLengthed;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
t = kHttpClientStateBody;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case kHttpClientStateBody:
|
|
|
|
|
if (!g) {
|
|
|
|
|
paylen = inbuf.n;
|
|
|
|
|
goto Finished;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case kHttpClientStateBodyLengthed:
|
|
|
|
|
if (!g) goto TransportError;
|
|
|
|
|
if (inbuf.n - hdrsize >= paylen) goto Finished;
|
|
|
|
|
break;
|
|
|
|
|
case kHttpClientStateBodyChunked:
|
|
|
|
|
Chunked:
|
|
|
|
|
rc = Unchunk(&u, inbuf.p + hdrsize, inbuf.n - hdrsize, &paylen);
|
|
|
|
|
if (rc == -1) goto TransportError;
|
|
|
|
|
if (rc) goto Finished;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
unreachable;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Finished:
|
|
|
|
|
if (paylen && logbodies) LogBody("received", inbuf.p + hdrsize, paylen);
|
|
|
|
|
LOGF("FETCH HTTP%02d %d %s %`'.*s", msg.version, msg.status, urlarg,
|
|
|
|
|
HeaderLength(kHttpServer), HeaderData(kHttpServer));
|
|
|
|
|
lua_pushinteger(L, msg.status);
|
|
|
|
|
LuaPushHeaders(L, &msg, inbuf.p);
|
|
|
|
|
lua_pushlstring(L, inbuf.p + hdrsize, paylen);
|
|
|
|
|
DestroyHttpMessage(&msg);
|
|
|
|
|
free(inbuf.p);
|
|
|
|
|
close(sock);
|
|
|
|
|
return 3;
|
|
|
|
|
TransportError:
|
|
|
|
|
close(sock);
|
|
|
|
|
free(inbuf.p);
|
|
|
|
|
DestroyHttpMessage(&msg);
|
|
|
|
|
luaL_error(L, "transport error");
|
|
|
|
|
unreachable;
|
|
|
|
|
VerifyFailed:
|
|
|
|
|
LockInc(&shared->c.sslverifyfailed);
|
|
|
|
|
close(sock);
|
|
|
|
|
LuaThrowTlsError(
|
|
|
|
|
L, gc(DescribeSslVerifyFailure(sslcli.session_negotiate->verify_result)),
|
|
|
|
|
ret);
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-25 00:09:01 +00:00
|
|
|
|
static int LuaGetDate(lua_State *L) {
|
|
|
|
|
lua_pushinteger(L, shared->nowish);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaGetVersion(lua_State *L) {
|
|
|
|
|
lua_pushinteger(L, msg.version);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaGetMethod(lua_State *L) {
|
|
|
|
|
if (msg.method) {
|
|
|
|
|
lua_pushstring(L, kHttpMethod[msg.method]);
|
|
|
|
|
} else {
|
|
|
|
|
lua_pushlstring(L, inbuf.p + msg.xmethod.a, msg.xmethod.b - msg.xmethod.a);
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaGetAddr(lua_State *L, void GetAddr(uint32_t *, uint16_t *)) {
|
|
|
|
|
uint32_t ip;
|
|
|
|
|
uint16_t port;
|
|
|
|
|
GetAddr(&ip, &port);
|
|
|
|
|
lua_pushinteger(L, ip);
|
|
|
|
|
lua_pushinteger(L, port);
|
|
|
|
|
return 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaGetServerAddr(lua_State *L) {
|
|
|
|
|
return LuaGetAddr(L, GetServerAddr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaGetClientAddr(lua_State *L) {
|
|
|
|
|
return LuaGetAddr(L, GetClientAddr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaGetRemoteAddr(lua_State *L) {
|
|
|
|
|
return LuaGetAddr(L, GetRemoteAddr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaFormatIp(lua_State *L) {
|
|
|
|
|
char b[16];
|
|
|
|
|
uint32_t ip;
|
|
|
|
|
ip = htonl(luaL_checkinteger(L, 1));
|
|
|
|
|
inet_ntop(AF_INET, &ip, b, sizeof(b));
|
|
|
|
|
lua_pushstring(L, b);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaParseIp(lua_State *L) {
|
|
|
|
|
size_t n;
|
|
|
|
|
const char *s;
|
|
|
|
|
s = luaL_checklstring(L, 1, &n);
|
|
|
|
|
lua_pushinteger(L, ParseIp(s, n));
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaIsIp(lua_State *L, bool IsIp(uint32_t)) {
|
|
|
|
|
lua_pushboolean(L, IsIp(luaL_checkinteger(L, 1)));
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaIsPublicIp(lua_State *L) {
|
|
|
|
|
return LuaIsIp(L, IsPublicIp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaIsPrivateIp(lua_State *L) {
|
|
|
|
|
return LuaIsIp(L, IsPrivateIp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaIsLoopbackIp(lua_State *L) {
|
|
|
|
|
return LuaIsIp(L, IsLoopbackIp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaCategorizeIp(lua_State *L) {
|
|
|
|
|
lua_pushstring(L, GetIpCategoryName(CategorizeIp(luaL_checkinteger(L, 1))));
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaGetUrl(lua_State *L) {
|
|
|
|
|
char *p;
|
|
|
|
|
size_t n;
|
|
|
|
|
p = EncodeUrl(&url, &n);
|
|
|
|
|
lua_pushlstring(L, p, n);
|
|
|
|
|
free(p);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void LuaPushUrlView(lua_State *L, struct UrlView *v) {
|
|
|
|
|
if (v->p) {
|
|
|
|
|
lua_pushlstring(L, v->p, v->n);
|
|
|
|
|
} else {
|
|
|
|
|
lua_pushnil(L);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaGetScheme(lua_State *L) {
|
|
|
|
|
LuaPushUrlView(L, &url.scheme);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaGetPath(lua_State *L) {
|
|
|
|
|
LuaPushUrlView(L, &url.path);
|
|
|
|
|
return 1;
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static int LuaGetEffectivePath(lua_State *L) {
|
|
|
|
|
lua_pushlstring(L, effectivepath.p, effectivepath.n);
|
2021-04-21 02:14:21 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static int LuaGetFragment(lua_State *L) {
|
|
|
|
|
LuaPushUrlView(L, &url.fragment);
|
2021-04-21 02:14:21 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static int LuaGetUser(lua_State *L) {
|
|
|
|
|
size_t n;
|
|
|
|
|
const char *p, *q;
|
|
|
|
|
if (url.user.p) {
|
|
|
|
|
LuaPushUrlView(L, &url.user);
|
|
|
|
|
} else if ((p = GetBasicAuthorization(&n))) {
|
|
|
|
|
if (!(q = memchr(p, ':', n))) q = p + n;
|
|
|
|
|
lua_pushlstring(L, p, q - p);
|
|
|
|
|
free(p);
|
|
|
|
|
} else {
|
|
|
|
|
lua_pushnil(L);
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaGetPass(lua_State *L) {
|
|
|
|
|
size_t n;
|
|
|
|
|
const char *p, *q;
|
|
|
|
|
if (url.user.p) {
|
|
|
|
|
LuaPushUrlView(L, &url.pass);
|
|
|
|
|
} else if ((p = GetBasicAuthorization(&n))) {
|
|
|
|
|
if ((q = memchr(p, ':', n))) {
|
|
|
|
|
lua_pushlstring(L, q + 1, p + n - (q + 1));
|
|
|
|
|
} else {
|
|
|
|
|
lua_pushnil(L);
|
|
|
|
|
}
|
|
|
|
|
free(p);
|
|
|
|
|
} else {
|
|
|
|
|
lua_pushnil(L);
|
|
|
|
|
}
|
2021-04-21 02:14:21 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaGetHost(lua_State *L) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
char b[16];
|
2021-04-21 02:14:21 +00:00
|
|
|
|
if (url.host.n) {
|
|
|
|
|
lua_pushlstring(L, url.host.p, url.host.n);
|
|
|
|
|
} else {
|
2021-06-24 19:31:26 +00:00
|
|
|
|
inet_ntop(AF_INET, &serveraddr->sin_addr.s_addr, b, sizeof(b));
|
2021-04-23 17:45:19 +00:00
|
|
|
|
lua_pushstring(L, b);
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
2021-04-23 17:45:19 +00:00
|
|
|
|
return 1;
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaGetPort(lua_State *L) {
|
|
|
|
|
int i, x = 0;
|
|
|
|
|
for (i = 0; i < url.port.n; ++i) x = url.port.p[i] - '0' + x * 10;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
if (!x) x = ntohs(serveraddr->sin_port);
|
2021-04-21 02:14:21 +00:00
|
|
|
|
lua_pushinteger(L, x);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-28 14:54:21 +00:00
|
|
|
|
static int LuaFormatHttpDateTime(lua_State *L) {
|
2021-03-25 09:21:13 +00:00
|
|
|
|
char buf[30];
|
2021-03-28 14:54:21 +00:00
|
|
|
|
lua_pushstring(L, FormatUnixHttpDateTime(buf, luaL_checkinteger(L, 1)));
|
2021-03-25 09:21:13 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-28 14:54:21 +00:00
|
|
|
|
static int LuaParseHttpDateTime(lua_State *L) {
|
2021-03-25 09:21:13 +00:00
|
|
|
|
size_t n;
|
|
|
|
|
const char *s;
|
|
|
|
|
s = luaL_checklstring(L, 1, &n);
|
|
|
|
|
lua_pushinteger(L, ParseHttpDateTime(s, n));
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-27 14:29:55 +00:00
|
|
|
|
static int LuaGetPayload(lua_State *L) {
|
2021-07-05 08:03:45 +00:00
|
|
|
|
lua_pushlstring(L, inbuf.p + hdrsize, payloadlength);
|
2021-03-27 14:29:55 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
|
static int LuaGetHeader(lua_State *L) {
|
|
|
|
|
int h;
|
2021-03-28 07:10:17 +00:00
|
|
|
|
const char *key;
|
2021-07-08 04:44:27 +00:00
|
|
|
|
size_t i, keylen;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
key = luaL_checklstring(L, 1, &keylen);
|
|
|
|
|
if ((h = GetHttpHeader(key, keylen)) != -1) {
|
2021-04-18 18:34:59 +00:00
|
|
|
|
if (msg.headers[h].a) {
|
2021-07-08 04:44:27 +00:00
|
|
|
|
return LuaPushHeader(L, &msg, inbuf.p, h);
|
2021-03-27 14:29:55 +00:00
|
|
|
|
}
|
2021-04-18 18:34:59 +00:00
|
|
|
|
} else {
|
|
|
|
|
for (i = 0; i < msg.xheaders.n; ++i) {
|
|
|
|
|
if (SlicesEqualCase(key, keylen, inbuf.p + msg.xheaders.p[i].k.a,
|
|
|
|
|
msg.xheaders.p[i].k.b - msg.xheaders.p[i].k.a)) {
|
|
|
|
|
LuaPushLatin1(L, inbuf.p + msg.xheaders.p[i].v.a,
|
|
|
|
|
msg.xheaders.p[i].v.b - msg.xheaders.p[i].v.a);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-27 14:29:55 +00:00
|
|
|
|
}
|
2021-04-18 18:34:59 +00:00
|
|
|
|
lua_pushnil(L);
|
2021-03-27 14:29:55 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaGetHeaders(lua_State *L) {
|
2021-07-08 04:44:27 +00:00
|
|
|
|
return LuaPushHeaders(L, &msg, inbuf.p);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaSetHeader(lua_State *L) {
|
2021-03-28 07:10:17 +00:00
|
|
|
|
int h;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
ssize_t rc;
|
2021-04-25 00:09:01 +00:00
|
|
|
|
char *p, *q;
|
2021-03-28 07:10:17 +00:00
|
|
|
|
const char *key, *val, *eval;
|
|
|
|
|
size_t i, keylen, vallen, evallen;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
key = luaL_checklstring(L, 1, &keylen);
|
|
|
|
|
val = luaL_checklstring(L, 2, &vallen);
|
2021-03-28 07:10:17 +00:00
|
|
|
|
if ((h = GetHttpHeader(key, keylen)) == -1) {
|
|
|
|
|
if (!IsValidHttpToken(key, keylen)) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
luaL_argerror(L, 1, "invalid");
|
|
|
|
|
unreachable;
|
2021-03-28 07:10:17 +00:00
|
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
2021-03-28 07:10:17 +00:00
|
|
|
|
if (!(eval = EncodeHttpHeaderValue(val, vallen, &evallen))) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
luaL_argerror(L, 2, "invalid");
|
|
|
|
|
unreachable;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
2021-04-25 00:09:01 +00:00
|
|
|
|
p = GetLuaResponse();
|
|
|
|
|
while (p - hdrbuf.p + keylen + 2 + evallen + 2 + 512 > hdrbuf.n) {
|
|
|
|
|
hdrbuf.n += hdrbuf.n >> 1;
|
|
|
|
|
q = xrealloc(hdrbuf.p, hdrbuf.n);
|
|
|
|
|
luaheaderp = p = q + (p - hdrbuf.p);
|
|
|
|
|
hdrbuf.p = q;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
2021-03-28 07:10:17 +00:00
|
|
|
|
switch (h) {
|
2021-03-25 09:21:13 +00:00
|
|
|
|
case kHttpConnection:
|
2021-05-16 04:53:26 +00:00
|
|
|
|
if (SlicesEqualCase(eval, evallen, "close", 5)) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
luaL_argerror(L, 2, "unsupported");
|
|
|
|
|
unreachable;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
connectionclose = true;
|
|
|
|
|
break;
|
|
|
|
|
case kHttpContentType:
|
2021-03-28 07:10:17 +00:00
|
|
|
|
p = AppendContentType(p, eval);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
break;
|
|
|
|
|
case kHttpServer:
|
|
|
|
|
branded = true;
|
2021-03-28 07:10:17 +00:00
|
|
|
|
p = AppendHeader(p, "Server", eval);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
break;
|
|
|
|
|
default:
|
2021-03-28 07:10:17 +00:00
|
|
|
|
p = AppendHeader(p, key, eval);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
luaheaderp = p;
|
2021-03-28 07:10:17 +00:00
|
|
|
|
free(eval);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaHasParam(lua_State *L) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
size_t i, n;
|
|
|
|
|
const char *s;
|
|
|
|
|
s = luaL_checklstring(L, 1, &n);
|
|
|
|
|
for (i = 0; i < url.params.n; ++i) {
|
|
|
|
|
if (SlicesEqual(s, n, url.params.p[i].key.p, url.params.p[i].key.n)) {
|
2021-03-25 09:21:13 +00:00
|
|
|
|
lua_pushboolean(L, true);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
lua_pushboolean(L, false);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaGetParam(lua_State *L) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
size_t i, n;
|
|
|
|
|
const char *s;
|
|
|
|
|
s = luaL_checklstring(L, 1, &n);
|
|
|
|
|
for (i = 0; i < url.params.n; ++i) {
|
|
|
|
|
if (SlicesEqual(s, n, url.params.p[i].key.p, url.params.p[i].key.n)) {
|
|
|
|
|
if (url.params.p[i].val.p) {
|
|
|
|
|
lua_pushlstring(L, url.params.p[i].val.p, url.params.p[i].val.n);
|
|
|
|
|
return 1;
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
lua_pushnil(L);
|
2021-03-22 00:27:53 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-21 02:14:21 +00:00
|
|
|
|
static void LuaPushUrlParams(lua_State *L, struct UrlParams *h) {
|
2021-03-25 09:21:13 +00:00
|
|
|
|
size_t i;
|
2021-07-08 22:56:23 +00:00
|
|
|
|
lua_newtable(L);
|
|
|
|
|
for (i = 0; i < h->n; ++i) {
|
2021-03-25 09:21:13 +00:00
|
|
|
|
lua_newtable(L);
|
2021-07-08 22:56:23 +00:00
|
|
|
|
lua_pushlstring(L, h->p[i].key.p, h->p[i].key.n);
|
|
|
|
|
lua_seti(L, -2, 1);
|
|
|
|
|
if (h->p[i].val.p) {
|
|
|
|
|
lua_pushlstring(L, h->p[i].val.p, h->p[i].val.n);
|
|
|
|
|
lua_seti(L, -2, 2);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
2021-07-08 22:56:23 +00:00
|
|
|
|
lua_seti(L, -2, i + 1);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
2021-04-18 18:34:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaGetParams(lua_State *L) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
LuaPushUrlParams(L, &url.params);
|
2021-04-18 18:34:59 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaParseParams(lua_State *L) {
|
|
|
|
|
void *m;
|
|
|
|
|
size_t size;
|
|
|
|
|
const char *data;
|
|
|
|
|
struct UrlParams h;
|
|
|
|
|
data = luaL_checklstring(L, 1, &size);
|
|
|
|
|
memset(&h, 0, sizeof(h));
|
|
|
|
|
m = ParseParams(data, size, &h);
|
2021-04-21 02:14:21 +00:00
|
|
|
|
LuaPushUrlParams(L, &h);
|
2021-04-18 18:34:59 +00:00
|
|
|
|
free(h.p);
|
|
|
|
|
free(m);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void LuaSetUrlView(lua_State *L, struct UrlView *v, const char *k) {
|
|
|
|
|
LuaPushUrlView(L, v);
|
|
|
|
|
lua_setfield(L, -2, k);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaParseUrl(lua_State *L) {
|
|
|
|
|
void *m;
|
2021-04-21 02:14:21 +00:00
|
|
|
|
size_t n;
|
2021-04-18 18:34:59 +00:00
|
|
|
|
struct Url h;
|
2021-04-21 02:14:21 +00:00
|
|
|
|
const char *p;
|
|
|
|
|
p = luaL_checklstring(L, 1, &n);
|
|
|
|
|
m = ParseUrl(p, n, &h);
|
2021-04-18 18:34:59 +00:00
|
|
|
|
lua_newtable(L);
|
2021-04-21 02:14:21 +00:00
|
|
|
|
LuaSetUrlView(L, &h.scheme, "scheme");
|
2021-04-18 18:34:59 +00:00
|
|
|
|
LuaSetUrlView(L, &h.user, "user");
|
|
|
|
|
LuaSetUrlView(L, &h.pass, "pass");
|
|
|
|
|
LuaSetUrlView(L, &h.host, "host");
|
|
|
|
|
LuaSetUrlView(L, &h.port, "port");
|
|
|
|
|
LuaSetUrlView(L, &h.path, "path");
|
|
|
|
|
LuaSetUrlView(L, &h.fragment, "fragment");
|
2021-04-21 02:14:21 +00:00
|
|
|
|
LuaPushUrlParams(L, &h.params);
|
2021-04-18 18:34:59 +00:00
|
|
|
|
lua_setfield(L, -2, "params");
|
|
|
|
|
free(h.params.p);
|
|
|
|
|
free(m);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-21 02:14:21 +00:00
|
|
|
|
static int LuaParseHost(lua_State *L) {
|
|
|
|
|
void *m;
|
|
|
|
|
size_t n;
|
|
|
|
|
struct Url h;
|
|
|
|
|
const char *p;
|
|
|
|
|
memset(&h, 0, sizeof(h));
|
|
|
|
|
p = luaL_checklstring(L, 1, &n);
|
|
|
|
|
m = ParseHost(p, n, &h);
|
|
|
|
|
lua_newtable(L);
|
|
|
|
|
LuaPushUrlView(L, &h.host);
|
|
|
|
|
LuaPushUrlView(L, &h.port);
|
|
|
|
|
free(m);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaEncodeUrl(lua_State *L) {
|
|
|
|
|
void *m;
|
|
|
|
|
size_t size;
|
|
|
|
|
struct Url h;
|
|
|
|
|
int i, j, k, n;
|
|
|
|
|
const char *data;
|
|
|
|
|
if (!lua_isnil(L, 1)) {
|
|
|
|
|
memset(&h, 0, sizeof(h));
|
|
|
|
|
luaL_checktype(L, 1, LUA_TTABLE);
|
|
|
|
|
if (lua_getfield(L, 1, "scheme"))
|
|
|
|
|
h.scheme.p = lua_tolstring(L, -1, &h.scheme.n);
|
|
|
|
|
if (lua_getfield(L, 1, "fragment"))
|
|
|
|
|
h.fragment.p = lua_tolstring(L, -1, &h.fragment.n);
|
|
|
|
|
if (lua_getfield(L, 1, "user")) h.user.p = lua_tolstring(L, -1, &h.user.n);
|
|
|
|
|
if (lua_getfield(L, 1, "pass")) h.pass.p = lua_tolstring(L, -1, &h.pass.n);
|
|
|
|
|
if (lua_getfield(L, 1, "host")) h.host.p = lua_tolstring(L, -1, &h.host.n);
|
|
|
|
|
if (lua_getfield(L, 1, "port")) h.port.p = lua_tolstring(L, -1, &h.port.n);
|
|
|
|
|
if (lua_getfield(L, 1, "path")) h.path.p = lua_tolstring(L, -1, &h.path.n);
|
|
|
|
|
if (lua_getfield(L, 1, "params")) {
|
|
|
|
|
luaL_checktype(L, -1, LUA_TTABLE);
|
|
|
|
|
lua_len(L, -1);
|
|
|
|
|
n = lua_tointeger(L, -1);
|
|
|
|
|
for (i = -2, k = 0, j = 1; j <= n; ++j) {
|
|
|
|
|
if (lua_geti(L, i--, j)) {
|
|
|
|
|
luaL_checktype(L, -1, LUA_TTABLE);
|
|
|
|
|
if (lua_geti(L, -1, 1)) {
|
|
|
|
|
h.params.p =
|
|
|
|
|
xrealloc(h.params.p, ++h.params.n * sizeof(*h.params.p));
|
|
|
|
|
h.params.p[h.params.n - 1].key.p =
|
|
|
|
|
lua_tolstring(L, -1, &h.params.p[h.params.n - 1].key.n);
|
|
|
|
|
if (lua_geti(L, -2, 2)) {
|
|
|
|
|
h.params.p[h.params.n - 1].val.p =
|
|
|
|
|
lua_tolstring(L, -1, &h.params.p[h.params.n - 1].val.n);
|
|
|
|
|
} else {
|
|
|
|
|
h.params.p[h.params.n - 1].val.p = 0;
|
|
|
|
|
h.params.p[h.params.n - 1].val.n = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
i--;
|
|
|
|
|
}
|
|
|
|
|
i--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
data = EncodeUrl(&h, &size);
|
|
|
|
|
lua_pushlstring(L, data, size);
|
|
|
|
|
free(data);
|
|
|
|
|
} else {
|
|
|
|
|
lua_pushnil(L);
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
|
static int LuaWrite(lua_State *L) {
|
2021-03-22 00:27:53 +00:00
|
|
|
|
size_t size;
|
|
|
|
|
const char *data;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
if (!lua_isnil(L, 1)) {
|
|
|
|
|
data = luaL_checklstring(L, 1, &size);
|
|
|
|
|
AppendData(data, size);
|
|
|
|
|
}
|
2021-03-22 00:27:53 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static int LuaCheckControlFlags(lua_State *L, int idx) {
|
|
|
|
|
int f = luaL_checkinteger(L, idx);
|
|
|
|
|
if (f & ~(kControlWs | kControlC0 | kControlC1)) {
|
|
|
|
|
luaL_argerror(L, idx, "invalid control flags");
|
|
|
|
|
unreachable;
|
|
|
|
|
}
|
|
|
|
|
return f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaHasControlCodes(lua_State *L) {
|
|
|
|
|
int f;
|
|
|
|
|
size_t n;
|
|
|
|
|
const char *p;
|
|
|
|
|
p = luaL_checklstring(L, 1, &n);
|
|
|
|
|
f = LuaCheckControlFlags(L, 2);
|
2021-05-02 18:11:26 +00:00
|
|
|
|
lua_pushboolean(L, HasControlCodes(p, n, f) != -1);
|
2021-04-18 18:34:59 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-16 04:53:26 +00:00
|
|
|
|
static int LuaIsValid(lua_State *L, bool V(const char *, size_t)) {
|
2021-04-18 18:34:59 +00:00
|
|
|
|
size_t size;
|
|
|
|
|
const char *data;
|
|
|
|
|
data = luaL_checklstring(L, 1, &size);
|
2021-05-16 04:53:26 +00:00
|
|
|
|
lua_pushboolean(L, V(data, size));
|
2021-04-18 18:34:59 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static int LuaIsValidHttpToken(lua_State *L) {
|
|
|
|
|
return LuaIsValid(L, IsValidHttpToken);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaIsAcceptablePath(lua_State *L) {
|
|
|
|
|
return LuaIsValid(L, IsAcceptablePath);
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-25 00:09:01 +00:00
|
|
|
|
static int LuaIsReasonablePath(lua_State *L) {
|
|
|
|
|
return LuaIsValid(L, IsReasonablePath);
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-21 02:14:21 +00:00
|
|
|
|
static int LuaIsAcceptableHost(lua_State *L) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
return LuaIsValid(L, IsAcceptableHost);
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaIsAcceptablePort(lua_State *L) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
return LuaIsValid(L, IsAcceptablePort);
|
2021-04-18 18:34:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-05 14:25:39 +00:00
|
|
|
|
static noinline int LuaCoderImpl(lua_State *L,
|
2021-05-14 12:36:58 +00:00
|
|
|
|
char *C(const char *, size_t, size_t *)) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
void *p;
|
|
|
|
|
size_t n;
|
|
|
|
|
p = luaL_checklstring(L, 1, &n);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
p = C(p, n, &n);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
lua_pushlstring(L, p, n);
|
|
|
|
|
free(p);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-05 14:25:39 +00:00
|
|
|
|
static noinline int LuaCoder(lua_State *L,
|
2021-05-14 12:36:58 +00:00
|
|
|
|
char *C(const char *, size_t, size_t *)) {
|
|
|
|
|
return LuaCoderImpl(L, C);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaUnderlong(lua_State *L) {
|
|
|
|
|
return LuaCoder(L, Underlong);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaEncodeBase64(lua_State *L) {
|
|
|
|
|
return LuaCoder(L, EncodeBase64);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaDecodeBase64(lua_State *L) {
|
|
|
|
|
return LuaCoder(L, DecodeBase64);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaDecodeLatin1(lua_State *L) {
|
|
|
|
|
return LuaCoder(L, DecodeLatin1);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
|
static int LuaEscapeHtml(lua_State *L) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
return LuaCoder(L, EscapeHtml);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaEscapeParam(lua_State *L) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
return LuaCoder(L, EscapeParam);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaEscapePath(lua_State *L) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
return LuaCoder(L, EscapePath);
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaEscapeHost(lua_State *L) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
return LuaCoder(L, EscapeHost);
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaEscapeIp(lua_State *L) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
return LuaCoder(L, EscapeIp);
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaEscapeUser(lua_State *L) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
return LuaCoder(L, EscapeUser);
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaEscapePass(lua_State *L) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
return LuaCoder(L, EscapePass);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaEscapeSegment(lua_State *L) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
return LuaCoder(L, EscapeSegment);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaEscapeFragment(lua_State *L) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
return LuaCoder(L, EscapeFragment);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaEscapeLiteral(lua_State *L) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
return LuaCoder(L, EscapeJsStringLiteral);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static int LuaVisualizeControlCodes(lua_State *L) {
|
|
|
|
|
return LuaCoder(L, VisualizeControlCodes);
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-05 01:36:47 +00:00
|
|
|
|
static int Sha224(const void *p, size_t n, uint8_t d[28]) {
|
|
|
|
|
return mbedtls_sha256_ret(p, n, d, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int Sha256(const void *p, size_t n, uint8_t d[32]) {
|
|
|
|
|
return mbedtls_sha256_ret(p, n, d, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int Sha384(const void *p, size_t n, uint8_t d[48]) {
|
|
|
|
|
return mbedtls_sha512_ret(p, n, d, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int Sha512(const void *p, size_t n, uint8_t d[64]) {
|
|
|
|
|
return mbedtls_sha512_ret(p, n, d, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static noinline int LuaHasherImpl(lua_State *L, size_t k,
|
|
|
|
|
int H(const void *, size_t, uint8_t *)) {
|
|
|
|
|
void *p;
|
|
|
|
|
size_t n;
|
|
|
|
|
uint8_t d[64];
|
|
|
|
|
p = luaL_checklstring(L, 1, &n);
|
|
|
|
|
H(p, n, d);
|
|
|
|
|
lua_pushlstring(L, (void *)d, k);
|
|
|
|
|
mbedtls_platform_zeroize(d, sizeof(d));
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static noinline int LuaHasher(lua_State *L, size_t k,
|
|
|
|
|
int H(const void *, size_t, uint8_t *)) {
|
|
|
|
|
return LuaHasherImpl(L, k, H);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaMd5(lua_State *L) {
|
|
|
|
|
return LuaHasher(L, 16, mbedtls_md5_ret);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaSha1(lua_State *L) {
|
|
|
|
|
return LuaHasher(L, 20, mbedtls_sha1_ret);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaSha224(lua_State *L) {
|
|
|
|
|
return LuaHasher(L, 28, Sha224);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaSha256(lua_State *L) {
|
|
|
|
|
return LuaHasher(L, 32, Sha256);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaSha384(lua_State *L) {
|
|
|
|
|
return LuaHasher(L, 48, Sha384);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaSha512(lua_State *L) {
|
|
|
|
|
return LuaHasher(L, 64, Sha512);
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-08 04:44:27 +00:00
|
|
|
|
static int LuaGetHttpReason(lua_State *L) {
|
|
|
|
|
lua_pushstring(L, GetHttpReason(luaL_checkinteger(L, 1)));
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static int LuaEncodeLatin1(lua_State *L) {
|
|
|
|
|
int f;
|
2021-03-28 01:17:54 +00:00
|
|
|
|
char *p;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
size_t n;
|
|
|
|
|
p = luaL_checklstring(L, 1, &n);
|
|
|
|
|
f = LuaCheckControlFlags(L, 2);
|
|
|
|
|
p = EncodeLatin1(p, n, &n, f);
|
2021-03-28 01:17:54 +00:00
|
|
|
|
lua_pushlstring(L, p, n);
|
|
|
|
|
free(p);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-08 20:21:16 +00:00
|
|
|
|
static int LuaSlurp(lua_State *L) {
|
|
|
|
|
char *p;
|
|
|
|
|
size_t n;
|
|
|
|
|
p = xslurp(luaL_checkstring(L, 1), &n);
|
|
|
|
|
lua_pushlstring(L, p, n);
|
|
|
|
|
free(p);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static int LuaIndentLines(lua_State *L) {
|
|
|
|
|
void *p;
|
|
|
|
|
size_t n, j;
|
|
|
|
|
p = luaL_checklstring(L, 1, &n);
|
|
|
|
|
j = luaL_optinteger(L, 2, 1);
|
|
|
|
|
if (!(0 <= j && j <= 65535)) {
|
|
|
|
|
luaL_argerror(L, 2, "not in range 0..65535");
|
|
|
|
|
unreachable;
|
|
|
|
|
}
|
|
|
|
|
p = IndentLines(p, n, &n, j);
|
2021-03-28 01:17:54 +00:00
|
|
|
|
lua_pushlstring(L, p, n);
|
|
|
|
|
free(p);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static int LuaGetMonospaceWidth(lua_State *L) {
|
|
|
|
|
int w;
|
|
|
|
|
if (lua_isinteger(L, 1)) {
|
|
|
|
|
w = wcwidth(lua_tointeger(L, 1));
|
|
|
|
|
} else if (lua_isstring(L, 1)) {
|
|
|
|
|
w = strwidth(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0) & 7);
|
|
|
|
|
} else {
|
|
|
|
|
luaL_argerror(L, 1, "not integer or string");
|
|
|
|
|
unreachable;
|
|
|
|
|
}
|
|
|
|
|
lua_pushinteger(L, w);
|
2021-04-21 02:14:21 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-27 14:29:55 +00:00
|
|
|
|
static int LuaPopcnt(lua_State *L) {
|
|
|
|
|
lua_pushinteger(L, popcnt(luaL_checkinteger(L, 1)));
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaBsr(lua_State *L) {
|
|
|
|
|
long x;
|
|
|
|
|
if ((x = luaL_checkinteger(L, 1))) {
|
|
|
|
|
lua_pushinteger(L, bsr(x));
|
|
|
|
|
return 1;
|
|
|
|
|
} else {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
luaL_argerror(L, 1, "zero");
|
|
|
|
|
unreachable;
|
2021-03-27 14:29:55 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaBsf(lua_State *L) {
|
|
|
|
|
long x;
|
|
|
|
|
if ((x = luaL_checkinteger(L, 1))) {
|
|
|
|
|
lua_pushinteger(L, bsf(x));
|
|
|
|
|
return 1;
|
|
|
|
|
} else {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
luaL_argerror(L, 1, "zero");
|
|
|
|
|
unreachable;
|
2021-03-27 14:29:55 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static int LuaHash(lua_State *L, uint32_t H(uint32_t, const void *, size_t)) {
|
2021-03-28 14:54:21 +00:00
|
|
|
|
long i;
|
|
|
|
|
size_t n;
|
|
|
|
|
const char *p;
|
|
|
|
|
i = luaL_checkinteger(L, 1);
|
|
|
|
|
p = luaL_checklstring(L, 2, &n);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
lua_pushinteger(L, H(i, p, n));
|
2021-03-28 14:54:21 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static int LuaCrc32(lua_State *L) {
|
|
|
|
|
return LuaHash(L, crc32_z);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-28 14:54:21 +00:00
|
|
|
|
static int LuaCrc32c(lua_State *L) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
return LuaHash(L, crc32c);
|
2021-03-28 14:54:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-14 12:36:58 +00:00
|
|
|
|
static noinline int LuaProgramInt(lua_State *L, void P(long)) {
|
|
|
|
|
P(luaL_checkinteger(L, 1));
|
2021-03-29 08:22:49 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static int LuaProgramPort(lua_State *L) {
|
|
|
|
|
return LuaProgramInt(L, ProgramPort);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-29 08:22:49 +00:00
|
|
|
|
static int LuaProgramCache(lua_State *L) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
return LuaProgramInt(L, ProgramCache);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaProgramTimeout(lua_State *L) {
|
|
|
|
|
return LuaProgramInt(L, ProgramTimeout);
|
2021-03-29 08:22:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-07-08 22:56:23 +00:00
|
|
|
|
static int LuaProgramUid(lua_State *L) {
|
|
|
|
|
return LuaProgramInt(L, ProgramUid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaProgramGid(lua_State *L) {
|
|
|
|
|
return LuaProgramInt(L, ProgramGid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static noinline int LuaProgramString(lua_State *L, void P(const char *)) {
|
|
|
|
|
P(luaL_checkstring(L, 1));
|
2021-03-29 08:22:49 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-08 22:56:23 +00:00
|
|
|
|
static int LuaProgramAddr(lua_State *L) {
|
|
|
|
|
return LuaProgramString(L, ProgramAddr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaProgramBrand(lua_State *L) {
|
|
|
|
|
return LuaProgramString(L, ProgramBrand);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaProgramDirectory(lua_State *L) {
|
|
|
|
|
return LuaProgramString(L, ProgramDirectory);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaProgramLogPath(lua_State *L) {
|
|
|
|
|
return LuaProgramString(L, ProgramLogPath);
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-24 19:31:26 +00:00
|
|
|
|
static int LuaProgramPrivateKey(lua_State *L) {
|
|
|
|
|
#ifndef UNSECURE
|
|
|
|
|
size_t n;
|
|
|
|
|
const char *p;
|
|
|
|
|
p = luaL_checklstring(L, 1, &n);
|
|
|
|
|
ProgramPrivateKey(p, n);
|
|
|
|
|
#endif
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaProgramCertificate(lua_State *L) {
|
|
|
|
|
#ifndef UNSECURE
|
|
|
|
|
size_t n;
|
|
|
|
|
const char *p;
|
|
|
|
|
p = luaL_checklstring(L, 1, &n);
|
|
|
|
|
ProgramCertificate(p, n);
|
|
|
|
|
#endif
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static int LuaProgramHeader(lua_State *L) {
|
2021-05-14 12:36:58 +00:00
|
|
|
|
ProgramHeader(
|
|
|
|
|
gc(xasprintf("%s: %s", luaL_checkstring(L, 1), luaL_checkstring(L, 2))));
|
2021-04-23 17:45:19 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-29 08:22:49 +00:00
|
|
|
|
static int LuaProgramRedirect(lua_State *L) {
|
2021-05-14 12:36:58 +00:00
|
|
|
|
int code;
|
|
|
|
|
const char *from, *to;
|
|
|
|
|
code = luaL_checkinteger(L, 1);
|
|
|
|
|
from = luaL_checkstring(L, 2);
|
|
|
|
|
to = luaL_checkstring(L, 3);
|
|
|
|
|
ProgramRedirect(code, strdup(from), strlen(from), strdup(to), strlen(to));
|
2021-03-29 08:22:49 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-08 22:56:23 +00:00
|
|
|
|
static noinline int LuaProgramBool(lua_State *L, bool *b) {
|
2021-07-08 04:44:27 +00:00
|
|
|
|
*b = lua_toboolean(L, 1);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaProgramSslClientVerify(lua_State *L) {
|
|
|
|
|
return LuaProgramBool(L, &sslclientverify);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaProgramSslFetchVerify(lua_State *L) {
|
|
|
|
|
return LuaProgramBool(L, &sslfetchverify);
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-08 22:56:23 +00:00
|
|
|
|
static int LuaProgramLogMessages(lua_State *L) {
|
|
|
|
|
return LuaProgramBool(L, &logmessages);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaProgramLogBodies(lua_State *L) {
|
|
|
|
|
return LuaProgramBool(L, &logbodies);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-29 08:22:49 +00:00
|
|
|
|
static int LuaGetLogLevel(lua_State *L) {
|
|
|
|
|
lua_pushinteger(L, __log_level);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaSetLogLevel(lua_State *L) {
|
|
|
|
|
__log_level = luaL_checkinteger(L, 1);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-29 09:39:20 +00:00
|
|
|
|
static int LuaHidePath(lua_State *L) {
|
2021-04-18 18:34:59 +00:00
|
|
|
|
size_t pathlen;
|
|
|
|
|
const char *path;
|
|
|
|
|
path = luaL_checklstring(L, 1, &pathlen);
|
2021-06-10 15:55:04 +00:00
|
|
|
|
AddString(&hidepaths, memcpy(malloc(pathlen), path, pathlen), pathlen);
|
2021-03-29 09:39:20 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-29 08:22:49 +00:00
|
|
|
|
static int LuaLog(lua_State *L) {
|
|
|
|
|
int level;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
char *module;
|
2021-03-29 08:22:49 +00:00
|
|
|
|
lua_Debug ar;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
const char *msg;
|
2021-03-29 08:22:49 +00:00
|
|
|
|
level = luaL_checkinteger(L, 1);
|
|
|
|
|
if (LOGGABLE(level)) {
|
|
|
|
|
msg = luaL_checkstring(L, 2);
|
|
|
|
|
lua_getstack(L, 1, &ar);
|
|
|
|
|
lua_getinfo(L, "nSl", &ar);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
if (!strcmp(ar.name, "main")) {
|
|
|
|
|
module = strndup(effectivepath.p, effectivepath.n);
|
|
|
|
|
flogf(level, module, ar.currentline, NULL, "%s", msg);
|
|
|
|
|
free(module);
|
|
|
|
|
} else {
|
|
|
|
|
flogf(level, ar.name, ar.currentline, NULL, "%s", msg);
|
|
|
|
|
}
|
2021-03-29 08:22:49 +00:00
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-29 09:39:20 +00:00
|
|
|
|
static int LuaIsHiddenPath(lua_State *L) {
|
2021-05-14 12:36:58 +00:00
|
|
|
|
size_t n;
|
|
|
|
|
const char *s;
|
|
|
|
|
s = luaL_checklstring(L, 1, &n);
|
|
|
|
|
lua_pushboolean(L, IsHiddenPath(s, n));
|
2021-03-29 09:39:20 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaGetZipPaths(lua_State *L) {
|
|
|
|
|
char *path;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
uint8_t *zcf;
|
2021-03-29 09:39:20 +00:00
|
|
|
|
size_t i, n, pathlen;
|
|
|
|
|
lua_newtable(L);
|
|
|
|
|
i = 0;
|
2021-05-02 18:50:43 +00:00
|
|
|
|
n = GetZipCdirRecords(zcdir);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
for (zcf = zbase + GetZipCdirOffset(zcdir); n--;
|
|
|
|
|
zcf += ZIP_CFILE_HDRSIZE(zcf)) {
|
|
|
|
|
CHECK_EQ(kZipCfileHdrMagic, ZIP_CFILE_MAGIC(zcf));
|
|
|
|
|
path = GetAssetPath(zcf, &pathlen);
|
2021-03-29 09:39:20 +00:00
|
|
|
|
lua_pushlstring(L, path, pathlen);
|
|
|
|
|
lua_seti(L, -2, ++i);
|
|
|
|
|
free(path);
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-21 02:14:21 +00:00
|
|
|
|
static int LuaGetAssetMode(lua_State *L) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
size_t pathlen;
|
2021-04-21 02:14:21 +00:00
|
|
|
|
struct Asset *a;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
const char *path;
|
|
|
|
|
path = LuaCheckPath(L, 1, &pathlen);
|
|
|
|
|
if ((a = GetAsset(path, pathlen))) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
lua_pushinteger(L, GetMode(a));
|
|
|
|
|
} else {
|
|
|
|
|
lua_pushnil(L);
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaGetLastModifiedTime(lua_State *L) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
size_t pathlen;
|
2021-04-21 02:14:21 +00:00
|
|
|
|
struct Asset *a;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
const char *path;
|
|
|
|
|
path = LuaCheckPath(L, 1, &pathlen);
|
|
|
|
|
if ((a = GetAsset(path, pathlen))) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
if (a->file) {
|
|
|
|
|
lua_pushinteger(L, a->file->st.st_mtim.tv_sec);
|
|
|
|
|
} else {
|
2021-05-14 12:36:58 +00:00
|
|
|
|
lua_pushinteger(L, GetZipCfileLastModified(zbase + a->cf));
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
lua_pushnil(L);
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaGetAssetSize(lua_State *L) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
size_t pathlen;
|
2021-04-21 02:14:21 +00:00
|
|
|
|
struct Asset *a;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
const char *path;
|
|
|
|
|
path = LuaCheckPath(L, 1, &pathlen);
|
|
|
|
|
if ((a = GetAsset(path, pathlen))) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
if (a->file) {
|
|
|
|
|
lua_pushinteger(L, a->file->st.st_size);
|
|
|
|
|
} else {
|
2021-05-14 12:36:58 +00:00
|
|
|
|
lua_pushinteger(L, GetZipLfileUncompressedSize(zbase + a->lf));
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
lua_pushnil(L);
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaIsCompressed(lua_State *L) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
size_t pathlen;
|
2021-04-21 02:14:21 +00:00
|
|
|
|
struct Asset *a;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
const char *path;
|
|
|
|
|
path = LuaCheckPath(L, 1, &pathlen);
|
|
|
|
|
if ((a = GetAsset(path, pathlen))) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
lua_pushboolean(L, IsCompressed(a));
|
|
|
|
|
} else {
|
|
|
|
|
lua_pushnil(L);
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaGetComment(lua_State *L) {
|
|
|
|
|
struct Asset *a;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
const char *path;
|
|
|
|
|
size_t pathlen, m;
|
|
|
|
|
path = LuaCheckPath(L, 1, &pathlen);
|
|
|
|
|
if ((a = GetAssetZip(path, pathlen)) &&
|
2021-05-14 12:36:58 +00:00
|
|
|
|
(m = strnlen(ZIP_CFILE_COMMENT(zbase + a->cf),
|
|
|
|
|
ZIP_CFILE_COMMENTSIZE(zbase + a->cf)))) {
|
|
|
|
|
lua_pushlstring(L, ZIP_CFILE_COMMENT(zbase + a->cf), m);
|
2021-04-21 02:14:21 +00:00
|
|
|
|
} else {
|
|
|
|
|
lua_pushnil(L);
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static void LuaSetIntField(lua_State *L, const char *k, lua_Integer v) {
|
|
|
|
|
lua_pushinteger(L, v);
|
|
|
|
|
lua_setfield(L, -2, k);
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-18 14:24:36 +00:00
|
|
|
|
static int LuaLaunchBrowser(lua_State *L) {
|
2021-06-24 19:31:26 +00:00
|
|
|
|
launchbrowser = strdup(luaL_optstring(L, 1, "/"));
|
|
|
|
|
return 0;
|
2021-04-18 14:24:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static regex_t *LuaReCompileImpl(lua_State *L, const char *p, int f) {
|
|
|
|
|
int e;
|
2021-04-21 02:14:21 +00:00
|
|
|
|
regex_t *r;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
r = xmalloc(sizeof(regex_t));
|
|
|
|
|
f &= REG_EXTENDED | REG_ICASE | REG_NEWLINE | REG_NOSUB;
|
|
|
|
|
f ^= REG_EXTENDED;
|
|
|
|
|
if ((e = regcomp(r, p, f)) != REG_OK) {
|
|
|
|
|
free(r);
|
|
|
|
|
luaL_error(L, "regcomp(%s) → REG_%s", p,
|
|
|
|
|
kRegCode[MAX(0, MIN(ARRAYLEN(kRegCode) - 1, e))]);
|
|
|
|
|
unreachable;
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
2021-04-23 17:45:19 +00:00
|
|
|
|
return r;
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static int LuaReSearchImpl(lua_State *L, regex_t *r, const char *s, int f) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
int i, n;
|
|
|
|
|
regmatch_t *m;
|
|
|
|
|
n = r->re_nsub + 1;
|
|
|
|
|
m = xcalloc(n, sizeof(regmatch_t));
|
2021-04-23 17:45:19 +00:00
|
|
|
|
if (regexec(r, s, n, m, f >> 8) == REG_OK) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
|
|
|
lua_pushlstring(L, s + m[i].rm_so, m[i].rm_eo - m[i].rm_so);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
n = 0;
|
|
|
|
|
}
|
|
|
|
|
free(m);
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static int LuaReSearch(lua_State *L) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
regex_t *r;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
int i, e, n, f;
|
|
|
|
|
const char *p, *s;
|
|
|
|
|
p = luaL_checkstring(L, 1);
|
|
|
|
|
s = luaL_checkstring(L, 2);
|
|
|
|
|
f = luaL_optinteger(L, 3, 0);
|
|
|
|
|
if (f & ~(REG_EXTENDED | REG_ICASE | REG_NEWLINE | REG_NOSUB |
|
|
|
|
|
REG_NOTBOL << 8 | REG_NOTEOL << 8)) {
|
|
|
|
|
luaL_argerror(L, 3, "invalid flags");
|
|
|
|
|
unreachable;
|
|
|
|
|
}
|
|
|
|
|
r = LuaReCompileImpl(L, p, f);
|
|
|
|
|
n = LuaReSearchImpl(L, r, s, f);
|
|
|
|
|
regfree(r);
|
|
|
|
|
free(r);
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaReCompile(lua_State *L) {
|
|
|
|
|
int f, e;
|
|
|
|
|
const char *p;
|
|
|
|
|
regex_t *r, **u;
|
|
|
|
|
p = luaL_checkstring(L, 1);
|
|
|
|
|
f = luaL_optinteger(L, 2, 0);
|
|
|
|
|
if (f & ~(REG_EXTENDED | REG_ICASE | REG_NEWLINE | REG_NOSUB)) {
|
|
|
|
|
luaL_argerror(L, 3, "invalid flags");
|
|
|
|
|
unreachable;
|
|
|
|
|
}
|
|
|
|
|
r = LuaReCompileImpl(L, p, f);
|
|
|
|
|
u = lua_newuserdata(L, sizeof(regex_t *));
|
|
|
|
|
luaL_setmetatable(L, "regex_t*");
|
|
|
|
|
*u = r;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaReRegexSearch(lua_State *L) {
|
|
|
|
|
int f;
|
|
|
|
|
regex_t **u;
|
|
|
|
|
const char *s;
|
|
|
|
|
u = luaL_checkudata(L, 1, "regex_t*");
|
|
|
|
|
s = luaL_checkstring(L, 2);
|
|
|
|
|
f = luaL_optinteger(L, 3, 0);
|
|
|
|
|
if (!*u) {
|
|
|
|
|
luaL_argerror(L, 1, "destroyed");
|
|
|
|
|
unreachable;
|
|
|
|
|
}
|
|
|
|
|
if (f & ~(REG_NOTBOL << 8 | REG_NOTEOL << 8)) {
|
|
|
|
|
luaL_argerror(L, 3, "invalid flags");
|
|
|
|
|
unreachable;
|
|
|
|
|
}
|
|
|
|
|
return LuaReSearchImpl(L, *u, s, f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaReRegexGc(lua_State *L) {
|
|
|
|
|
regex_t **u;
|
|
|
|
|
u = luaL_checkudata(L, 1, "regex_t*");
|
|
|
|
|
if (*u) {
|
|
|
|
|
regfree(*u);
|
|
|
|
|
free(*u);
|
|
|
|
|
*u = NULL;
|
|
|
|
|
}
|
2021-04-21 02:14:21 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static const luaL_Reg kLuaRe[] = {
|
|
|
|
|
{"compile", LuaReCompile}, //
|
|
|
|
|
{"search", LuaReSearch}, //
|
|
|
|
|
{NULL, NULL}, //
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const luaL_Reg kLuaReRegexMeth[] = {
|
|
|
|
|
{"search", LuaReRegexSearch}, //
|
|
|
|
|
{NULL, NULL}, //
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const luaL_Reg kLuaReRegexMeta[] = {
|
|
|
|
|
{"__gc", LuaReRegexGc}, //
|
|
|
|
|
{NULL, NULL}, //
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void LuaReRegex(lua_State *L) {
|
|
|
|
|
luaL_newmetatable(L, "regex_t*");
|
|
|
|
|
luaL_setfuncs(L, kLuaReRegexMeta, 0);
|
|
|
|
|
luaL_newlibtable(L, kLuaReRegexMeth);
|
|
|
|
|
luaL_setfuncs(L, kLuaReRegexMeth, 0);
|
|
|
|
|
lua_setfield(L, -2, "__index");
|
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int LuaRe(lua_State *L) {
|
|
|
|
|
luaL_newlib(L, kLuaRe);
|
|
|
|
|
LuaSetIntField(L, "BASIC", REG_EXTENDED);
|
|
|
|
|
LuaSetIntField(L, "ICASE", REG_ICASE);
|
|
|
|
|
LuaSetIntField(L, "NEWLINE", REG_NEWLINE);
|
|
|
|
|
LuaSetIntField(L, "NOSUB", REG_NOSUB);
|
|
|
|
|
LuaSetIntField(L, "NOTBOL", REG_NOTBOL << 8);
|
|
|
|
|
LuaSetIntField(L, "NOTEOL", REG_NOTEOL << 8);
|
|
|
|
|
LuaReRegex(L);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool LuaRun(const char *path) {
|
|
|
|
|
size_t pathlen;
|
2021-03-27 05:31:41 +00:00
|
|
|
|
struct Asset *a;
|
|
|
|
|
const char *code;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
pathlen = strlen(path);
|
|
|
|
|
if ((a = GetAsset(path, pathlen))) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
if ((code = LoadAsset(a, NULL))) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
effectivepath.p = path;
|
|
|
|
|
effectivepath.n = pathlen;
|
|
|
|
|
DEBUGF("LuaRun(%`'s)", path);
|
2021-04-21 02:14:21 +00:00
|
|
|
|
if (luaL_dostring(L, code) != LUA_OK) {
|
|
|
|
|
WARNF("%s %s", path, lua_tostring(L, -1));
|
|
|
|
|
}
|
|
|
|
|
free(code);
|
2021-03-27 05:31:41 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-23 17:45:19 +00:00
|
|
|
|
return !!a;
|
2021-03-27 05:31:41 +00:00
|
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
|
|
2021-03-22 00:27:53 +00:00
|
|
|
|
static const luaL_Reg kLuaFuncs[] = {
|
2021-07-08 04:44:27 +00:00
|
|
|
|
{"CategorizeIp", LuaCategorizeIp}, //
|
|
|
|
|
{"DecodeBase64", LuaDecodeBase64}, //
|
|
|
|
|
{"DecodeLatin1", LuaDecodeLatin1}, //
|
|
|
|
|
{"EncodeBase64", LuaEncodeBase64}, //
|
|
|
|
|
{"EncodeLatin1", LuaEncodeLatin1}, //
|
|
|
|
|
{"EncodeUrl", LuaEncodeUrl}, //
|
|
|
|
|
{"EscapeFragment", LuaEscapeFragment}, //
|
|
|
|
|
{"EscapeHost", LuaEscapeHost}, //
|
|
|
|
|
{"EscapeHtml", LuaEscapeHtml}, //
|
|
|
|
|
{"EscapeIp", LuaEscapeIp}, //
|
|
|
|
|
{"EscapeLiteral", LuaEscapeLiteral}, //
|
|
|
|
|
{"EscapeParam", LuaEscapeParam}, //
|
|
|
|
|
{"EscapePass", LuaEscapePass}, //
|
|
|
|
|
{"EscapePath", LuaEscapePath}, //
|
|
|
|
|
{"EscapeSegment", LuaEscapeSegment}, //
|
|
|
|
|
{"EscapeUser", LuaEscapeUser}, //
|
|
|
|
|
{"Fetch", LuaFetch}, //
|
|
|
|
|
{"FormatHttpDateTime", LuaFormatHttpDateTime}, //
|
|
|
|
|
{"FormatIp", LuaFormatIp}, //
|
|
|
|
|
{"GetAssetMode", LuaGetAssetMode}, //
|
|
|
|
|
{"GetAssetSize", LuaGetAssetSize}, //
|
|
|
|
|
{"GetClientAddr", LuaGetClientAddr}, //
|
|
|
|
|
{"GetComment", LuaGetComment}, //
|
|
|
|
|
{"GetDate", LuaGetDate}, //
|
|
|
|
|
{"GetEffectivePath", LuaGetEffectivePath}, //
|
|
|
|
|
{"GetFragment", LuaGetFragment}, //
|
|
|
|
|
{"GetHeader", LuaGetHeader}, //
|
|
|
|
|
{"GetHeaders", LuaGetHeaders}, //
|
|
|
|
|
{"GetHost", LuaGetHost}, //
|
|
|
|
|
{"GetHttpReason", LuaGetHttpReason}, //
|
|
|
|
|
{"GetLastModifiedTime", LuaGetLastModifiedTime}, //
|
|
|
|
|
{"GetLogLevel", LuaGetLogLevel}, //
|
|
|
|
|
{"GetMethod", LuaGetMethod}, //
|
|
|
|
|
{"GetMonospaceWidth", LuaGetMonospaceWidth}, //
|
|
|
|
|
{"GetParam", LuaGetParam}, //
|
|
|
|
|
{"GetParams", LuaGetParams}, //
|
|
|
|
|
{"GetPass", LuaGetPass}, //
|
|
|
|
|
{"GetPath", LuaGetPath}, //
|
|
|
|
|
{"GetPayload", LuaGetPayload}, //
|
|
|
|
|
{"GetPort", LuaGetPort}, //
|
|
|
|
|
{"GetRemoteAddr", LuaGetRemoteAddr}, //
|
|
|
|
|
{"GetScheme", LuaGetScheme}, //
|
|
|
|
|
{"GetServerAddr", LuaGetServerAddr}, //
|
|
|
|
|
{"GetUrl", LuaGetUrl}, //
|
|
|
|
|
{"GetUser", LuaGetUser}, //
|
|
|
|
|
{"GetVersion", LuaGetVersion}, //
|
|
|
|
|
{"GetZipPaths", LuaGetZipPaths}, //
|
|
|
|
|
{"HasControlCodes", LuaHasControlCodes}, //
|
|
|
|
|
{"HasParam", LuaHasParam}, //
|
|
|
|
|
{"HidePath", LuaHidePath}, //
|
|
|
|
|
{"IndentLines", LuaIndentLines}, //
|
|
|
|
|
{"IsAcceptableHost", LuaIsAcceptableHost}, //
|
|
|
|
|
{"IsAcceptablePath", LuaIsAcceptablePath}, //
|
|
|
|
|
{"IsAcceptablePort", LuaIsAcceptablePort}, //
|
|
|
|
|
{"IsCompressed", LuaIsCompressed}, //
|
|
|
|
|
{"IsHiddenPath", LuaIsHiddenPath}, //
|
|
|
|
|
{"IsLoopbackIp", LuaIsLoopbackIp}, //
|
|
|
|
|
{"IsPrivateIp", LuaIsPrivateIp}, //
|
|
|
|
|
{"IsPublicIp", LuaIsPublicIp}, //
|
|
|
|
|
{"IsReasonablePath", LuaIsReasonablePath}, //
|
|
|
|
|
{"IsValidHttpToken", LuaIsValidHttpToken}, //
|
|
|
|
|
{"LaunchBrowser", LuaLaunchBrowser}, //
|
|
|
|
|
{"LoadAsset", LuaLoadAsset}, //
|
|
|
|
|
{"Log", LuaLog}, //
|
|
|
|
|
{"Md5", LuaMd5}, //
|
|
|
|
|
{"ParseHost", LuaParseHost}, //
|
|
|
|
|
{"ParseHttpDateTime", LuaParseHttpDateTime}, //
|
|
|
|
|
{"ParseIp", LuaParseIp}, //
|
|
|
|
|
{"ParseParams", LuaParseParams}, //
|
|
|
|
|
{"ParseUrl", LuaParseUrl}, //
|
|
|
|
|
{"ProgramAddr", LuaProgramAddr}, //
|
|
|
|
|
{"ProgramBrand", LuaProgramBrand}, //
|
|
|
|
|
{"ProgramCache", LuaProgramCache}, //
|
|
|
|
|
{"ProgramCertificate", LuaProgramCertificate}, //
|
2021-07-08 22:56:23 +00:00
|
|
|
|
{"ProgramDirectory", LuaProgramDirectory}, //
|
|
|
|
|
{"ProgramGid", LuaProgramGid}, //
|
2021-07-08 04:44:27 +00:00
|
|
|
|
{"ProgramHeader", LuaProgramHeader}, //
|
2021-07-08 22:56:23 +00:00
|
|
|
|
{"ProgramLogBodies", LuaProgramLogBodies}, //
|
|
|
|
|
{"ProgramLogMessages", LuaProgramLogMessages}, //
|
|
|
|
|
{"ProgramLogPath", LuaProgramLogPath}, //
|
2021-07-08 04:44:27 +00:00
|
|
|
|
{"ProgramPort", LuaProgramPort}, //
|
|
|
|
|
{"ProgramPrivateKey", LuaProgramPrivateKey}, //
|
|
|
|
|
{"ProgramRedirect", LuaProgramRedirect}, //
|
|
|
|
|
{"ProgramSslClientVerify", LuaProgramSslClientVerify}, //
|
|
|
|
|
{"ProgramSslFetchVerify", LuaProgramSslFetchVerify}, //
|
|
|
|
|
{"ProgramTimeout", LuaProgramTimeout}, //
|
2021-07-08 22:56:23 +00:00
|
|
|
|
{"ProgramUid", LuaProgramUid}, //
|
2021-07-08 04:44:27 +00:00
|
|
|
|
{"Route", LuaRoute}, //
|
|
|
|
|
{"RouteHost", LuaRouteHost}, //
|
|
|
|
|
{"RoutePath", LuaRoutePath}, //
|
|
|
|
|
{"ServeAsset", LuaServeAsset}, //
|
|
|
|
|
{"ServeError", LuaServeError}, //
|
|
|
|
|
{"ServeIndex", LuaServeIndex}, //
|
|
|
|
|
{"ServeListing", LuaServeListing}, //
|
|
|
|
|
{"ServeStatusz", LuaServeStatusz}, //
|
|
|
|
|
{"SetHeader", LuaSetHeader}, //
|
|
|
|
|
{"SetLogLevel", LuaSetLogLevel}, //
|
|
|
|
|
{"SetStatus", LuaSetStatus}, //
|
|
|
|
|
{"Sha1", LuaSha1}, //
|
|
|
|
|
{"Sha224", LuaSha224}, //
|
|
|
|
|
{"Sha256", LuaSha256}, //
|
|
|
|
|
{"Sha384", LuaSha384}, //
|
|
|
|
|
{"Sha512", LuaSha512}, //
|
2021-07-08 20:21:16 +00:00
|
|
|
|
{"Slurp", LuaSlurp}, //
|
2021-07-08 04:44:27 +00:00
|
|
|
|
{"StoreAsset", LuaStoreAsset}, //
|
|
|
|
|
{"Underlong", LuaUnderlong}, //
|
|
|
|
|
{"VisualizeControlCodes", LuaVisualizeControlCodes}, //
|
|
|
|
|
{"Write", LuaWrite}, //
|
|
|
|
|
{"bsf", LuaBsf}, //
|
|
|
|
|
{"bsr", LuaBsr}, //
|
|
|
|
|
{"crc32", LuaCrc32}, //
|
|
|
|
|
{"crc32c", LuaCrc32c}, //
|
|
|
|
|
{"popcnt", LuaPopcnt}, //
|
2021-04-23 17:45:19 +00:00
|
|
|
|
};
|
|
|
|
|
|
Tune SQLite build for redbean (#97)
redbean lua handlers that perform sql queries can do 400k qps.
We now use a separate compile-time options for SQLite, when building the
SQLite shell versus building the production web serving code. It doesn't
seem appropriate for something like redbean to include backups, progress
callbacks, query completion, profiling, EXPLAIN, ALTER, ANALYZE, VACUUM,
etc. since those tasks are better left to the sqlite3.com shell program.
Lua SQLite pointer APIs have been removed since we're not using threads.
The Lua APIs for installing update / commit / rollback hooks are removed
due to a general sense of disagreement and an overall lack of comfort.
Full-Text Search and R*Tree are as large as the rest of SQLite combined.
Turning those off keeps redbean under 1mb when built for MODE=tiny which
is nice for marketing purposes.
If you need something that was removed, file an issue, and we'll add it.
2021-06-10 15:00:08 +00:00
|
|
|
|
extern int luaopen_lsqlite3(lua_State *);
|
2021-06-06 00:33:04 +00:00
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static const luaL_Reg kLuaLibs[] = {
|
Tune SQLite build for redbean (#97)
redbean lua handlers that perform sql queries can do 400k qps.
We now use a separate compile-time options for SQLite, when building the
SQLite shell versus building the production web serving code. It doesn't
seem appropriate for something like redbean to include backups, progress
callbacks, query completion, profiling, EXPLAIN, ALTER, ANALYZE, VACUUM,
etc. since those tasks are better left to the sqlite3.com shell program.
Lua SQLite pointer APIs have been removed since we're not using threads.
The Lua APIs for installing update / commit / rollback hooks are removed
due to a general sense of disagreement and an overall lack of comfort.
Full-Text Search and R*Tree are as large as the rest of SQLite combined.
Turning those off keeps redbean under 1mb when built for MODE=tiny which
is nice for marketing purposes.
If you need something that was removed, file an issue, and we'll add it.
2021-06-10 15:00:08 +00:00
|
|
|
|
{"re", LuaRe}, //
|
|
|
|
|
{"lsqlite3", luaopen_lsqlite3}, //
|
2021-03-22 00:27:53 +00:00
|
|
|
|
};
|
|
|
|
|
|
2021-03-29 08:22:49 +00:00
|
|
|
|
static void LuaSetArgv(lua_State *L) {
|
2021-03-27 14:29:55 +00:00
|
|
|
|
size_t i;
|
|
|
|
|
lua_newtable(L);
|
|
|
|
|
for (i = optind; i < __argc; ++i) {
|
|
|
|
|
lua_pushstring(L, __argv[i]);
|
|
|
|
|
lua_seti(L, -2, i - optind + 1);
|
|
|
|
|
}
|
|
|
|
|
lua_setglobal(L, "argv");
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-29 08:22:49 +00:00
|
|
|
|
static void LuaSetConstant(lua_State *L, const char *s, long x) {
|
|
|
|
|
lua_pushinteger(L, x);
|
|
|
|
|
lua_setglobal(L, s);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-22 00:27:53 +00:00
|
|
|
|
static void LuaInit(void) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
#ifndef STATIC
|
2021-03-22 00:27:53 +00:00
|
|
|
|
size_t i;
|
|
|
|
|
L = luaL_newstate();
|
2021-03-27 05:31:41 +00:00
|
|
|
|
luaL_openlibs(L);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
for (i = 0; i < ARRAYLEN(kLuaLibs); ++i) {
|
|
|
|
|
luaL_requiref(L, kLuaLibs[i].name, kLuaLibs[i].func, 1);
|
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
}
|
2021-03-22 00:27:53 +00:00
|
|
|
|
for (i = 0; i < ARRAYLEN(kLuaFuncs); ++i) {
|
|
|
|
|
lua_pushcfunction(L, kLuaFuncs[i].func);
|
|
|
|
|
lua_setglobal(L, kLuaFuncs[i].name);
|
|
|
|
|
}
|
2021-03-29 08:22:49 +00:00
|
|
|
|
LuaSetArgv(L);
|
|
|
|
|
LuaSetConstant(L, "kLogDebug", kLogDebug);
|
|
|
|
|
LuaSetConstant(L, "kLogVerbose", kLogVerbose);
|
|
|
|
|
LuaSetConstant(L, "kLogInfo", kLogInfo);
|
|
|
|
|
LuaSetConstant(L, "kLogWarn", kLogWarn);
|
|
|
|
|
LuaSetConstant(L, "kLogError", kLogError);
|
|
|
|
|
LuaSetConstant(L, "kLogFatal", kLogFatal);
|
2021-04-25 00:09:01 +00:00
|
|
|
|
if (LuaRun("/.init.lua")) {
|
|
|
|
|
hasluaglobalhandler = !!lua_getglobal(L, "OnHttpRequest");
|
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
} else {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
DEBUGF("no /.init.lua defined");
|
|
|
|
|
}
|
2021-04-21 02:14:21 +00:00
|
|
|
|
#endif
|
2021-03-27 05:31:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void LuaReload(void) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
#ifndef STATIC
|
2021-04-23 17:45:19 +00:00
|
|
|
|
if (!LuaRun("/.reload.lua")) {
|
|
|
|
|
DEBUGF("no /.reload.lua defined");
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-03 19:09:35 +00:00
|
|
|
|
static const char *DescribeClose(void) {
|
|
|
|
|
if (killed) return "killed";
|
|
|
|
|
if (meltdown) return "meltdown";
|
|
|
|
|
if (terminated) return "terminated";
|
|
|
|
|
if (connectionclose) return "connectionclose";
|
|
|
|
|
return "destroyed";
|
2021-04-23 17:45:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-03 19:09:35 +00:00
|
|
|
|
static void LogClose(const char *reason) {
|
|
|
|
|
if (amtread || meltdown || killed) {
|
|
|
|
|
LockInc(&shared->c.fumbles);
|
|
|
|
|
LOGF("%s %s with %,ld unprocessed and %,d handled (%,d workers)",
|
|
|
|
|
DescribeClient(), reason, amtread, messageshandled, shared->workers);
|
|
|
|
|
} else {
|
|
|
|
|
DEBUGF("%s %s with %,d requests handled", DescribeClient(), reason,
|
|
|
|
|
messageshandled);
|
2021-05-02 18:50:43 +00:00
|
|
|
|
}
|
2021-03-22 00:27:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-25 00:09:01 +00:00
|
|
|
|
static ssize_t SendString(const char *s) {
|
|
|
|
|
size_t n;
|
|
|
|
|
ssize_t rc;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
struct iovec iov;
|
2021-04-25 00:09:01 +00:00
|
|
|
|
n = strlen(s);
|
2021-06-24 19:31:26 +00:00
|
|
|
|
iov.iov_base = s;
|
|
|
|
|
iov.iov_len = n;
|
2021-07-08 22:56:23 +00:00
|
|
|
|
if (logmessages) {
|
|
|
|
|
LogMessage("sending", s, n);
|
|
|
|
|
}
|
2021-04-25 00:09:01 +00:00
|
|
|
|
for (;;) {
|
2021-06-24 19:31:26 +00:00
|
|
|
|
if ((rc = writer(client, &iov, 1)) != -1 || errno != EINTR) {
|
2021-04-25 00:09:01 +00:00
|
|
|
|
return rc;
|
2021-03-29 08:22:49 +00:00
|
|
|
|
}
|
2021-04-18 18:34:59 +00:00
|
|
|
|
}
|
2021-04-25 00:09:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ssize_t SendContinue(void) {
|
|
|
|
|
return SendString("\
|
|
|
|
|
HTTP/1.1 100 Continue\r\n\
|
|
|
|
|
\r\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ssize_t SendTimeout(void) {
|
|
|
|
|
return SendString("\
|
|
|
|
|
HTTP/1.1 408 Request Timeout\r\n\
|
|
|
|
|
Connection: close\r\n\
|
|
|
|
|
Content-Length: 0\r\n\
|
|
|
|
|
\r\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ssize_t SendServiceUnavailable(void) {
|
|
|
|
|
return SendString("\
|
|
|
|
|
HTTP/1.1 503 Service Unavailable\r\n\
|
|
|
|
|
Connection: close\r\n\
|
|
|
|
|
Content-Length: 0\r\n\
|
|
|
|
|
\r\n");
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-03 19:09:35 +00:00
|
|
|
|
static void EnterMeltdownMode(void) {
|
|
|
|
|
if (shared->lastmeltdown && nowl() - shared->lastmeltdown < 1) return;
|
|
|
|
|
WARNF("redbean is melting down (%,d workers)", shared->workers);
|
|
|
|
|
LOGIFNEG1(kill(0, SIGUSR2));
|
|
|
|
|
shared->lastmeltdown = nowl();
|
|
|
|
|
++shared->c.meltdowns;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *HandlePayloadDisconnect(void) {
|
|
|
|
|
LockInc(&shared->c.payloaddisconnects);
|
|
|
|
|
LogClose("payload disconnect");
|
|
|
|
|
return ServeFailure(400, "Bad Request"); /* XXX */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *HandlePayloadDrop(void) {
|
|
|
|
|
LockInc(&shared->c.dropped);
|
|
|
|
|
LogClose(DescribeClose());
|
|
|
|
|
return ServeFailure(503, "Service Unavailable");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *HandleBadContentLength(void) {
|
|
|
|
|
LockInc(&shared->c.badlengths);
|
|
|
|
|
return ServeFailure(400, "Bad Content Length");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *HandleLengthRequired(void) {
|
|
|
|
|
LockInc(&shared->c.missinglengths);
|
|
|
|
|
return ServeFailure(411, "Length Required");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *HandleVersionNotSupported(void) {
|
|
|
|
|
LockInc(&shared->c.http12);
|
|
|
|
|
return ServeFailure(505, "HTTP Version Not Supported");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *HandleConnectRefused(void) {
|
|
|
|
|
LockInc(&shared->c.connectsrefused);
|
|
|
|
|
return ServeFailure(501, "Not Implemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *HandleExpectFailed(void) {
|
|
|
|
|
LockInc(&shared->c.expectsrefused);
|
|
|
|
|
return ServeFailure(417, "Expectation Failed");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *HandleHugePayload(void) {
|
|
|
|
|
LockInc(&shared->c.hugepayloads);
|
|
|
|
|
return ServeFailure(413, "Payload Too Large");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *HandlePayloadSlowloris(void) {
|
|
|
|
|
LockInc(&shared->c.slowloris);
|
|
|
|
|
LogClose("payload slowloris");
|
|
|
|
|
return ServeFailure(408, "Request Timeout");
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-05 08:03:45 +00:00
|
|
|
|
static char *HandleTransferRefused(void) {
|
|
|
|
|
LockInc(&shared->c.transfersrefused);
|
|
|
|
|
return ServeFailure(501, "Not Implemented");
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-03 19:09:35 +00:00
|
|
|
|
static char *HandleMapFailed(struct Asset *a, int fd) {
|
|
|
|
|
LockInc(&shared->c.mapfails);
|
|
|
|
|
WARNF("mmap(%`'s) failed %s", a->file->path, strerror(errno));
|
|
|
|
|
close(fd);
|
|
|
|
|
return ServeError(500, "Internal Server Error");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *HandleOpenFail(struct Asset *a) {
|
|
|
|
|
LockInc(&shared->c.openfails);
|
|
|
|
|
WARNF("open(%`'s) failed %s", a->file->path, strerror(errno));
|
|
|
|
|
if (errno == ENFILE) {
|
|
|
|
|
LockInc(&shared->c.enfiles);
|
|
|
|
|
return ServeError(503, "Service Unavailable");
|
|
|
|
|
} else if (errno == EMFILE) {
|
|
|
|
|
LockInc(&shared->c.emfiles);
|
|
|
|
|
return ServeError(503, "Service Unavailable");
|
2021-04-25 00:09:01 +00:00
|
|
|
|
} else {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
return ServeError(500, "Internal Server Error");
|
2021-04-18 18:34:59 +00:00
|
|
|
|
}
|
2021-04-25 00:09:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-03 19:09:35 +00:00
|
|
|
|
static char *HandlePayloadReadError(void) {
|
|
|
|
|
if (errno == ECONNRESET) {
|
|
|
|
|
LockInc(&shared->c.readresets);
|
|
|
|
|
LogClose("payload reset");
|
|
|
|
|
return ServeFailure(400, "Bad Request"); /* XXX */
|
|
|
|
|
} else if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
|
|
|
LockInc(&shared->c.readtimeouts);
|
|
|
|
|
LogClose("payload read timeout");
|
|
|
|
|
return ServeFailure(408, "Request Timeout");
|
|
|
|
|
} else {
|
|
|
|
|
LockInc(&shared->c.readerrors);
|
|
|
|
|
LOGF("%s payload read error %s", DescribeClient(), strerror(errno));
|
|
|
|
|
return ServeFailure(500, "Internal Server Error");
|
|
|
|
|
}
|
2021-04-25 00:09:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-03 19:09:35 +00:00
|
|
|
|
static void HandleForkFailure(void) {
|
2021-06-24 19:31:26 +00:00
|
|
|
|
FATALF("too many processes %s", strerror(errno));
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.forkerrors);
|
|
|
|
|
LockInc(&shared->c.dropped);
|
|
|
|
|
EnterMeltdownMode();
|
|
|
|
|
SendServiceUnavailable();
|
2021-06-24 19:31:26 +00:00
|
|
|
|
close(client);
|
2021-05-03 19:09:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void HandleFrag(size_t got) {
|
|
|
|
|
LockInc(&shared->c.frags);
|
|
|
|
|
if (++frags == 32) {
|
|
|
|
|
SendTimeout();
|
|
|
|
|
LogClose("slowloris");
|
|
|
|
|
LockInc(&shared->c.slowloris);
|
|
|
|
|
return;
|
|
|
|
|
} else {
|
|
|
|
|
DEBUGF("%s fragged msg added %,ld bytes to %,ld byte buffer",
|
|
|
|
|
DescribeClient(), amtread, got);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void HandleReload(void) {
|
|
|
|
|
LockInc(&shared->c.reloads);
|
|
|
|
|
Reindex();
|
|
|
|
|
LuaReload();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void HandleHeartbeat(void) {
|
2021-06-24 19:31:26 +00:00
|
|
|
|
size_t i;
|
|
|
|
|
sigset_t mask;
|
2021-05-03 19:09:35 +00:00
|
|
|
|
if (nowl() - lastrefresh > 60 * 60) RefreshTime();
|
|
|
|
|
UpdateCurrentDate(nowl());
|
2021-05-14 12:36:58 +00:00
|
|
|
|
Reindex();
|
2021-05-03 19:09:35 +00:00
|
|
|
|
getrusage(RUSAGE_SELF, &shared->server);
|
|
|
|
|
#ifndef STATIC
|
|
|
|
|
LuaRun("/.heartbeat.lua");
|
|
|
|
|
#endif
|
2021-06-24 19:31:26 +00:00
|
|
|
|
for (i = 0; i < servers.n; ++i) {
|
|
|
|
|
if (polls[i].fd < 0) {
|
|
|
|
|
polls[i].fd = -polls[i].fd;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-29 08:22:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-03 19:09:35 +00:00
|
|
|
|
static char *OpenAsset(struct Asset *a) {
|
|
|
|
|
int fd;
|
|
|
|
|
void *data;
|
|
|
|
|
size_t size;
|
|
|
|
|
if (a->file->st.st_size) {
|
|
|
|
|
size = a->file->st.st_size;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
if (msg.method == kHttpHead) {
|
|
|
|
|
content = 0;
|
|
|
|
|
contentlength = size;
|
|
|
|
|
} else {
|
|
|
|
|
OpenAgain:
|
|
|
|
|
if ((fd = open(a->file->path.s, O_RDONLY)) != -1) {
|
|
|
|
|
data = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
|
|
|
|
if (data != MAP_FAILED) {
|
|
|
|
|
LockInc(&shared->c.maps);
|
|
|
|
|
UnmapLater(fd, data, size);
|
|
|
|
|
content = data;
|
|
|
|
|
contentlength = size;
|
|
|
|
|
} else {
|
|
|
|
|
return HandleMapFailed(a, fd);
|
|
|
|
|
}
|
|
|
|
|
} else if (errno == EINTR) {
|
|
|
|
|
goto OpenAgain;
|
2021-05-03 19:09:35 +00:00
|
|
|
|
} else {
|
2021-05-14 12:36:58 +00:00
|
|
|
|
return HandleOpenFail(a);
|
2021-05-03 19:09:35 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
content = "";
|
|
|
|
|
contentlength = 0;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *ServeServerOptions(void) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
char *p;
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.serveroptions);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
p = SetStatus(200, "OK");
|
|
|
|
|
#ifdef STATIC
|
|
|
|
|
p = stpcpy(p, "Allow: GET, HEAD, OPTIONS\r\n");
|
|
|
|
|
#else
|
|
|
|
|
p = stpcpy(p, "Accept: */*\r\n"
|
2021-05-01 12:11:35 +00:00
|
|
|
|
"Accept-Charset: utf-8,ISO-8859-1;q=0.7,*;q=0.5\r\n"
|
2021-04-23 17:45:19 +00:00
|
|
|
|
"Allow: GET, HEAD, POST, PUT, DELETE, OPTIONS\r\n");
|
|
|
|
|
#endif
|
|
|
|
|
return p;
|
|
|
|
|
}
|
2021-04-21 02:14:21 +00:00
|
|
|
|
|
2021-07-05 08:03:45 +00:00
|
|
|
|
static void SendContinueIfNeeded(void) {
|
|
|
|
|
if (msg.version >= 11 && HeaderEqualCase(kHttpExpect, "100-continue")) {
|
|
|
|
|
LockInc(&shared->c.continues);
|
|
|
|
|
SendContinue();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *ReadMore(void) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
size_t got;
|
|
|
|
|
ssize_t rc;
|
2021-07-05 08:03:45 +00:00
|
|
|
|
LockInc(&shared->c.frags);
|
|
|
|
|
if (++frags == 64) return HandlePayloadSlowloris();
|
|
|
|
|
if ((rc = reader(client, inbuf.p + amtread, inbuf.n - amtread)) != -1) {
|
|
|
|
|
if (!(got = rc)) return HandlePayloadDisconnect();
|
|
|
|
|
amtread += got;
|
|
|
|
|
} else if (errno == EINTR) {
|
|
|
|
|
LockInc(&shared->c.readinterrupts);
|
|
|
|
|
if (killed || ((meltdown || terminated) && nowl() - startread > 1)) {
|
|
|
|
|
return HandlePayloadDrop();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return HandlePayloadReadError();
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *SynchronizeLength(void) {
|
|
|
|
|
char *p;
|
|
|
|
|
if (hdrsize + payloadlength > amtread) {
|
|
|
|
|
if (hdrsize + payloadlength > inbuf.n) return HandleHugePayload();
|
|
|
|
|
SendContinueIfNeeded();
|
|
|
|
|
while (amtread < hdrsize + payloadlength) {
|
|
|
|
|
if ((p = ReadMore())) return p;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
msgsize = hdrsize + payloadlength;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *SynchronizeChunked(void) {
|
|
|
|
|
char *p;
|
|
|
|
|
ssize_t transferlength;
|
|
|
|
|
struct HttpUnchunker u = {0};
|
|
|
|
|
SendContinueIfNeeded();
|
|
|
|
|
while (!(transferlength = Unchunk(&u, inbuf.p + hdrsize, amtread - hdrsize,
|
|
|
|
|
&payloadlength))) {
|
|
|
|
|
if ((p = ReadMore())) return p;
|
|
|
|
|
}
|
|
|
|
|
if (transferlength == -1) return HandleHugePayload();
|
|
|
|
|
msgsize = hdrsize + transferlength;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *SynchronizeStream(void) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
int64_t cl;
|
2021-07-05 08:03:45 +00:00
|
|
|
|
if (HasHeader(kHttpTransferEncoding) &&
|
2021-07-08 04:44:27 +00:00
|
|
|
|
!HeaderEqualCase(kHttpTransferEncoding, "identity")) {
|
|
|
|
|
if (HeaderEqualCase(kHttpTransferEncoding, "chunked")) {
|
2021-07-05 08:03:45 +00:00
|
|
|
|
return SynchronizeChunked();
|
|
|
|
|
} else {
|
|
|
|
|
return HandleTransferRefused();
|
|
|
|
|
}
|
|
|
|
|
} else if (HasHeader(kHttpContentLength)) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
if ((cl = ParseContentLength(HeaderData(kHttpContentLength),
|
2021-07-05 08:03:45 +00:00
|
|
|
|
HeaderLength(kHttpContentLength))) != -1) {
|
|
|
|
|
payloadlength = cl;
|
|
|
|
|
return SynchronizeLength();
|
|
|
|
|
} else {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
return HandleBadContentLength();
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
2021-05-03 19:09:35 +00:00
|
|
|
|
} else if (msg.method == kHttpPost || msg.method == kHttpPut) {
|
|
|
|
|
return HandleLengthRequired();
|
|
|
|
|
} else {
|
2021-07-05 08:03:45 +00:00
|
|
|
|
msgsize = hdrsize;
|
|
|
|
|
payloadlength = 0;
|
|
|
|
|
return NULL;
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ParseRequestParameters(void) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
uint32_t ip;
|
2021-04-21 02:14:21 +00:00
|
|
|
|
FreeLater(ParseRequestUri(inbuf.p + msg.uri.a, msg.uri.b - msg.uri.a, &url));
|
|
|
|
|
if (!url.host.p) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
GetRemoteAddr(&ip, 0);
|
|
|
|
|
if (HasHeader(kHttpXForwardedHost) &&
|
|
|
|
|
(IsPrivateIp(ip) || IsLoopbackIp(ip))) {
|
|
|
|
|
FreeLater(ParseHost(HeaderData(kHttpXForwardedHost),
|
|
|
|
|
HeaderLength(kHttpXForwardedHost), &url));
|
|
|
|
|
} else if (HasHeader(kHttpHost)) {
|
|
|
|
|
FreeLater(
|
|
|
|
|
ParseHost(HeaderData(kHttpHost), HeaderLength(kHttpHost), &url));
|
2021-06-24 19:31:26 +00:00
|
|
|
|
} else {
|
|
|
|
|
FreeLater(ParseHost(DescribeServer(), -1, &url));
|
2021-04-23 17:45:19 +00:00
|
|
|
|
}
|
2021-04-21 02:14:21 +00:00
|
|
|
|
} else if (!url.path.n) {
|
|
|
|
|
url.path.p = "/";
|
|
|
|
|
url.path.n = 1;
|
|
|
|
|
}
|
2021-06-24 19:31:26 +00:00
|
|
|
|
if (!url.scheme.n) {
|
|
|
|
|
if (usessl) {
|
|
|
|
|
url.scheme.p = "https";
|
|
|
|
|
url.scheme.n = 5;
|
|
|
|
|
} else {
|
|
|
|
|
url.scheme.p = "http";
|
|
|
|
|
url.scheme.n = 4;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-03 19:09:35 +00:00
|
|
|
|
static bool HasAtMostThisElement(int h, const char *s) {
|
|
|
|
|
size_t i, n;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
struct HttpHeader *x;
|
2021-05-03 19:09:35 +00:00
|
|
|
|
if (HasHeader(h)) {
|
|
|
|
|
n = strlen(s);
|
2021-07-05 08:03:45 +00:00
|
|
|
|
if (!SlicesEqualCase(s, n, HeaderData(h), HeaderLength(h))) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
for (i = 0; i < msg.xheaders.n; ++i) {
|
|
|
|
|
x = msg.xheaders.p + i;
|
|
|
|
|
if (GetHttpHeader(inbuf.p + x->k.a, x->k.b - x->k.a) == h &&
|
|
|
|
|
!SlicesEqualCase(inbuf.p + x->v.a, x->v.b - x->v.a, s, n)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
2021-05-03 19:09:35 +00:00
|
|
|
|
return true;
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-06-24 19:31:26 +00:00
|
|
|
|
static char *SendHttpsRedirect(void) {
|
|
|
|
|
size_t n;
|
|
|
|
|
char *p, *old, *neu;
|
|
|
|
|
LockInc(&shared->c.sslupgrades);
|
|
|
|
|
if ((old = FreeLater(EncodeUrl(&url, &n))) && n < hdrbuf.n / 2) {
|
|
|
|
|
url.scheme.p = "https";
|
|
|
|
|
url.scheme.n = 5;
|
|
|
|
|
if (listeningonport443) {
|
|
|
|
|
url.port.p = 0;
|
|
|
|
|
url.port.n = 0;
|
|
|
|
|
} else if (!url.port.n) {
|
|
|
|
|
url.port.p = "80";
|
|
|
|
|
url.port.n = 2;
|
|
|
|
|
}
|
|
|
|
|
neu = FreeLater(EncodeUrl(&url, 0));
|
2021-06-24 22:22:24 +00:00
|
|
|
|
LOGF("REDIRECT %s from %s → %s", DescribeClient(), old, neu);
|
2021-06-24 19:31:26 +00:00
|
|
|
|
p = SetStatus(307, "Temporary Redirect");
|
|
|
|
|
p = AppendHeader(p, "Location", neu);
|
|
|
|
|
return p;
|
|
|
|
|
} else {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static char *HandleRequest(void) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
char *p;
|
2021-05-03 19:09:35 +00:00
|
|
|
|
if (msg.version == 11) {
|
|
|
|
|
LockInc(&shared->c.http11);
|
|
|
|
|
} else if (msg.version < 10) {
|
|
|
|
|
LockInc(&shared->c.http09);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
} else if (msg.version == 10) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.http10);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
} else {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
return HandleVersionNotSupported();
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
if ((p = SynchronizeStream())) return p;
|
2021-07-05 08:03:45 +00:00
|
|
|
|
if (logbodies) LogBody("received", inbuf.p + hdrsize, payloadlength);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
if (msg.version < 11 || HeaderEqualCase(kHttpConnection, "close")) {
|
2021-03-25 09:21:13 +00:00
|
|
|
|
connectionclose = true;
|
|
|
|
|
}
|
2021-04-23 17:45:19 +00:00
|
|
|
|
if (msg.method == kHttpOptions &&
|
|
|
|
|
SlicesEqual(inbuf.p + msg.uri.a, msg.uri.b - msg.uri.a, "*", 1)) {
|
|
|
|
|
return ServeServerOptions();
|
|
|
|
|
}
|
2021-04-21 02:14:21 +00:00
|
|
|
|
if (msg.method == kHttpConnect) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
return HandleConnectRefused();
|
2021-04-21 02:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
if (!HasAtMostThisElement(kHttpExpect, "100-continue")) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
return HandleExpectFailed();
|
2021-04-18 18:34:59 +00:00
|
|
|
|
}
|
2021-04-21 02:14:21 +00:00
|
|
|
|
ParseRequestParameters();
|
2021-06-24 19:31:26 +00:00
|
|
|
|
if (!url.host.n || !url.path.n || url.path.p[0] != '/' ||
|
2021-04-21 02:14:21 +00:00
|
|
|
|
!IsAcceptablePath(url.path.p, url.path.n) ||
|
|
|
|
|
!IsAcceptableHost(url.host.p, url.host.n) ||
|
|
|
|
|
!IsAcceptablePort(url.port.p, url.port.n)) {
|
2021-07-05 21:03:50 +00:00
|
|
|
|
free(url.params.p);
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.urisrefused);
|
2021-05-17 01:22:39 +00:00
|
|
|
|
return ServeFailure(400, "Bad URI");
|
2021-04-23 17:45:19 +00:00
|
|
|
|
}
|
2021-06-24 19:31:26 +00:00
|
|
|
|
if (HasHeader(kHttpUpgradeInsecureRequests) && !usessl &&
|
|
|
|
|
upgradeinsecurerequests && (p = SendHttpsRedirect())) {
|
2021-07-05 21:03:50 +00:00
|
|
|
|
free(url.params.p);
|
2021-06-24 19:31:26 +00:00
|
|
|
|
return p;
|
|
|
|
|
}
|
2021-04-23 17:45:19 +00:00
|
|
|
|
LOGF("RECEIVED %s HTTP%02d %.*s %s %`'.*s %`'.*s", DescribeClient(),
|
|
|
|
|
msg.version, msg.xmethod.b - msg.xmethod.a, inbuf.p + msg.xmethod.a,
|
|
|
|
|
FreeLater(EncodeUrl(&url, 0)), HeaderLength(kHttpReferer),
|
|
|
|
|
HeaderData(kHttpReferer), HeaderLength(kHttpUserAgent),
|
|
|
|
|
HeaderData(kHttpUserAgent));
|
2021-07-05 08:03:45 +00:00
|
|
|
|
if (HasHeader(kHttpContentType) &&
|
|
|
|
|
IsMimeType(HeaderData(kHttpContentType), HeaderLength(kHttpContentType),
|
|
|
|
|
"application/x-www-form-urlencoded")) {
|
|
|
|
|
FreeLater(ParseParams(inbuf.p + hdrsize, payloadlength, &url.params));
|
|
|
|
|
}
|
|
|
|
|
FreeLater(url.params.p);
|
2021-06-24 19:31:26 +00:00
|
|
|
|
#ifndef STATIC
|
2021-05-03 19:09:35 +00:00
|
|
|
|
if (hasluaglobalhandler) return LuaOnHttpRequest();
|
2021-06-24 19:31:26 +00:00
|
|
|
|
#endif
|
2021-04-25 00:09:01 +00:00
|
|
|
|
return Route(url.host.p, url.host.n, url.path.p, url.path.n);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-03 19:09:35 +00:00
|
|
|
|
static char *Route(const char *host, size_t hostlen, const char *path,
|
|
|
|
|
size_t pathlen) {
|
|
|
|
|
char *p;
|
2021-07-08 22:56:23 +00:00
|
|
|
|
if (logmessages) LogMessage("received", inbuf.p, hdrsize);
|
2021-05-03 19:09:35 +00:00
|
|
|
|
if (hostlen && (p = RouteHost(host, hostlen, path, pathlen))) {
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
if (SlicesEqual(path, pathlen, "/", 1)) {
|
|
|
|
|
if ((p = ServeIndex("/", 1))) return p;
|
|
|
|
|
return ServeListing();
|
|
|
|
|
} else if ((p = RoutePath(path, pathlen))) {
|
|
|
|
|
return p;
|
|
|
|
|
} else if (SlicesEqual(path, pathlen, "/statusz", 8)) {
|
|
|
|
|
return ServeStatusz();
|
|
|
|
|
} else {
|
|
|
|
|
LockInc(&shared->c.notfounds);
|
|
|
|
|
return ServeError(404, "Not Found");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *RoutePath(const char *path, size_t pathlen) {
|
|
|
|
|
int m;
|
|
|
|
|
long r;
|
|
|
|
|
char *p;
|
|
|
|
|
struct Asset *a;
|
|
|
|
|
DEBUGF("RoutePath(%`'.*s)", pathlen, path);
|
|
|
|
|
if ((a = GetAsset(path, pathlen))) {
|
|
|
|
|
if ((m = GetMode(a)) & 0004) {
|
|
|
|
|
if (!S_ISDIR(m)) {
|
|
|
|
|
return HandleAsset(a, path, pathlen);
|
|
|
|
|
} else {
|
|
|
|
|
return HandleFolder(path, pathlen);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
LockInc(&shared->c.forbiddens);
|
|
|
|
|
LOGF("asset %`'.*s %#o isn't readable", pathlen, path, m);
|
|
|
|
|
return ServeError(403, "Forbidden");
|
|
|
|
|
}
|
|
|
|
|
} else if ((r = FindRedirect(path, pathlen)) != -1) {
|
|
|
|
|
return HandleRedirect(redirects.p + r);
|
|
|
|
|
} else {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *RouteHost(const char *host, size_t hostlen, const char *path,
|
|
|
|
|
size_t pathlen) {
|
|
|
|
|
size_t hn, hm;
|
|
|
|
|
char *hp, *p, b[96];
|
|
|
|
|
if (hostlen) {
|
|
|
|
|
hn = 1 + hostlen + url.path.n;
|
|
|
|
|
hm = 3 + 1 + hn;
|
|
|
|
|
hp = hm <= sizeof(b) ? b : FreeLater(xmalloc(hm));
|
|
|
|
|
hp[0] = '/';
|
|
|
|
|
mempcpy(mempcpy(hp + 1, host, hostlen), path, pathlen);
|
|
|
|
|
if ((p = RoutePath(hp, hn))) return p;
|
|
|
|
|
if (!isdigit(host[0])) {
|
|
|
|
|
if (hostlen > 4 &&
|
|
|
|
|
READ32LE(host) == ('w' | 'w' << 8 | 'w' << 16 | '.' << 24)) {
|
|
|
|
|
mempcpy(mempcpy(hp + 1, host + 4, hostlen - 4), path, pathlen);
|
|
|
|
|
if ((p = RoutePath(hp, hn - 4))) return p;
|
|
|
|
|
} else {
|
|
|
|
|
mempcpy(mempcpy(mempcpy(hp + 1, "www.", 4), host, hostlen), path,
|
|
|
|
|
pathlen);
|
|
|
|
|
if ((p = RoutePath(hp, hn + 4))) return p;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline bool IsLua(struct Asset *a) {
|
|
|
|
|
size_t n;
|
|
|
|
|
const char *p;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
if (a->file && a->file->path.n >= 4 &&
|
|
|
|
|
READ32LE(a->file->path.s + a->file->path.n - 4) ==
|
|
|
|
|
('.' | 'l' << 8 | 'u' << 16 | 'a' << 24)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
p = ZIP_CFILE_NAME(zbase + a->cf);
|
|
|
|
|
n = ZIP_CFILE_NAMESIZE(zbase + a->cf);
|
2021-05-03 19:09:35 +00:00
|
|
|
|
return n > 4 &&
|
|
|
|
|
READ32LE(p + n - 4) == ('.' | 'l' << 8 | 'u' << 16 | 'a' << 24);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *HandleAsset(struct Asset *a, const char *path, size_t pathlen) {
|
|
|
|
|
#ifndef STATIC
|
|
|
|
|
if (IsLua(a)) return ServeLua(a, path, pathlen);
|
|
|
|
|
#endif
|
|
|
|
|
if (msg.method == kHttpGet || msg.method == kHttpHead) {
|
|
|
|
|
LockInc(&shared->c.staticrequests);
|
|
|
|
|
return stpcpy(ServeAsset(a, path, pathlen),
|
|
|
|
|
"X-Content-Type-Options: nosniff\r\n");
|
|
|
|
|
} else {
|
|
|
|
|
return BadMethod();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *GetContentType(struct Asset *a, const char *path, size_t n) {
|
|
|
|
|
const char *r;
|
2021-05-14 12:36:58 +00:00
|
|
|
|
if (a->file && (r = FindContentType(a->file->path.s, a->file->path.n))) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
return firstnonnull(
|
|
|
|
|
FindContentType(path, n),
|
2021-05-14 12:36:58 +00:00
|
|
|
|
firstnonnull(FindContentType(ZIP_CFILE_NAME(zbase + a->cf),
|
|
|
|
|
ZIP_CFILE_NAMESIZE(zbase + a->cf)),
|
2021-05-03 19:09:35 +00:00
|
|
|
|
a->istext ? "text/plain" : "application/octet-stream"));
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-05 14:25:39 +00:00
|
|
|
|
static bool IsNotModified(struct Asset *a) {
|
|
|
|
|
if (msg.version < 10) return false;
|
|
|
|
|
if (!HasHeader(kHttpIfModifiedSince)) return false;
|
|
|
|
|
return a->lastmodified >=
|
|
|
|
|
ParseHttpDateTime(HeaderData(kHttpIfModifiedSince),
|
|
|
|
|
HeaderLength(kHttpIfModifiedSince));
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-03 19:09:35 +00:00
|
|
|
|
static char *ServeAsset(struct Asset *a, const char *path, size_t pathlen) {
|
|
|
|
|
char *p;
|
|
|
|
|
uint32_t crc;
|
|
|
|
|
const char *ct;
|
|
|
|
|
ct = GetContentType(a, path, pathlen);
|
|
|
|
|
if (IsNotModified(a)) {
|
|
|
|
|
LockInc(&shared->c.notmodifieds);
|
|
|
|
|
p = SetStatus(304, "Not Modified");
|
|
|
|
|
} else {
|
|
|
|
|
if (!a->file) {
|
2021-05-14 12:36:58 +00:00
|
|
|
|
content = (char *)ZIP_LFILE_CONTENT(zbase + a->lf);
|
|
|
|
|
contentlength = GetZipCfileCompressedSize(zbase + a->cf);
|
2021-05-03 19:09:35 +00:00
|
|
|
|
} else if ((p = OpenAsset(a))) {
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
if (IsCompressed(a)) {
|
|
|
|
|
if (ClientAcceptsGzip()) {
|
|
|
|
|
p = ServeAssetPrecompressed(a);
|
|
|
|
|
} else {
|
|
|
|
|
p = ServeAssetDecompressed(a);
|
|
|
|
|
}
|
|
|
|
|
} else if (msg.version >= 11 && HasHeader(kHttpRange)) {
|
|
|
|
|
p = ServeAssetRange(a);
|
|
|
|
|
} else if (!a->file) {
|
|
|
|
|
LockInc(&shared->c.identityresponses);
|
|
|
|
|
DEBUGF("ServeAssetZipIdentity(%`'s)", ct);
|
2021-05-14 12:36:58 +00:00
|
|
|
|
if (Verify(content, contentlength, ZIP_LFILE_CRC32(zbase + a->lf))) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
p = SetStatus(200, "OK");
|
|
|
|
|
} else {
|
|
|
|
|
return ServeError(500, "Internal Server Error");
|
|
|
|
|
}
|
2021-07-04 19:21:29 +00:00
|
|
|
|
} else if (!IsTiny() && ClientAcceptsGzip() &&
|
2021-05-03 19:09:35 +00:00
|
|
|
|
(strlen(ct) >= 5 && !memcasecmp(ct, "text/", 5)) &&
|
|
|
|
|
100 < contentlength && contentlength < 1024 * 1024 * 1024) {
|
|
|
|
|
p = ServeAssetCompressed(a);
|
|
|
|
|
} else {
|
|
|
|
|
p = ServeAssetIdentity(a, ct);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
p = AppendContentType(p, ct);
|
|
|
|
|
p = stpcpy(p, "Vary: Accept-Encoding\r\n");
|
|
|
|
|
p = AppendHeader(p, "Last-Modified", a->lastmodifiedstr);
|
|
|
|
|
if (msg.version >= 11) {
|
|
|
|
|
p = AppendCache(p, cacheseconds);
|
|
|
|
|
if (!IsCompressed(a)) {
|
|
|
|
|
p = stpcpy(p, "Accept-Ranges: bytes\r\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *SetStatus(unsigned code, const char *reason) {
|
2021-06-24 19:31:26 +00:00
|
|
|
|
if (msg.version == 10) {
|
|
|
|
|
if (code == 307) code = 302;
|
|
|
|
|
if (code == 308) code = 301;
|
|
|
|
|
}
|
2021-05-03 19:09:35 +00:00
|
|
|
|
statuscode = code;
|
|
|
|
|
stpcpy(hdrbuf.p, "HTTP/1.0 000 ");
|
|
|
|
|
hdrbuf.p[7] += msg.version & 1;
|
|
|
|
|
hdrbuf.p[9] += code / 100;
|
|
|
|
|
hdrbuf.p[10] += code / 10 % 10;
|
|
|
|
|
hdrbuf.p[11] += code % 10;
|
|
|
|
|
return AppendCrlf(stpcpy(hdrbuf.p + 13, reason));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline bool MustNotIncludeMessageBody(void) { /* RFC2616 § 4.4 */
|
|
|
|
|
return msg.method == kHttpHead || (100 <= statuscode && statuscode <= 199) ||
|
|
|
|
|
statuscode == 204 || statuscode == 304;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static bool HandleMessage(void) {
|
2021-03-25 09:21:13 +00:00
|
|
|
|
int rc;
|
|
|
|
|
int iovlen;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
char *p, *s;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
struct iovec iov[4];
|
|
|
|
|
long actualcontentlength;
|
2021-04-24 20:58:34 +00:00
|
|
|
|
g_syscount = 0;
|
2021-06-28 00:03:06 +00:00
|
|
|
|
if ((rc = ParseHttpMessage(&msg, inbuf.p, amtread)) != -1) {
|
2021-03-25 09:21:13 +00:00
|
|
|
|
if (!rc) return false;
|
2021-03-27 14:29:55 +00:00
|
|
|
|
hdrsize = rc;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
p = HandleRequest();
|
2021-03-25 09:21:13 +00:00
|
|
|
|
} else {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.badmessages);
|
2021-04-02 01:51:12 +00:00
|
|
|
|
connectionclose = true;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
LOGF("%s sent garbage %`'s", DescribeClient(),
|
|
|
|
|
VisualizeControlCodes(inbuf.p, MIN(128, amtread), 0));
|
|
|
|
|
p = ServeError(400, "Bad Message");
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
if (!msgsize) {
|
|
|
|
|
amtread = 0;
|
|
|
|
|
connectionclose = true;
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.synchronizationfailures);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
DEBUGF("could not synchronize message stream");
|
|
|
|
|
}
|
2021-04-21 02:14:21 +00:00
|
|
|
|
if (msg.version >= 10) {
|
2021-04-24 20:58:34 +00:00
|
|
|
|
p = AppendCrlf(stpcpy(stpcpy(p, "Date: "), shared->currentdate));
|
2021-05-05 14:25:39 +00:00
|
|
|
|
if (!branded) p = stpcpy(p, serverheader);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
if (extrahdrs) p = stpcpy(p, extrahdrs);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
if (connectionclose) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
p = stpcpy(p, "Connection: close\r\n");
|
2021-07-08 04:44:27 +00:00
|
|
|
|
} else if (timeout.tv_sec < 0 && msg.version >= 11) {
|
2021-04-21 02:14:21 +00:00
|
|
|
|
p = stpcpy(p, "Connection: keep-alive\r\n");
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
actualcontentlength = contentlength;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
if (gzipped) {
|
2021-03-25 09:21:13 +00:00
|
|
|
|
actualcontentlength += sizeof(kGzipHeader) + sizeof(gzip_footer);
|
2021-04-21 02:14:21 +00:00
|
|
|
|
p = stpcpy(p, "Content-Encoding: gzip\r\n");
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
p = AppendContentLength(p, actualcontentlength);
|
|
|
|
|
p = AppendCrlf(p);
|
|
|
|
|
CHECK_LE(p - hdrbuf.p, hdrbuf.n);
|
2021-04-24 20:58:34 +00:00
|
|
|
|
if (logmessages) LogMessage("sending", hdrbuf.p, p - hdrbuf.p);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
iov[0].iov_base = hdrbuf.p;
|
|
|
|
|
iov[0].iov_len = p - hdrbuf.p;
|
|
|
|
|
iovlen = 1;
|
|
|
|
|
if (!MustNotIncludeMessageBody()) {
|
|
|
|
|
if (gzipped) {
|
|
|
|
|
iov[iovlen].iov_base = kGzipHeader;
|
|
|
|
|
iov[iovlen].iov_len = sizeof(kGzipHeader);
|
|
|
|
|
++iovlen;
|
|
|
|
|
}
|
|
|
|
|
iov[iovlen].iov_base = content;
|
|
|
|
|
iov[iovlen].iov_len = contentlength;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
++iovlen;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
if (gzipped) {
|
|
|
|
|
iov[iovlen].iov_base = gzip_footer;
|
|
|
|
|
iov[iovlen].iov_len = sizeof(gzip_footer);
|
|
|
|
|
++iovlen;
|
|
|
|
|
}
|
2020-10-06 06:11:49 +00:00
|
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
|
} else {
|
|
|
|
|
iov[0].iov_base = content;
|
|
|
|
|
iov[0].iov_len = contentlength;
|
|
|
|
|
iovlen = 1;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
}
|
2021-04-23 17:45:19 +00:00
|
|
|
|
if (loglatency || LOGGABLE(kLogDebug)) {
|
2021-04-24 20:58:34 +00:00
|
|
|
|
flogf(kLogDebug, __FILE__, __LINE__, NULL, "%`'.*s latency %,ldµs",
|
2021-04-23 17:45:19 +00:00
|
|
|
|
msg.uri.b - msg.uri.a, inbuf.p + msg.uri.a,
|
|
|
|
|
(long)((nowl() - startrequest) * 1e6L));
|
|
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
|
Send(iov, iovlen);
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.messageshandled);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
++messageshandled;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
return true;
|
2020-09-07 04:39:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
|
static void InitRequest(void) {
|
|
|
|
|
frags = 0;
|
2021-04-25 00:09:01 +00:00
|
|
|
|
gzipped = 0;
|
|
|
|
|
branded = 0;
|
|
|
|
|
content = 0;
|
2021-03-27 14:29:55 +00:00
|
|
|
|
msgsize = 0;
|
2021-04-25 00:09:01 +00:00
|
|
|
|
loops.n = 0;
|
2021-04-24 20:58:34 +00:00
|
|
|
|
outbuf.n = 0;
|
2021-04-25 00:09:01 +00:00
|
|
|
|
luaheaderp = 0;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
contentlength = 0;
|
2021-06-28 00:03:06 +00:00
|
|
|
|
InitHttpMessage(&msg, kHttpRequest);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 17:45:19 +00:00
|
|
|
|
static void HandleMessages(void) {
|
2021-06-24 19:31:26 +00:00
|
|
|
|
bool once;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
ssize_t rc;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
size_t got;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
for (once = false;;) {
|
2021-03-25 09:21:13 +00:00
|
|
|
|
InitRequest();
|
|
|
|
|
startread = nowl();
|
|
|
|
|
for (;;) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
if (!msg.i && amtread) {
|
|
|
|
|
startrequest = nowl();
|
|
|
|
|
if (HandleMessage()) break;
|
|
|
|
|
}
|
2021-06-24 19:31:26 +00:00
|
|
|
|
if ((rc = reader(client, inbuf.p + amtread, inbuf.n - amtread)) != -1) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
startrequest = nowl();
|
2021-03-25 09:21:13 +00:00
|
|
|
|
got = rc;
|
|
|
|
|
amtread += got;
|
|
|
|
|
if (amtread) {
|
2021-06-24 19:31:26 +00:00
|
|
|
|
if (!once) {
|
|
|
|
|
once = true;
|
|
|
|
|
if (inbuf.p[0] == 22) {
|
|
|
|
|
#ifdef UNSECURE
|
|
|
|
|
WARNF("%s wants SSL but redbean was compiled with -DUNSECURE",
|
|
|
|
|
DescribeClient());
|
|
|
|
|
return;
|
|
|
|
|
#else
|
|
|
|
|
if (TlsSetup()) {
|
|
|
|
|
continue;
|
|
|
|
|
} else {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-23 17:45:19 +00:00
|
|
|
|
DEBUGF("%s read %,zd bytes", DescribeClient(), got);
|
|
|
|
|
if (HandleMessage()) {
|
2021-03-25 09:21:13 +00:00
|
|
|
|
break;
|
|
|
|
|
} else if (got) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
HandleFrag(got);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!got) {
|
2021-06-24 19:31:26 +00:00
|
|
|
|
NotifyClose();
|
2021-03-25 09:21:13 +00:00
|
|
|
|
LogClose("disconnect");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-04-23 17:45:19 +00:00
|
|
|
|
} else if (errno == EINTR) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.readinterrupts);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
} else if (errno == EAGAIN) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.readtimeouts);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
if (amtread) SendTimeout();
|
2021-06-24 19:31:26 +00:00
|
|
|
|
NotifyClose();
|
2021-04-23 17:45:19 +00:00
|
|
|
|
LogClose("timeout");
|
|
|
|
|
return;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
} else if (errno == ECONNRESET) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.readresets);
|
2021-03-25 09:21:13 +00:00
|
|
|
|
LogClose("reset");
|
|
|
|
|
return;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
} else {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.readerrors);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
WARNF("%s read failed %s", DescribeClient(), strerror(errno));
|
2021-03-25 09:21:13 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (killed || (terminated && !amtread) ||
|
|
|
|
|
(meltdown && (!amtread || nowl() - startread > 1))) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
if (amtread) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.dropped);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
SendServiceUnavailable();
|
|
|
|
|
}
|
2021-06-24 19:31:26 +00:00
|
|
|
|
NotifyClose();
|
2021-03-25 09:21:13 +00:00
|
|
|
|
LogClose(DescribeClose());
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-05-02 18:50:43 +00:00
|
|
|
|
if (invalidated) {
|
|
|
|
|
HandleReload();
|
|
|
|
|
invalidated = false;
|
|
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
|
}
|
2021-04-23 17:45:19 +00:00
|
|
|
|
if (msgsize == amtread) {
|
|
|
|
|
amtread = 0;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
if (killed) {
|
|
|
|
|
LogClose(DescribeClose());
|
|
|
|
|
return;
|
|
|
|
|
} else if (connectionclose || terminated || meltdown) {
|
|
|
|
|
NotifyClose();
|
2021-04-23 17:45:19 +00:00
|
|
|
|
LogClose(DescribeClose());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
CHECK_LT(msgsize, amtread);
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.pipelinedrequests);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
DEBUGF("%,ld pipelined bytes", amtread - msgsize);
|
|
|
|
|
memmove(inbuf.p, inbuf.p + msgsize, amtread - msgsize);
|
|
|
|
|
amtread -= msgsize;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
if (killed) {
|
|
|
|
|
LogClose(DescribeClose());
|
|
|
|
|
return;
|
|
|
|
|
} else if (connectionclose) {
|
|
|
|
|
NotifyClose();
|
2021-04-23 17:45:19 +00:00
|
|
|
|
LogClose(DescribeClose());
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-10-06 06:11:49 +00:00
|
|
|
|
}
|
2021-04-18 18:34:59 +00:00
|
|
|
|
CollectGarbage();
|
2021-05-02 18:50:43 +00:00
|
|
|
|
if (invalidated) {
|
|
|
|
|
HandleReload();
|
|
|
|
|
invalidated = false;
|
|
|
|
|
}
|
2020-10-06 06:11:49 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-24 19:31:26 +00:00
|
|
|
|
static void CloseServerFds(void) {
|
|
|
|
|
size_t i;
|
|
|
|
|
for (i = 0; i < servers.n; ++i) {
|
|
|
|
|
close(servers.p[i].fd);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void HandleConnection(size_t i) {
|
2020-10-06 06:11:49 +00:00
|
|
|
|
int pid;
|
|
|
|
|
clientaddrsize = sizeof(clientaddr);
|
2021-06-24 19:31:26 +00:00
|
|
|
|
if ((client = accept4(servers.p[i].fd, &clientaddr, &clientaddrsize,
|
|
|
|
|
SOCK_CLOEXEC)) != -1) {
|
2020-10-06 06:11:49 +00:00
|
|
|
|
startconnection = nowl();
|
2021-04-23 17:45:19 +00:00
|
|
|
|
messageshandled = 0;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
if (uniprocess) {
|
|
|
|
|
pid = -1;
|
|
|
|
|
connectionclose = true;
|
|
|
|
|
} else {
|
|
|
|
|
switch ((pid = fork())) {
|
|
|
|
|
case 0:
|
|
|
|
|
meltdown = false;
|
|
|
|
|
connectionclose = false;
|
2021-04-24 20:58:34 +00:00
|
|
|
|
if (funtrace && !IsTiny()) {
|
|
|
|
|
ftrace_install();
|
|
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
|
break;
|
|
|
|
|
case -1:
|
2021-05-03 19:09:35 +00:00
|
|
|
|
HandleForkFailure();
|
2021-04-23 17:45:19 +00:00
|
|
|
|
return;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
default:
|
2021-03-29 08:22:49 +00:00
|
|
|
|
++shared->workers;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
close(client);
|
2021-06-24 19:31:26 +00:00
|
|
|
|
ReseedRng(&rng, "parent");
|
2021-03-25 09:21:13 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
2020-10-06 06:11:49 +00:00
|
|
|
|
}
|
2021-06-24 19:31:26 +00:00
|
|
|
|
if (!pid) CloseServerFds();
|
2021-07-08 04:44:27 +00:00
|
|
|
|
VERBOSEF("ACCEPT %s VIA %s", DescribeClient(), DescribeServer());
|
2021-04-23 17:45:19 +00:00
|
|
|
|
HandleMessages();
|
|
|
|
|
DEBUGF("%s closing after %,ldµs", DescribeClient(),
|
|
|
|
|
(long)((nowl() - startconnection) * 1e6L));
|
2021-04-18 18:34:59 +00:00
|
|
|
|
if (!pid) {
|
|
|
|
|
_exit(0);
|
|
|
|
|
} else {
|
2021-06-24 19:31:26 +00:00
|
|
|
|
close(client);
|
|
|
|
|
oldin.p = 0;
|
|
|
|
|
oldin.n = 0;
|
|
|
|
|
if (inbuf.c) {
|
|
|
|
|
inbuf.p -= inbuf.c;
|
|
|
|
|
inbuf.n += inbuf.c;
|
|
|
|
|
inbuf.c = 0;
|
|
|
|
|
}
|
|
|
|
|
#ifndef UNSECURE
|
|
|
|
|
if (usessl) {
|
|
|
|
|
usessl = false;
|
|
|
|
|
reader = read;
|
|
|
|
|
writer = WritevAll;
|
|
|
|
|
mbedtls_ssl_session_reset(&ssl);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2021-04-18 18:34:59 +00:00
|
|
|
|
CollectGarbage();
|
|
|
|
|
}
|
2021-04-23 17:45:19 +00:00
|
|
|
|
} else if (errno == EINTR || errno == EAGAIN) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.acceptinterrupts);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
} else if (errno == ENFILE) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.enfiles);
|
2021-06-24 19:31:26 +00:00
|
|
|
|
WARNF("too many open files");
|
|
|
|
|
meltdown = true;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
} else if (errno == EMFILE) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.emfiles);
|
2021-06-24 19:31:26 +00:00
|
|
|
|
WARNF("ran out of open file quota");
|
|
|
|
|
meltdown = true;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
} else if (errno == ENOMEM) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.enomems);
|
2021-06-24 19:31:26 +00:00
|
|
|
|
WARNF("ran out of memory");
|
|
|
|
|
meltdown = true;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
} else if (errno == ENOBUFS) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.enobufs);
|
2021-06-24 19:31:26 +00:00
|
|
|
|
WARNF("ran out of buffer");
|
|
|
|
|
meltdown = true;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
} else if (errno == ENONET) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.enonets);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
WARNF("%s network gone", DescribeServer());
|
2021-06-24 19:31:26 +00:00
|
|
|
|
polls[i].fd = -polls[i].fd;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
} else if (errno == ENETDOWN) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.enetdowns);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
WARNF("%s network down", DescribeServer());
|
2021-06-24 19:31:26 +00:00
|
|
|
|
polls[i].fd = -polls[i].fd;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
} else if (errno == ECONNABORTED) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.acceptresets);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
WARNF("%s connection reset before accept");
|
|
|
|
|
} else if (errno == ENETUNREACH || errno == EHOSTUNREACH ||
|
|
|
|
|
errno == EOPNOTSUPP || errno == ENOPROTOOPT || errno == EPROTO) {
|
2021-05-03 19:09:35 +00:00
|
|
|
|
LockInc(&shared->c.accepterrors);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
WARNF("%s ephemeral accept error %s", DescribeServer(), strerror(errno));
|
|
|
|
|
} else {
|
|
|
|
|
FATALF("%s accept error %s", DescribeServer(), strerror(errno));
|
2020-10-06 06:11:49 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-24 19:31:26 +00:00
|
|
|
|
static void HandlePoll(void) {
|
|
|
|
|
size_t i;
|
|
|
|
|
if (poll(polls, servers.n, 500) != -1) {
|
|
|
|
|
for (i = 0; i < servers.n; ++i) {
|
|
|
|
|
if (polls[i].revents) {
|
|
|
|
|
serveraddr = &servers.p[i].addr;
|
|
|
|
|
HandleConnection(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (errno == EINTR || errno == EAGAIN) {
|
|
|
|
|
LockInc(&shared->c.pollinterrupts);
|
|
|
|
|
} else if (errno == ENOMEM) {
|
|
|
|
|
LockInc(&shared->c.enomems);
|
|
|
|
|
WARNF("%s ran out of memory");
|
|
|
|
|
meltdown = true;
|
|
|
|
|
} else {
|
|
|
|
|
FATALF("poll error %s", strerror(errno));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-02 18:50:43 +00:00
|
|
|
|
static void RestoreApe(void) {
|
2021-04-18 18:34:59 +00:00
|
|
|
|
char *p;
|
|
|
|
|
size_t n;
|
|
|
|
|
struct Asset *a;
|
|
|
|
|
extern char ape_rom_vaddr[] __attribute__((__weak__));
|
2021-07-04 19:21:29 +00:00
|
|
|
|
if (!(SUPPORT_VECTOR & (METAL | WINDOWS | XNU))) return;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
if (IsWindows()) return; /* TODO */
|
|
|
|
|
if (IsOpenbsd()) return; /* TODO */
|
|
|
|
|
if (IsNetbsd()) return; /* TODO */
|
2021-05-02 18:50:43 +00:00
|
|
|
|
if (endswith(zpath, ".com.dbg")) return;
|
2021-05-03 20:06:43 +00:00
|
|
|
|
close(zfd);
|
|
|
|
|
zfd = OpenExecutable();
|
2021-04-23 17:45:19 +00:00
|
|
|
|
if ((a = GetAssetZip("/.ape", 5)) && (p = LoadAsset(a, &n))) {
|
2021-05-03 20:06:43 +00:00
|
|
|
|
write(zfd, p, n);
|
2021-04-18 18:34:59 +00:00
|
|
|
|
free(p);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
} else {
|
2021-05-03 18:52:24 +00:00
|
|
|
|
WARNF("/.ape not found");
|
2021-04-18 18:34:59 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-24 19:31:26 +00:00
|
|
|
|
static void Listen(void) {
|
|
|
|
|
char ipbuf[16];
|
|
|
|
|
size_t i, j, n;
|
|
|
|
|
uint32_t ip, port, addrsize, *ifs, *ifp;
|
|
|
|
|
if (!ports.n) {
|
|
|
|
|
ProgramPort(8080);
|
|
|
|
|
}
|
|
|
|
|
if (!ips.n) {
|
|
|
|
|
if ((ifs = GetHostIps()) && *ifs) {
|
|
|
|
|
for (ifp = ifs; *ifp; ++ifp) {
|
|
|
|
|
sprintf(ipbuf, "%hhu.%hhu.%hhu.%hhu", *ifp >> 24, *ifp >> 16, *ifp >> 8,
|
|
|
|
|
*ifp);
|
|
|
|
|
ProgramAddr(ipbuf);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ProgramAddr("0.0.0.0");
|
|
|
|
|
}
|
|
|
|
|
free(ifs);
|
|
|
|
|
}
|
|
|
|
|
servers.p = malloc(ips.n * ports.n * sizeof(*servers.p));
|
|
|
|
|
for (n = i = 0; i < ips.n; ++i) {
|
|
|
|
|
for (j = 0; j < ports.n; ++j, ++n) {
|
|
|
|
|
memset(servers.p + n, 0, sizeof(*servers.p));
|
|
|
|
|
servers.p[n].addr.sin_family = AF_INET;
|
|
|
|
|
servers.p[n].addr.sin_port = htons(ports.p[j]);
|
|
|
|
|
servers.p[n].addr.sin_addr.s_addr = htonl(ips.p[i]);
|
2021-07-08 04:44:27 +00:00
|
|
|
|
if ((servers.p[n].fd = Socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC,
|
|
|
|
|
IPPROTO_TCP, true)) == -1) {
|
2021-06-24 19:31:26 +00:00
|
|
|
|
perror("socket");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
if (bind(servers.p[n].fd, &servers.p[n].addr,
|
|
|
|
|
sizeof(servers.p[n].addr)) == -1) {
|
|
|
|
|
fprintf(stderr, "error: %s: %hhu.%hhu.%hhu.%hhu:%hu\n", strerror(errno),
|
|
|
|
|
ips.p[i] >> 24, ips.p[i] >> 16, ips.p[i] >> 8, ips.p[i],
|
|
|
|
|
ports.p[j]);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
if (listen(servers.p[n].fd, 10) == -1) {
|
|
|
|
|
perror("listen");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
addrsize = sizeof(servers.p[n].addr);
|
|
|
|
|
if (getsockname(servers.p[n].fd, &servers.p[n].addr, &addrsize) == -1) {
|
|
|
|
|
perror("getsockname");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
port = ntohs(servers.p[n].addr.sin_port);
|
|
|
|
|
ip = ntohl(servers.p[n].addr.sin_addr.s_addr);
|
|
|
|
|
if (ip == INADDR_ANY) ip = INADDR_LOOPBACK;
|
|
|
|
|
LOGF("LISTEN http://%hhu.%hhu.%hhu.%hhu:%d", ip >> 24, ip >> 16, ip >> 8,
|
|
|
|
|
ip, port);
|
|
|
|
|
if (printport && !ports.p[j]) {
|
|
|
|
|
printf("%d\n", port);
|
|
|
|
|
fflush(stdout);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
servers.n = n;
|
|
|
|
|
polls = malloc(n * sizeof(*polls));
|
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
|
|
|
polls[i].fd = servers.p[i].fd;
|
|
|
|
|
polls[i].events = POLLIN;
|
|
|
|
|
polls[i].revents = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-02 18:50:43 +00:00
|
|
|
|
void RedBean(int argc, char *argv[]) {
|
2021-06-24 19:31:26 +00:00
|
|
|
|
long double t;
|
|
|
|
|
#ifndef UNSECURE
|
|
|
|
|
InitializeRng(&rng);
|
2021-07-08 04:44:27 +00:00
|
|
|
|
InitializeRng(&rngcli);
|
2021-06-24 19:31:26 +00:00
|
|
|
|
LoadSslRoots();
|
|
|
|
|
#endif
|
|
|
|
|
reader = read;
|
|
|
|
|
writer = WritevAll;
|
2021-04-23 17:45:19 +00:00
|
|
|
|
gmtoff = GetGmtOffset((lastrefresh = startserver = nowl()));
|
2021-04-18 18:34:59 +00:00
|
|
|
|
CHECK_GT(CLK_TCK, 0);
|
2021-03-29 08:22:49 +00:00
|
|
|
|
CHECK_NE(MAP_FAILED,
|
|
|
|
|
(shared = mmap(NULL, ROUNDUP(sizeof(struct Shared), FRAMESIZE),
|
|
|
|
|
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS,
|
|
|
|
|
-1, 0)));
|
2021-05-02 18:50:43 +00:00
|
|
|
|
zpath = (const char *)getauxval(AT_EXECFN);
|
2021-05-03 20:06:43 +00:00
|
|
|
|
CHECK_NE(-1, (zfd = open(zpath, O_RDONLY)));
|
2021-05-14 12:36:58 +00:00
|
|
|
|
CHECK_NE(-1, fstat(zfd, &zst));
|
|
|
|
|
OpenZip(true);
|
2021-05-02 18:50:43 +00:00
|
|
|
|
RestoreApe();
|
2021-03-29 08:22:49 +00:00
|
|
|
|
SetDefaults();
|
|
|
|
|
GetOpts(argc, argv);
|
|
|
|
|
LuaInit();
|
|
|
|
|
if (uniprocess) shared->workers = 1;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
xsigaction(SIGINT, OnInt, 0, 0, 0);
|
|
|
|
|
xsigaction(SIGHUP, OnHup, 0, 0, 0);
|
|
|
|
|
xsigaction(SIGTERM, OnTerm, 0, 0, 0);
|
|
|
|
|
xsigaction(SIGCHLD, OnChld, 0, 0, 0);
|
|
|
|
|
xsigaction(SIGUSR1, OnUsr1, 0, 0, 0);
|
|
|
|
|
xsigaction(SIGUSR2, OnUsr2, 0, 0, 0);
|
2020-10-06 06:11:49 +00:00
|
|
|
|
xsigaction(SIGPIPE, SIG_IGN, 0, 0, 0);
|
2021-04-23 17:45:19 +00:00
|
|
|
|
/* TODO(jart): SIGXCPU and SIGXFSZ */
|
2021-06-24 19:31:26 +00:00
|
|
|
|
Listen();
|
|
|
|
|
#ifndef UNSECURE
|
|
|
|
|
mbedtls_ssl_config_defaults(
|
|
|
|
|
&conf, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM,
|
|
|
|
|
suiteb ? MBEDTLS_SSL_PRESET_SUITEB : MBEDTLS_SSL_PRESET_DEFAULT);
|
2021-07-08 04:44:27 +00:00
|
|
|
|
mbedtls_ssl_config_defaults(
|
|
|
|
|
&confcli, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM,
|
|
|
|
|
suiteb ? MBEDTLS_SSL_PRESET_SUITEB : MBEDTLS_SSL_PRESET_DEFAULT);
|
2021-06-24 19:31:26 +00:00
|
|
|
|
mbedtls_ssl_conf_dbg(&conf, TlsDebug, 0);
|
2021-07-08 04:44:27 +00:00
|
|
|
|
mbedtls_ssl_conf_dbg(&confcli, TlsDebug, 0);
|
2021-06-24 19:31:26 +00:00
|
|
|
|
LoadCertificates();
|
|
|
|
|
mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &rng);
|
2021-07-08 04:44:27 +00:00
|
|
|
|
mbedtls_ssl_conf_rng(&confcli, mbedtls_ctr_drbg_random, &rngcli);
|
|
|
|
|
mbedtls_ssl_conf_authmode(&conf, sslclientverify ? MBEDTLS_SSL_VERIFY_REQUIRED
|
|
|
|
|
: MBEDTLS_SSL_VERIFY_NONE);
|
|
|
|
|
mbedtls_ssl_conf_authmode(&confcli, sslfetchverify
|
|
|
|
|
? MBEDTLS_SSL_VERIFY_REQUIRED
|
|
|
|
|
: MBEDTLS_SSL_VERIFY_NONE);
|
|
|
|
|
mbedtls_ssl_conf_ca_chain(&confcli, GetSslRoots(), 0);
|
2021-07-03 12:51:04 +00:00
|
|
|
|
mbedtls_ssl_setup(&ssl, &conf);
|
2021-07-08 04:44:27 +00:00
|
|
|
|
mbedtls_ssl_setup(&sslcli, &confcli);
|
2021-07-03 12:51:04 +00:00
|
|
|
|
mbedtls_ssl_set_bio(&ssl, &client, TlsSend, 0, TlsRecv);
|
2021-06-24 19:31:26 +00:00
|
|
|
|
#endif
|
|
|
|
|
if (launchbrowser) {
|
|
|
|
|
LaunchBrowser(launchbrowser);
|
2021-02-27 18:47:19 +00:00
|
|
|
|
}
|
2021-05-03 08:21:50 +00:00
|
|
|
|
if (daemonize) {
|
|
|
|
|
Daemonize();
|
|
|
|
|
} else {
|
|
|
|
|
setpgid(getpid(), getpid());
|
2021-07-08 22:56:23 +00:00
|
|
|
|
if (logpath) {
|
|
|
|
|
close(2);
|
|
|
|
|
open(logpath, O_APPEND | O_WRONLY | O_CREAT, 0640);
|
|
|
|
|
}
|
|
|
|
|
ChangeUser();
|
2021-05-03 08:21:50 +00:00
|
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
|
UpdateCurrentDate(nowl());
|
2021-04-23 17:45:19 +00:00
|
|
|
|
freelist.c = 8;
|
|
|
|
|
freelist.p = xcalloc(freelist.c, sizeof(*freelist.p));
|
|
|
|
|
unmaplist.c = 1;
|
|
|
|
|
unmaplist.p = xcalloc(unmaplist.c, sizeof(*unmaplist.p));
|
2021-03-25 09:21:13 +00:00
|
|
|
|
hdrbuf.n = 4 * 1024;
|
2021-05-03 08:21:50 +00:00
|
|
|
|
hdrbuf.p = xmalloc(hdrbuf.n);
|
2021-05-01 12:11:35 +00:00
|
|
|
|
inbuf.n = maxpayloadsize;
|
2021-05-03 08:21:50 +00:00
|
|
|
|
inbuf.p = xmalloc(inbuf.n);
|
2021-07-08 22:56:23 +00:00
|
|
|
|
oldloglevel = __log_level;
|
2020-09-07 04:39:00 +00:00
|
|
|
|
while (!terminated) {
|
2021-03-25 09:21:13 +00:00
|
|
|
|
if (zombied) {
|
|
|
|
|
ReapZombies();
|
|
|
|
|
} else if (invalidated) {
|
2021-04-23 17:45:19 +00:00
|
|
|
|
HandleReload();
|
2020-10-06 06:11:49 +00:00
|
|
|
|
invalidated = false;
|
2021-04-21 02:14:21 +00:00
|
|
|
|
} else if (meltdown) {
|
|
|
|
|
EnterMeltdownMode();
|
|
|
|
|
meltdown = false;
|
2021-06-24 19:31:26 +00:00
|
|
|
|
} else if ((t = nowl()) - lastheartbeat > .5) {
|
|
|
|
|
lastheartbeat = t;
|
|
|
|
|
HandleHeartbeat();
|
2021-03-25 09:21:13 +00:00
|
|
|
|
} else {
|
2021-06-24 19:31:26 +00:00
|
|
|
|
HandlePoll();
|
2020-10-06 06:11:49 +00:00
|
|
|
|
}
|
2020-09-07 04:39:00 +00:00
|
|
|
|
}
|
2021-06-24 19:31:26 +00:00
|
|
|
|
CloseServerFds();
|
2021-04-23 17:45:19 +00:00
|
|
|
|
if (keyboardinterrupt) {
|
|
|
|
|
LOGF("received keyboard interrupt");
|
|
|
|
|
} else {
|
|
|
|
|
LOGF("received term signal");
|
2021-03-25 09:21:13 +00:00
|
|
|
|
if (!killed) {
|
|
|
|
|
terminated = false;
|
|
|
|
|
}
|
2021-04-23 17:45:19 +00:00
|
|
|
|
DEBUGF("sending TERM to process group");
|
2021-03-25 09:21:13 +00:00
|
|
|
|
LOGIFNEG1(kill(0, SIGTERM));
|
|
|
|
|
}
|
2020-10-06 06:11:49 +00:00
|
|
|
|
WaitAll();
|
2021-04-23 17:45:19 +00:00
|
|
|
|
VERBOSEF("shutdown complete");
|
2020-10-06 06:11:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int main(int argc, char *argv[]) {
|
2021-04-25 02:49:49 +00:00
|
|
|
|
if (!IsTiny()) {
|
|
|
|
|
setenv("GDB", "", true);
|
|
|
|
|
showcrashreports();
|
|
|
|
|
}
|
2021-05-02 18:50:43 +00:00
|
|
|
|
RedBean(argc, argv);
|
2020-09-07 04:39:00 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|