mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-01 03:53:33 +00:00
cc1920749e
Your redbean can now interoperate with clients that require TLS crypto. This is accomplished using a protocol polyglot that lets us distinguish between HTTP and HTTPS regardless of the port number. Certificates will be generated automatically, if none are supplied by the user. Footprint increases by only a few hundred kb so redbean in MODY=tiny is now 1.0mb - Add lseek() polyfills for ZIP executable - Automatically polyfill /tmp/FOO paths on NT - Fix readdir() / ftw() / nftw() bugs on Windows - Introduce -B flag for slower SSL that's stronger - Remove mbedtls features Cosmopolitan doesn't need - Have base64 decoder support the uri-safe alternative - Remove Truncated HMAC because it's forbidden by the IETF - Add all the mbedtls test suites and make them go 3x faster - Support opendir() / readdir() / closedir() on ZIP executable - Use Everest for ECDHE-ECDSA because it's so good it's so good - Add tinier implementation of sha1 since it's not worth the rom - Add chi-square monte-carlo mean correlation tests for getrandom() - Source entropy on Windows from the proper interface everyone uses We're continuing to outperform NGINX and other servers on raw message throughput. Using SSL means that instead of 1,000,000 qps you can get around 300,000 qps. However redbean isn't as fast as NGINX yet at SSL handshakes, since redbean can do 2,627 per second and NGINX does 4.3k Right now, the SSL UX story works best if you give your redbean a key signing key since that can be easily generated by openssl using a one liner then redbean will do all the things that are impossibly hard to do like signing ecdsa and rsa certificates that'll work in chrome. We should integrate the let's encrypt acme protocol in the future. Live Demo: https://redbean.justine.lol/ Root Cert: https://redbean.justine.lol/redbean1.crt
392 lines
13 KiB
C
392 lines
13 KiB
C
/*-*- 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 (c) 1989 The Regents of the University of California. │
|
|
│ All rights reserved. │
|
|
│ │
|
|
│ Redistribution and use in source and binary forms are permitted │
|
|
│ provided that the above copyright notice and this paragraph are │
|
|
│ duplicated in all such forms and that any documentation, │
|
|
│ advertising materials, and other materials related to such │
|
|
│ distribution and use acknowledge that the software was developed │
|
|
│ by the University of California, Berkeley. The name of the │
|
|
│ University may not be used to endorse or promote products derived │
|
|
│ from this software without specific prior written permission. │
|
|
│ THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR │
|
|
│ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED │
|
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "libc/assert.h"
|
|
#include "libc/calls/calls.h"
|
|
#include "libc/fmt/fmt.h"
|
|
#include "libc/fmt/itoa.h"
|
|
#include "libc/macros.internal.h"
|
|
#include "libc/nexgen32e/nexgen32e.h"
|
|
#include "libc/time/struct/tm.h"
|
|
#include "libc/time/time.h"
|
|
#include "libc/time/tzfile.internal.h"
|
|
|
|
asm(".ident\t\"\\n\\n\
|
|
strftime (BSD-3)\\n\
|
|
Copyright 1989 The Regents of the University of California\"");
|
|
asm(".include \"libc/disclaimer.inc\"");
|
|
|
|
static char *strftime_add(char *p, const char *pe, const char *str) {
|
|
while (p < pe && (*p = *str++)) ++p;
|
|
return p;
|
|
}
|
|
|
|
static char *strftime_conv(char *p, const char *pe, int n, const char *format) {
|
|
char buf[22];
|
|
(snprintf)(buf, sizeof(buf), format, n);
|
|
return strftime_add(p, pe, buf);
|
|
}
|
|
|
|
static char *strftime_secs(char *p, const char *pe, const struct tm *t) {
|
|
char ibuf[21];
|
|
struct tm tmp;
|
|
int64_t s;
|
|
tmp = *t; /* Make a copy, mktime(3) modifies the tm struct. */
|
|
s = mktime(&tmp);
|
|
int64toarray_radix10(s, ibuf);
|
|
return strftime_add(p, pe, ibuf);
|
|
}
|
|
|
|
static char *strftime_timefmt(char *p, const char *pe, const char *format,
|
|
const struct tm *t) {
|
|
int i;
|
|
long diff;
|
|
char const *sign;
|
|
/* size_t z1, z2, z3; */
|
|
for (; *format; ++format) {
|
|
if (*format == '%') {
|
|
label:
|
|
switch (*++format) {
|
|
case '\0':
|
|
--format;
|
|
break;
|
|
case 'A':
|
|
p = strftime_add(p, pe,
|
|
(t->tm_wday < 0 || t->tm_wday > 6)
|
|
? "?"
|
|
: kWeekdayName[t->tm_wday]);
|
|
continue;
|
|
case 'a':
|
|
p = strftime_add(p, pe,
|
|
(t->tm_wday < 0 || t->tm_wday > 6)
|
|
? "?"
|
|
: kWeekdayNameShort[t->tm_wday]);
|
|
continue;
|
|
case 'B':
|
|
p = strftime_add(
|
|
p, pe,
|
|
(t->tm_mon < 0 || t->tm_mon > 11) ? "?" : kMonthName[t->tm_mon]);
|
|
continue;
|
|
case 'b':
|
|
case 'h':
|
|
p = strftime_add(p, pe,
|
|
(t->tm_mon < 0 || t->tm_mon > 11)
|
|
? "?"
|
|
: kMonthNameShort[t->tm_mon]);
|
|
continue;
|
|
case 'c':
|
|
p = strftime_timefmt(p, pe, "%D %X", t);
|
|
continue;
|
|
case 'C':
|
|
/*
|
|
** %C used to do a...
|
|
** strftime_timefmt("%a %b %e %X %Y", t);
|
|
** ...whereas now POSIX 1003.2 calls for
|
|
** something completely different.
|
|
** (ado, 5/24/93)
|
|
*/
|
|
p = strftime_conv(p, pe, div100int64(t->tm_year + TM_YEAR_BASE),
|
|
"%02d");
|
|
continue;
|
|
case 'D':
|
|
p = strftime_timefmt(p, pe, "%m/%d/%y", t);
|
|
continue;
|
|
case 'x':
|
|
/*
|
|
** Version 3.0 of strftime from Arnold Robbins
|
|
** (arnold@skeeve.atl.ga.us) does the
|
|
** equivalent of...
|
|
** strftime_timefmt("%a %b %e %Y");
|
|
** ...for %x; since the X3J11 C language
|
|
** standard calls for "date, using locale's
|
|
** date format," anything goes. Using just
|
|
** numbers (as here) makes Quakers happier.
|
|
** Word from Paul Eggert (eggert@twinsun.com)
|
|
** is that %Y-%m-%d is the ISO standard date
|
|
** format, specified in ISO 2014 and later
|
|
** ISO 8601:1988, with a summary available in
|
|
** pub/doc/ISO/english/ISO8601.ps.Z on
|
|
** ftp.uni-erlangen.de.
|
|
** (ado, 5/30/93)
|
|
*/
|
|
p = strftime_timefmt(p, pe, "%m/%d/%y", t);
|
|
continue;
|
|
case 'd':
|
|
p = strftime_conv(p, pe, t->tm_mday, "%02d");
|
|
continue;
|
|
case 'E':
|
|
case 'O':
|
|
/*
|
|
** POSIX locale extensions, a la
|
|
** Arnold Robbins' strftime version 3.0.
|
|
** The sequences
|
|
** %Ec %EC %Ex %Ey %EY
|
|
** %Od %oe %OH %OI %Om %OM
|
|
** %OS %Ou %OU %OV %Ow %OW %Oy
|
|
** are supposed to provide alternate
|
|
** representations.
|
|
** (ado, 5/24/93)
|
|
*/
|
|
goto label;
|
|
case 'e':
|
|
p = strftime_conv(p, pe, t->tm_mday, "%2d");
|
|
continue;
|
|
case 'H':
|
|
p = strftime_conv(p, pe, t->tm_hour, "%02d");
|
|
continue;
|
|
case 'I':
|
|
p = strftime_conv(p, pe, (t->tm_hour % 12) ? (t->tm_hour % 12) : 12,
|
|
"%02d");
|
|
continue;
|
|
case 'j':
|
|
p = strftime_conv(p, pe, t->tm_yday + 1, "%03d");
|
|
continue;
|
|
case 'k':
|
|
/*
|
|
** This used to be...
|
|
** strftime_conv(t->tm_hour % 12 ?
|
|
** t->tm_hour % 12 : 12, 2, ' ');
|
|
** ...and has been changed to the below to
|
|
** match SunOS 4.1.1 and Arnold Robbins'
|
|
** strftime version 3.0. That is, "%k" and
|
|
** "%l" have been swapped.
|
|
** (ado, 5/24/93)
|
|
*/
|
|
p = strftime_conv(p, pe, t->tm_hour, "%2d");
|
|
continue;
|
|
#ifdef KITCHEN_SINK
|
|
case 'K':
|
|
/*
|
|
** After all this time, still unclaimed!
|
|
*/
|
|
p = strftime_add(p, pe, "kitchen sink");
|
|
continue;
|
|
#endif /* defined KITCHEN_SINK */
|
|
case 'l':
|
|
/*
|
|
** This used to be...
|
|
** strftime_conv(t->tm_hour, 2, ' ');
|
|
** ...and has been changed to the below to
|
|
** match SunOS 4.1.1 and Arnold Robbin's
|
|
** strftime version 3.0. That is, "%k" and
|
|
** "%l" have been swapped.
|
|
** (ado, 5/24/93)
|
|
*/
|
|
p = strftime_conv(p, pe, (t->tm_hour % 12) ? (t->tm_hour % 12) : 12,
|
|
"%2d");
|
|
continue;
|
|
case 'M':
|
|
p = strftime_conv(p, pe, t->tm_min, "%02d");
|
|
continue;
|
|
case 'm':
|
|
p = strftime_conv(p, pe, t->tm_mon + 1, "%02d");
|
|
continue;
|
|
case 'n':
|
|
p = strftime_add(p, pe, "\n");
|
|
continue;
|
|
case 'p':
|
|
p = strftime_add(p, pe, t->tm_hour >= 12 ? "PM" : "AM");
|
|
continue;
|
|
case 'R':
|
|
p = strftime_timefmt(p, pe, "%H:%M", t);
|
|
continue;
|
|
case 'r':
|
|
p = strftime_timefmt(p, pe, "%I:%M:%S %p", t);
|
|
continue;
|
|
case 'S':
|
|
p = strftime_conv(p, pe, t->tm_sec, "%02d");
|
|
continue;
|
|
case 's':
|
|
p = strftime_secs(p, pe, t);
|
|
continue;
|
|
case 'T':
|
|
case 'X':
|
|
p = strftime_timefmt(p, pe, "%H:%M:%S", t);
|
|
continue;
|
|
case 't':
|
|
p = strftime_add(p, pe, "\t");
|
|
continue;
|
|
case 'U':
|
|
p = strftime_conv(p, pe, (t->tm_yday + 7 - t->tm_wday) / 7, "%02d");
|
|
continue;
|
|
case 'u':
|
|
/*
|
|
** From Arnold Robbins' strftime version 3.0:
|
|
** "ISO 8601: Weekday as a decimal number
|
|
** [1 (Monday) - 7]"
|
|
** (ado, 5/24/93)
|
|
*/
|
|
p = strftime_conv(p, pe, (t->tm_wday == 0) ? 7 : t->tm_wday, "%d");
|
|
continue;
|
|
case 'V':
|
|
/*
|
|
** From Arnold Robbins' strftime version 3.0:
|
|
** "the week number of the year (the first
|
|
** Monday as the first day of week 1) as a
|
|
** decimal number (01-53). The method for
|
|
** determining the week number is as specified
|
|
** by ISO 8601 (to wit: if the week containing
|
|
** January 1 has four or more days in the new
|
|
** year, then it is week 1, otherwise it is
|
|
** week 53 of the previous year and the next
|
|
** week is week 1)."
|
|
** (ado, 5/24/93)
|
|
*/
|
|
/*
|
|
** XXX--If January 1 falls on a Friday,
|
|
** January 1-3 are part of week 53 of the
|
|
** previous year. By analogy, if January
|
|
** 1 falls on a Thursday, are December 29-31
|
|
** of the PREVIOUS year part of week 1???
|
|
** (ado 5/24/93)
|
|
**
|
|
** You are understood not to expect this.
|
|
*/
|
|
i = (t->tm_yday + 10 - (t->tm_wday ? (t->tm_wday - 1) : 6)) / 7;
|
|
if (i == 0) {
|
|
/*
|
|
** What day of the week does
|
|
** January 1 fall on?
|
|
*/
|
|
i = t->tm_wday - (t->tm_yday - 1);
|
|
/*
|
|
** Fri Jan 1: 53
|
|
** Sun Jan 1: 52
|
|
** Sat Jan 1: 53 if previous
|
|
** year a leap
|
|
** year, else 52
|
|
*/
|
|
if (i == TM_FRIDAY)
|
|
i = 53;
|
|
else if (i == TM_SUNDAY)
|
|
i = 52;
|
|
else
|
|
i = isleap(t->tm_year + TM_YEAR_BASE) ? 53 : 52;
|
|
#ifdef XPG4_1994_04_09
|
|
/*
|
|
** As of 4/9/94, though,
|
|
** XPG4 calls for 53
|
|
** unconditionally.
|
|
*/
|
|
i = 53;
|
|
#endif /* defined XPG4_1994_04_09 */
|
|
}
|
|
p = strftime_conv(p, pe, i, "%02d");
|
|
continue;
|
|
case 'v':
|
|
/*
|
|
** From Arnold Robbins' strftime version 3.0:
|
|
** "date as dd-bbb-YYYY"
|
|
** (ado, 5/24/93)
|
|
*/
|
|
p = strftime_timefmt(p, pe, "%e-%b-%Y", t);
|
|
continue;
|
|
case 'W':
|
|
p = strftime_conv(
|
|
p, pe, (t->tm_yday + 7 - (t->tm_wday ? (t->tm_wday - 1) : 6)) / 7,
|
|
"%02d");
|
|
continue;
|
|
case 'w':
|
|
p = strftime_conv(p, pe, t->tm_wday, "%d");
|
|
continue;
|
|
case 'y':
|
|
p = strftime_conv(p, pe, (t->tm_year + TM_YEAR_BASE) % 100, "%02d");
|
|
continue;
|
|
case 'Y':
|
|
p = strftime_conv(p, pe, t->tm_year + TM_YEAR_BASE, "%04d");
|
|
continue;
|
|
case 'Z':
|
|
if (t->tm_zone) {
|
|
p = strftime_add(p, pe, t->tm_zone);
|
|
} else {
|
|
if (t->tm_isdst == 0 || t->tm_isdst == 1) {
|
|
p = strftime_add(p, pe, tzname[t->tm_isdst]);
|
|
} else {
|
|
p = strftime_add(p, pe, "?");
|
|
}
|
|
}
|
|
continue;
|
|
case 'z':
|
|
if (t->tm_isdst < 0) continue;
|
|
#ifdef TM_GMTOFF
|
|
diff = t->TM_GMTOFF;
|
|
#else /* !defined TM_GMTOFF */
|
|
if (t->tm_isdst == 0)
|
|
#ifdef USG_COMPAT
|
|
diff = -timezone;
|
|
#else /* !defined USG_COMPAT */
|
|
continue;
|
|
#endif /* !defined USG_COMPAT */
|
|
else
|
|
#ifdef ALTZONE
|
|
diff = -altzone;
|
|
#else /* !defined ALTZONE */
|
|
continue;
|
|
#endif /* !defined ALTZONE */
|
|
#endif /* !defined TM_GMTOFF */
|
|
if (diff < 0) {
|
|
sign = "-";
|
|
diff = -diff;
|
|
} else {
|
|
sign = "+";
|
|
}
|
|
p = strftime_add(p, pe, sign);
|
|
diff /= SECSPERMIN;
|
|
diff = (diff / MINSPERHOUR) * 100 + (diff % MINSPERHOUR);
|
|
p = strftime_conv(p, pe, diff, "%04d");
|
|
continue;
|
|
case '%':
|
|
/*
|
|
* X311J/88-090 (4.12.3.5): if conversion char is
|
|
* undefined, behavior is undefined. Print out the
|
|
* character itself as printf(3) also does.
|
|
*/
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (p >= pe) break;
|
|
*p++ = *format;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
/**
|
|
* Converts time to string, e.g.
|
|
*
|
|
* char b[64];
|
|
* int64_t sec;
|
|
* struct tm tm;
|
|
* time(&sec);
|
|
* localtime_r(&sec, &tm);
|
|
* strftime(b, sizeof(b), "%Y-%m-%dT%H:%M:%S%z", &tm); // ISO8601
|
|
* strftime(b, sizeof(b), "%a, %d %b %Y %H:%M:%S %Z", &tm); // RFC1123
|
|
*
|
|
* @return bytes copied excluding nul, or 0 on error
|
|
*/
|
|
size_t strftime(char *s, size_t size, const char *f, const struct tm *t) {
|
|
char *p;
|
|
p = strftime_timefmt(s, s + size, f, t);
|
|
if (p < s + size) {
|
|
*p = '\0';
|
|
return p - s;
|
|
} else {
|
|
s[size - 1] = '\0';
|
|
return 0;
|
|
}
|
|
}
|