From 579b597dedf9332ff95cebdaa29b5552b338fdf8 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sat, 14 Aug 2021 06:17:56 -0700 Subject: [PATCH] Refactor out some duplicated code --- ape/etc/bochsrc.dbg | 1279 ---------------- ape/etc/bochsrc.ffs | 1294 ----------------- build/bootstrap/compile.com | Bin 106496 -> 106496 bytes build/bootstrap/zipobj.com | Bin 200200 -> 215209 bytes examples/curl.c | 52 +- libc/calls/addrusage.c | 39 + libc/calls/addtimeval.c | 28 + libc/calls/math.h | 13 + libc/fmt/conv.h | 6 + libc/fmt/fmt.mk | 1 + libc/sock/goodsocket.c | 59 + libc/sock/goodsocket.internal.h | 11 + libc/str/compareslices.c | 28 + libc/str/compareslicescase.c | 28 + libc/str/getzipcfiletimestamps.c | 101 ++ libc/str/istext.c | 32 + libc/str/isutf8.c | 38 + libc/str/slice.h | 22 + libc/str/startswithi.c | 27 + libc/str/str.h | 3 + libc/str/str.mk | 19 +- libc/str/windowstimetotime.c | 24 + libc/zip.h | 3 + libc/zipos/stat-impl.c | 66 +- net/https/certhashost.c | 32 + net/https/certhasip.c | 32 + net/https/chaincertificate.c | 35 + net/https/choosecertificatelifetime.c | 33 + net/https/finishcertificate.c | 46 + net/https/formatx509name.c | 26 + net/https/generatecertificateserial.c | 30 + net/https/getentropy.c | 26 + net/https/https.h | 30 + net/https/https.mk | 2 + net/https/initializekey.c | 35 + net/https/initializerng.c | 30 + net/https/isselfsigned.c | 23 + net/https/isservercert.c | 29 + net/https/logcertificate.c | 33 + net/https/tlsdebug.c | 24 + net/https/tlsdie.c | 28 + net/https/tlserror.c | 26 + test/tool/net/redbean_test.c | 13 +- third_party/getopt/getopt.c | 3 +- third_party/mbedtls/{check.h => check.inc} | 17 - third_party/mbedtls/config.h | 5 +- third_party/mbedtls/ecp.c | 5 +- .../mbedtls/test/test_suite_asn1parse.c | 1 - tool/build/build.mk | 5 +- tool/build/compile.c | 10 +- tool/build/lib/buildlib.mk | 1 + tool/build/lib/eztls.c | 62 +- tool/build/lib/eztls.h | 18 +- tool/build/runit.c | 9 +- tool/build/runitd.c | 10 +- tool/build/zipobj.c | 26 +- tool/net/redbean.c | 394 +---- tool/net/wb.c | 52 +- 58 files changed, 1110 insertions(+), 3214 deletions(-) delete mode 100644 ape/etc/bochsrc.dbg delete mode 100644 ape/etc/bochsrc.ffs create mode 100644 libc/calls/addrusage.c create mode 100644 libc/calls/addtimeval.c create mode 100644 libc/calls/math.h create mode 100644 libc/sock/goodsocket.c create mode 100644 libc/sock/goodsocket.internal.h create mode 100644 libc/str/compareslices.c create mode 100644 libc/str/compareslicescase.c create mode 100644 libc/str/getzipcfiletimestamps.c create mode 100644 libc/str/istext.c create mode 100644 libc/str/isutf8.c create mode 100644 libc/str/slice.h create mode 100644 libc/str/startswithi.c create mode 100644 libc/str/windowstimetotime.c create mode 100644 net/https/certhashost.c create mode 100644 net/https/certhasip.c create mode 100644 net/https/chaincertificate.c create mode 100644 net/https/choosecertificatelifetime.c create mode 100644 net/https/finishcertificate.c create mode 100644 net/https/formatx509name.c create mode 100644 net/https/generatecertificateserial.c create mode 100644 net/https/getentropy.c create mode 100644 net/https/initializekey.c create mode 100644 net/https/initializerng.c create mode 100644 net/https/isselfsigned.c create mode 100644 net/https/isservercert.c create mode 100644 net/https/logcertificate.c create mode 100644 net/https/tlsdebug.c create mode 100644 net/https/tlsdie.c create mode 100644 net/https/tlserror.c rename third_party/mbedtls/{check.h => check.inc} (97%) diff --git a/ape/etc/bochsrc.dbg b/ape/etc/bochsrc.dbg deleted file mode 100644 index 28a8d2715..000000000 --- a/ape/etc/bochsrc.dbg +++ /dev/null @@ -1,1279 +0,0 @@ -# make boot.com -# bochs -q -f .bochsrc.dbg floppya:1_44=win64.exe,status=inserted - -#======================================================================= -# PLUGIN_CTRL: -# Controls the presence of optional device plugins. These plugins are loaded -# directly with this option and some of them install a config option that is -# only available when the plugin device is loaded. The value "1" means to load -# the plugin and "0" will unload it (if loaded before). -# -# These plugins will be loaded by default (if present): 'biosdev', 'extfpuirq', -# 'gameport', 'iodebug','parallel', 'serial', 'speaker' and 'unmapped'. -# -# These plugins are also supported, but they are usually loaded directly with -# their bochsrc option: 'e1000', 'es1370', 'ne2k', 'pcidev', 'pcipnic', 'sb16', -# 'usb_ehci', 'usb_ohci', 'usb_uhci', 'usb_xhci' and 'voodoo'. -#======================================================================= -#plugin_ctrl: unmapped=0, e1000=1 # unload 'unmapped' and load 'e1000' - -#======================================================================= -# CONFIG_INTERFACE -# -# The configuration interface is a series of menus or dialog boxes that -# allows you to change all the settings that control Bochs's behavior. -# Depending on the platform there are up to 3 choices of configuration -# interface: a text mode version called "textconfig" and two graphical versions -# called "win32config" and "wx". The text mode version uses stdin/stdout and -# is always compiled in, unless Bochs is compiled for wx only. The choice -# "win32config" is only available on win32 and it is the default there. -# The choice "wx" is only available when you use "--with-wx" on the configure -# command. If you do not write a config_interface line, Bochs will -# choose a default for you. -# -# NOTE: if you use the "wx" configuration interface, you must also use -# the "wx" display library. -#======================================================================= -#config_interface: textconfig -#config_interface: win32config -#config_interface: wx - -#======================================================================= -# DISPLAY_LIBRARY -# -# The display library is the code that displays the Bochs VGA screen. Bochs -# has a selection of about 10 different display library implementations for -# different platforms. If you run configure with multiple --with-* options, -# the display_library command lets you choose which one you want to run with. -# If you do not write a display_library line, Bochs will choose a default for -# you. -# -# The choices are: -# x use X windows interface, cross platform -# win32 use native win32 libraries -# carbon use Carbon library (for MacOS X) -# macintosh use MacOS pre-10 -# amigaos use native AmigaOS libraries -# sdl use SDL 1.2.x library, cross platform -# sdl2 use SDL 2.x library, cross platform -# svga use SVGALIB library for Linux, allows graphics without X11 -# term text only, uses curses/ncurses library, cross platform -# rfb provides an interface to AT&T's VNC viewer, cross platform -# vncsrv use LibVNCServer for extended RFB(VNC) support -# wx use wxWidgets library, cross platform -# nogui no display at all -# -# NOTE: if you use the "wx" configuration interface, you must also use -# the "wx" display library. -# -# Specific options: -# Some display libraries now support specific options to control their -# behaviour. These options are supported by more than one display library: -# -# "gui_debug" - use GTK debugger gui (sdl, sdl2, x) / Win32 debugger gui (sdl, -# sdl2, win32) -# "hideIPS" - disable IPS output in status bar (rfb, sdl, sdl2, vncsrv, -# win32, wx, x) -# "nokeyrepeat" - turn off host keyboard repeat (sdl, sdl2, win32, x) -# "timeout" - time (in seconds) to wait for client (rfb, vncsrv) -# -# See the examples below for other currently supported options. -#======================================================================= -#display_library: amigaos -#display_library: carbon -#display_library: macintosh -#display_library: nogui -#display_library: rfb -#display_library: sdl, options="fullscreen" # startup in fullscreen mode -#display_library: sdl2, options="fullscreen" # startup in fullscreen mode -#display_library: term -#display_library: vncsrv -# "traphotkeys" - system hotkeys not handled by host OS, but sent to guest -# (win32 in mouse capture and fullscreen mode: alt-tab, win, -# alt-space, alt-esc, ctrl-esc) -#display_library: win32, options="traphotkeys" -#display_library: wx -#display_library: x, gui_debug=1 -display_library: x, options="gui_debug" - -#======================================================================= -# CPU: -# This defines cpu-related parameters inside Bochs: -# -# MODEL: -# Selects CPU configuration to emulate from pre-defined list of all -# supported configurations. When this option is used and the value -# is different from 'bx_generic', the parameters of the CPUID option -# have no effect anymore. -# -# CPU configurations that can be selected: -# ----------------------------------------------------------------- -# pentium Intel Pentium (P54C) -# pentium_mmx Intel Pentium MMX -# amd_k6_2_chomper AMD-K6(tm) 3D processor (Chomper) -# p2_klamath Intel Pentium II (Klamath) -# p3_katmai Intel Pentium III (Katmai) -# p4_willamette Intel(R) Pentium(R) 4 (Willamette) -# core_duo_t2400_yonah Intel(R) Core(TM) Duo CPU T2400 (Yonah) -# atom_n270 Intel(R) Atom(TM) CPU N270 -# p4_prescott_celeron_336 Intel(R) Celeron(R) 336 (Prescott) -# athlon64_clawhammer AMD Athlon(tm) 64 Processor 2800+ (Clawhammer) -# athlon64_venice AMD Athlon(tm) 64 Processor 3000+ (Venice) -# turion64_tyler AMD Turion(tm) 64 X2 Mobile TL-60 (Tyler) -# phenom_8650_toliman AMD Phenom X3 8650 (Toliman) -# core2_penryn_t9600 Intel Mobile Core 2 Duo T9600 (Penryn) -# corei5_lynnfield_750 Intel(R) Core(TM) i5 750 (Lynnfield) -# corei5_arrandale_m520 Intel(R) Core(TM) i5 M 520 (Arrandale) -# corei7_sandy_bridge_2600k Intel(R) Core(TM) i7-2600K (Sandy Bridge) -# zambezi AMD FX(tm)-4100 Quad-Core Processor (Zambezi) -# trinity_apu AMD A8-5600K APU (Trinity) -# ryzen AMD Ryzen 7 1700 -# corei7_ivy_bridge_3770k Intel(R) Core(TM) i7-3770K CPU (Ivy Bridge) -# corei7_haswell_4770 Intel(R) Core(TM) i7-4770 CPU (Haswell) -# broadwell_ult Intel(R) Processor 5Y70 CPU (Broadwell) -# -# COUNT: -# Set the number of processors:cores per processor:threads per core when -# Bochs is compiled for SMP emulation. Bochs currently supports up to -# 14 threads (legacy APIC) or 254 threads (xAPIC or higher) running -# simultaniosly. If Bochs is compiled without SMP support, it won't accept -# values different from 1. -# -# QUANTUM: -# Maximum amount of instructions allowed to execute by processor before -# returning control to another cpu. This option exists only in Bochs -# binary compiled with SMP support. -# -# RESET_ON_TRIPLE_FAULT: -# Reset the CPU when triple fault occur (highly recommended) rather than -# PANIC. Remember that if you trying to continue after triple fault the -# simulation will be completely bogus ! -# -# CPUID_LIMIT_WINNT: -# Determine whether to limit maximum CPUID function to 2. This mode is -# required to workaround WinNT installation and boot issues. -# -# MSRS: -# Define path to user CPU Model Specific Registers (MSRs) specification. -# See example in msrs.def. -# -# IGNORE_BAD_MSRS: -# Ignore MSR references that Bochs does not understand; print a warning -# message instead of generating #GP exception. This option is enabled -# by default but will not be avaiable if configurable MSRs are enabled. -# -# MWAIT_IS_NOP: -# When this option is enabled MWAIT will not put the CPU into a sleep state. -# This option exists only if Bochs compiled with --enable-monitor-mwait. -# -# IPS: -# Emulated Instructions Per Second. This is the number of IPS that bochs -# is capable of running on your machine. You can recompile Bochs with -# --enable-show-ips option enabled, to find your host's capability. -# Measured IPS value will then be logged into your log file or shown -# in the status bar (if supported by the gui). -# -# IPS is used to calibrate many time-dependent events within the bochs -# simulation. For example, changing IPS affects the frequency of VGA -# updates, the duration of time before a key starts to autorepeat, and -# the measurement of BogoMips and other benchmarks. -# -# Examples: -# -# Bochs Machine/Compiler Mips -# ______________________________________________________________________ -# 2.4.6 3.4Ghz Intel Core i7 2600 with Win7x64/g++ 4.5.2 85 to 95 Mips -# 2.3.7 3.2Ghz Intel Core 2 Q9770 with WinXP/g++ 3.4 50 to 55 Mips -# 2.3.7 2.6Ghz Intel Core 2 Duo with WinXP/g++ 3.4 38 to 43 Mips -# 2.2.6 2.6Ghz Intel Core 2 Duo with WinXP/g++ 3.4 21 to 25 Mips -# 2.2.6 2.1Ghz Athlon XP with Linux 2.6/g++ 3.4 12 to 15 Mips -#======================================================================= -#cpu: model=core2_penryn_t9600, count=1, ips=50000000, reset_on_triple_fault=1, ignore_bad_msrs=1, msrs="msrs.def" -#cpu: cpuid_limit_winnt=0 -cpu: model=corei7_haswell_4770 - -#======================================================================= -# CPUID: -# -# This defines features and functionality supported by Bochs emulated CPU. -# The option has no offect if CPU model was selected in CPU option. -# -# MMX: -# Select MMX instruction set support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 5. -# -# APIC: -# Select APIC configuration (LEGACY/XAPIC/XAPIC_EXT/X2APIC). -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 5. -# -# SEP: -# Select SYSENTER/SYSEXIT instruction set support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# SIMD: -# Select SIMD instructions support. -# Any of NONE/SSE/SSE2/SSE3/SSSE3/SSE4_1/SSE4_2/AVX/AVX2/AVX512 -# could be selected. -# -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# The AVX choises exists only if Bochs compiled with --enable-avx option. -# -# SSE4A: -# Select AMD SSE4A instructions support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# MISALIGNED_SSE: -# Select AMD Misaligned SSE mode support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# AES: -# Select AES instruction set support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# SHA: -# Select SHA instruction set support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# MOVBE: -# Select MOVBE Intel(R) Atom instruction support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# ADX: -# Select ADCX/ADOX instructions support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# XSAVE: -# Select XSAVE extensions support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# XSAVEOPT: -# Select XSAVEOPT instruction support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# AVX_F16C: -# Select AVX float16 convert instructions support. -# This option exists only if Bochs compiled with --enable-avx option. -# -# AVX_FMA: -# Select AVX fused multiply add (FMA) instructions support. -# This option exists only if Bochs compiled with --enable-avx option. -# -# BMI: -# Select BMI1/BMI2 instructions support. -# This option exists only if Bochs compiled with --enable-avx option. -# -# XOP: -# Select AMD XOP instructions support. -# This option exists only if Bochs compiled with --enable-avx option. -# -# FMA4: -# Select AMD four operand FMA instructions support. -# This option exists only if Bochs compiled with --enable-avx option. -# -# TBM: -# Select AMD Trailing Bit Manipulation (TBM) instructions support. -# This option exists only if Bochs compiled with --enable-avx option. -# -# X86-64: -# Enable x86-64 and long mode support. -# This option exists only if Bochs compiled with x86-64 support. -# -# 1G_PAGES: -# Enable 1G page size support in long mode. -# This option exists only if Bochs compiled with x86-64 support. -# -# PCID: -# Enable Process-Context Identifiers (PCID) support in long mode. -# This option exists only if Bochs compiled with x86-64 support. -# -# FSGSBASE: -# Enable GS/GS BASE access instructions support in long mode. -# This option exists only if Bochs compiled with x86-64 support. -# -# SMEP: -# Enable Supervisor Mode Execution Protection (SMEP) support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# SMAP: -# Enable Supervisor Mode Access Prevention (SMAP) support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# MWAIT: -# Select MONITOR/MWAIT instructions support. -# This option exists only if Bochs compiled with --enable-monitor-mwait. -# -# VMX: -# Select VMX extensions emulation support. -# This option exists only if Bochs compiled with --enable-vmx option. -# -# SVM: -# Select AMD SVM (Secure Virtual Machine) extensions emulation support. -# This option exists only if Bochs compiled with --enable-svm option. -# -# VENDOR_STRING: -# Set the CPUID vendor string returned by CPUID(0x0). This should be a -# twelve-character ASCII string. -# -# BRAND_STRING: -# Set the CPUID vendor string returned by CPUID(0x80000002 .. 0x80000004). -# This should be at most a forty-eight-character ASCII string. -# -# LEVEL: -# Set emulated CPU level information returned by CPUID. Default value is -# determined by configure option --enable-cpu-level. Currently supported -# values are 5 (for Pentium and similar processors) and 6 (for P6 and -# later processors). -# -# FAMILY: -# Set model information returned by CPUID. Default family value determined -# by configure option --enable-cpu-level. -# -# MODEL: -# Set model information returned by CPUID. Default model value is 3. -# -# STEPPING: -# Set stepping information returned by CPUID. Default stepping value is 3. -#======================================================================= -#cpuid: x86_64=1, mmx=1, sep=1, simd=sse4_2, apic=xapic, aes=1, movbe=1, xsave=1 -#cpuid: family=6, model=0x1a, stepping=5 - -#======================================================================= -# MEMORY -# Set the amount of physical memory you want to emulate. -# -# GUEST: -# Set amount of guest physical memory to emulate. The default is 32MB, -# the maximum amount limited only by physical address space limitations. -# -# HOST: -# Set amount of host memory you want to allocate for guest RAM emulation. -# It is possible to allocate less memory than you want to emulate in guest -# system. This will fake guest to see the non-existing memory. Once guest -# system touches new memory block it will be dynamically taken from the -# memory pool. You will be warned (by FATAL PANIC) in case guest already -# used all allocated host memory and wants more. -# -#======================================================================= -#memory: guest=512, host=256 - -#======================================================================= -# ROMIMAGE: -# The ROM BIOS controls what the PC does when it first powers on. -# Normally, you can use a precompiled BIOS in the source or binary -# distribution called BIOS-bochs-latest. The default ROM BIOS is usually loaded -# starting at address 0xfffe0000, and it is exactly 128k long. The legacy -# version of the Bochs BIOS is usually loaded starting at address 0xffff0000, -# and it is exactly 64k long. -# You can use the environment variable $BXSHARE to specify the location -# of the BIOS. -# The usage of external large BIOS images (up to 512k) at memory top is -# now supported, but we still recommend to use the BIOS distributed with Bochs. -# The start address is optional, since it can be calculated from image size. -# The Bochs BIOS currently supports only the option "fastboot" to skip the -# boot menu delay. -#======================================================================= -romimage: file=$BXSHARE/BIOS-bochs-latest, options=fastboot -#romimage: file=$BXSHARE/bios.bin-1.7.5 # http://www.seabios.org/SeaBIOS -#romimage: file=mybios.bin, address=0xfff80000 # 512k at memory top -#romimage: file=BIOS-bochs-latest, options=fastboot - -#======================================================================= -# VGAROMIMAGE -# You now need to load a VGA ROM BIOS into C0000. -#======================================================================= -#vgaromimage: file=$BXSHARE/VGABIOS-elpin-2.40 -vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latest -#vgaromimage: file=bios/VGABIOS-lgpl-latest-cirrus - -#======================================================================= -# OPTROMIMAGE[1-4]: -# You may now load up to 4 optional ROM images. Be sure to use a -# read-only area, typically between C8000 and EFFFF. These optional -# ROM images should not overwrite the rombios (located at -# F0000-FFFFF) and the videobios (located at C0000-C7FFF). -# Those ROM images will be initialized by the bios if they contain -# the right signature (0x55AA) and a valid checksum. -# It can also be a convenient way to upload some arbitrary code/data -# in the simulation, that can be retrieved by the boot loader -#======================================================================= -#optromimage1: file=optionalrom.bin, address=0xd0000 -#optromimage2: file=optionalrom.bin, address=0xd1000 -#optromimage3: file=optionalrom.bin, address=0xd2000 -#optromimage4: file=optionalrom.bin, address=0xd3000 - -#optramimage1: file=/path/file1.img, address=0x0010000 -#optramimage2: file=/path/file2.img, address=0x0020000 -#optramimage3: file=/path/file3.img, address=0x0030000 -#optramimage4: file=/path/file4.img, address=0x0040000 - -#======================================================================= -# VGA: -# This defines parameters related to the VGA display -# -# EXTENSION -# Here you can specify the display extension to be used. With the value -# 'none' you can use standard VGA with no extension. Other supported -# values are 'vbe' for Bochs VBE, 'cirrus' for Cirrus SVGA support and -# 'voodoo' for Voodoo Graphics support (see 'voodoo' option). -# -# UPDATE_FREQ -# This parameter specifies the number of display updates per second. -# The VGA update timer by default uses the realtime engine with a value -# of 5. This parameter can be changed at runtime. -# -# REALTIME -# If set to 1 (default), the VGA timer is based on realtime, otherwise it -# is driven by the cpu and depends on the ips setting. If the host is slow -# (low ips, update_freq) and the guest uses HLT appropriately, setting this -# to 0 and "clock: sync=none" may improve the responsiveness of the guest -# GUI when the guest is otherwise idle. -# -# Examples: -# vga: extension=cirrus, update_freq=10 -#======================================================================= -vga: extension=vbe, update_freq=5, realtime=1 - -#======================================================================= -# VOODOO: -# This defines the Voodoo Graphics emulation (experimental). Currently -# supported models are 'voodoo1', 'voodoo2', 'banshee' and 'voodoo3'. The -# Voodoo2 support is not yet complete, but almost usable. The Banshee and -# Voodoo3 support is under construction, but basicly usable. The 2D/3D cards -# require an external VGA BIOS the vga extension option to be set to 'voodoo'. -# If the i440BX PCI chipset is selected, they can be assigned to AGP (slot #5). -# The gui screen update timing for all models is controlled by the related 'vga' -# options. -# -# Examples: -# voodoo: enabled=1, model=voodoo2 -#======================================================================= -#voodoo: enabled=1, model=voodoo1 - -#======================================================================= -# KEYBOARD: -# This defines parameters related to the emulated keyboard -# -# TYPE: -# Type of keyboard return by a "identify keyboard" command to the -# keyboard controller. It must be one of "xt", "at" or "mf". -# Defaults to "mf". It should be ok for almost everybody. A known -# exception is french macs, that do have a "at"-like keyboard. -# -# SERIAL_DELAY: -# Approximate time in microseconds that it takes one character to -# be transferred from the keyboard to controller over the serial path. -# -# PASTE_DELAY: -# Approximate time in microseconds between attempts to paste -# characters to the keyboard controller. This leaves time for the -# guest os to deal with the flow of characters. The ideal setting -# depends on how your operating system processes characters. The -# default of 100000 usec (.1 seconds) was chosen because it works -# consistently in Windows. -# If your OS is losing characters during a paste, increase the paste -# delay until it stops losing characters. -# -# KEYMAP: -# This enables a remap of a physical localized keyboard to a -# virtualized us keyboard, as the PC architecture expects. -# -# USER_SHORTCUT: -# This defines the keyboard shortcut to be sent when you press the "user" -# button in the headerbar. The shortcut string is a combination of maximum -# 3 key names (listed below) separated with a '-' character. -# Valid key names: -# "alt", "bksl", "bksp", "ctrl", "del", "down", "end", "enter", "esc", -# "f1", ... "f12", "home", "ins", "left", "menu", "minus", "pgdwn", "pgup", -# "plus", "power", "print", "right", "scrlck", "shift", "space", "tab", "up" -# and "win". - -# Examples: -# keyboard: type=mf, serial_delay=200, paste_delay=100000 -# keyboard: keymap=gui/keymaps/x11-pc-de.map -# keyboard: user_shortcut=ctrl-alt-del -#======================================================================= -keyboard: type=mf, serial_delay=250 - -#======================================================================= -# MOUSE: -# This defines parameters for the emulated mouse type, the initial status -# of the mouse capture and the runtime method to toggle it. -# -# TYPE: -# With the mouse type option you can select the type of mouse to emulate. -# The default value is 'ps2'. The other choices are 'imps2' (wheel mouse -# on PS/2), 'serial', 'serial_wheel', 'serial_msys' (one com port requires -# setting 'mode=mouse') 'inport' and 'bus' (if present). To connect a mouse -# to a USB port, see the 'usb_uhci', 'usb_ohci', 'usb_ehci' or 'usb_xhci' -# options (requires PCI and USB support). -# -# ENABLED: -# The Bochs gui creates mouse "events" unless the 'enabled' option is -# set to 0. The hardware emulation itself is not disabled by this. -# Unless you have a particular reason for enabling the mouse by default, -# it is recommended that you leave it off. You can also toggle the mouse -# usage at runtime (RFB, SDL, Win32, wxWidgets and X11 - see below). -# -# TOGGLE: -# The default method to toggle the mouse capture at runtime is to press the -# CTRL key and the middle mouse button ('ctrl+mbutton'). This option allows -# to change the method to 'ctrl+f10' (like DOSBox), 'ctrl+alt' (like QEMU) -# or 'f12'. -# -# Examples: -# mouse: enabled=1 -# mouse: type=imps2, enabled=1 -# mouse: type=serial, enabled=1 -# mouse: enabled=0, toggle=ctrl+f10 -#======================================================================= -mouse: enabled=1 - -#======================================================================= -# PCI: -# This option controls the presence of a PCI chipset in Bochs. Currently it -# supports the i430FX, i440FX and i440BX chipsets. You can also specify the -# devices connected to PCI slots. Up to 5 slots are available. For these -# combined PCI/ISA devices assigning to slot is mandatory if you want to emulate -# the PCI model: cirrus, ne2k and pcivga. These PCI-only devices are also -# supported, but they are auto-assigned if you don't use the slot configuration: -# e1000, es1370, pcidev, pcipnic, usb_ehci, usb_ohci, usb_xhci and voodoo. -# In case of the i440BX chipset, slot #5 is the AGP slot. Currently only the -# 'voodoo' device can be assigned to AGP. -# -# Example: -# pci: enabled=1, chipset=i440fx, slot1=pcivga, slot2=ne2k -#======================================================================= -pci: enabled=1, chipset=i440fx - -#======================================================================= -# CLOCK: -# This defines the parameters of the clock inside Bochs: -# -# SYNC: -# This defines the method how to synchronize the Bochs internal time -# with realtime. With the value 'none' the Bochs time relies on the IPS -# value and no host time synchronization is used. The 'slowdown' method -# sacrifices performance to preserve reproducibility while allowing host -# time correlation. The 'realtime' method sacrifices reproducibility to -# preserve performance and host-time correlation. -# It is possible to enable both synchronization methods. -# -# RTC_SYNC: -# If this option is enabled together with the realtime synchronization, -# the RTC runs at realtime speed. This feature is disabled by default. -# -# TIME0: -# Specifies the start (boot) time of the virtual machine. Use a time -# value as returned by the time(2) system call or a string as returned -# by the ctime(3) system call. If no time0 value is set or if time0 -# equal to 1 (special case) or if time0 equal 'local', the simulation -# will be started at the current local host time. If time0 equal to 2 -# (special case) or if time0 equal 'utc', the simulation will be started -# at the current utc time. -# -# Syntax: -# clock: sync=[none|slowdown|realtime|both], time0=[timeValue|local|utc] -# -# Example: -# clock: sync=none, time0=local # Now (localtime) -# clock: sync=slowdown, time0=315529200 # Tue Jan 1 00:00:00 1980 -# clock: sync=none, time0="Mon Jan 1 00:00:00 1990" # 631148400 -# clock: sync=realtime, time0=938581955 # Wed Sep 29 07:12:35 1999 -# clock: sync=realtime, time0="Sat Jan 1 00:00:00 2000" # 946681200 -# clock: sync=none, time0=1 # Now (localtime) -# clock: sync=none, time0=utc # Now (utc/gmt) -# -# Default value are sync=none, rtc_sync=0, time0=local -#======================================================================= -clock: sync=slowdown, time0=315529200 - -#======================================================================= -# CMOSIMAGE: -# This defines a binary image file with size 128 bytes that can be loaded into -# the CMOS RAM at startup. The rtc_init parameter controls whether initialize -# the RTC with values stored in the image. By default the time0 argument given -# to the clock option is used. With 'rtc_init=image' the image is the source -# for the initial time. -# -# Example: -# cmosimage: file=cmos.img, rtc_init=image -#======================================================================= -#cmosimage: file=cmos.img, rtc_init=time0 - -#======================================================================= -# private_colormap: Request that the GUI create and use it's own -# non-shared colormap. This colormap will be used -# when in the bochs window. If not enabled, a -# shared colormap scheme may be used. Not implemented -# on all GUI's. -# -# Examples: -# private_colormap: enabled=1 -# private_colormap: enabled=0 -#======================================================================= -private_colormap: enabled=0 - -#======================================================================= -# FLOPPYA: -# Point this to pathname of floppy image file or device -# This should be of a bootable floppy(image/device) if you're -# booting from 'a' (or 'floppy'). -# -# You can set the initial status of the media to 'ejected' or 'inserted'. -# floppya: 2_88=path, status=ejected (2.88M 3.5" media) -# floppya: 1_44=path, status=inserted (1.44M 3.5" media) -# floppya: 1_2=path, status=ejected (1.2M 5.25" media) -# floppya: 720k=path, status=inserted (720K 3.5" media) -# floppya: 360k=path, status=inserted (360K 5.25" media) -# floppya: 320k=path, status=inserted (320K 5.25" media) -# floppya: 180k=path, status=inserted (180K 5.25" media) -# floppya: 160k=path, status=inserted (160K 5.25" media) -# floppya: image=path, status=inserted (guess media type from image size) -# floppya: 1_44=vvfat:path, status=inserted (use directory as VFAT media) -# floppya: type=1_44 (1.44M 3.5" floppy drive, no media) -# -# The path should be the name of a disk image file. On Unix, you can use a raw -# device name such as /dev/fd0 on Linux. On win32 platforms, use drive letters -# such as a: or b: as the path. The parameter 'image' works with image files -# only. In that case the size must match one of the supported types. -# The parameter 'type' can be used to enable the floppy drive without media -# and status specified. Usually the drive type is set up based on the media type. -# The optional parameter 'write_protected' can be used to control the media -# write protect switch. By default it is turned off. -#======================================================================= -#floppya: 1_44=, status=inserted -#floppya: image=../1.44, status=inserted -#floppya: 1_44=/dev/fd0H1440, status=inserted -#floppya: 1_2=../1_2, status=inserted -#floppya: 1_44=a:, status=inserted -#floppya: 1_44=a.img, status=inserted, write_protected=1 -#floppya: 1_44=/dev/rfd0a, status=inserted -#floppya: 1_44=boot.img, status=inserted, write_protected=1 - -#======================================================================= -# FLOPPYB: -# See FLOPPYA above for syntax -#======================================================================= -#floppyb: 1_44=b:, status=inserted -#floppyb: 1_44=b.img, status=inserted - -#======================================================================= -# ATA0, ATA1, ATA2, ATA3 -# ATA controller for hard disks and cdroms -# -# ata[0-3]: enabled=[0|1], ioaddr1=addr, ioaddr2=addr, irq=number -# -# These options enables up to 4 ata channels. For each channel -# the two base io addresses and the irq must be specified. -# -# ata0 and ata1 are enabled by default with the values shown below -# -# Examples: -# ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14 -# ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15 -# ata2: enabled=1, ioaddr1=0x1e8, ioaddr2=0x3e0, irq=11 -# ata3: enabled=1, ioaddr1=0x168, ioaddr2=0x360, irq=9 -#======================================================================= -ata0: enabled=0, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14 -ata1: enabled=0, ioaddr1=0x170, ioaddr2=0x370, irq=15 -ata2: enabled=0, ioaddr1=0x1e8, ioaddr2=0x3e0, irq=11 -ata3: enabled=0, ioaddr1=0x168, ioaddr2=0x360, irq=9 - -#======================================================================= -# ATA[0-3]-MASTER, ATA[0-3]-SLAVE -# -# This defines the type and characteristics of all attached ata devices: -# type= type of attached device [disk|cdrom] -# mode= only valid for disks [flat|concat|external|dll|sparse|vmware3] -# [vmware4|undoable|growing|volatile|vpc] -# [vbox|vvfat] -# path= path of the image / directory -# cylinders= only valid for disks -# heads= only valid for disks -# spt= only valid for disks -# status= only valid for cdroms [inserted|ejected] -# biosdetect= type of biosdetection [auto|cmos|none] -# translation=type of translation of the bios, only for disks [none|lba|large|rechs|auto] -# model= string returned by identify device command -# journal= optional filename of the redolog for undoable, volatile and vvfat disks -# -# Point this at a hard disk image file, cdrom iso file, or physical cdrom -# device. To create a hard disk image, try running bximage. It will help you -# choose the size and then suggest a line that works with it. -# -# In UNIX it may be possible to use a raw device as a Bochs hard disk, -# but WE DON'T RECOMMEND IT. In Windows there is no easy way. -# -# In windows, the drive letter + colon notation should be used for cdroms. -# Depending on versions of windows and drivers, you may only be able to -# access the "first" cdrom in the system. On MacOSX, use path="drive" -# to access the physical drive. -# -# The path is mandatory for hard disks. Disk geometry autodetection works with -# images created by bximage if CHS is set to 0/0/0 (cylinders are calculated -# using heads=16 and spt=63). For other hard disk images and modes the -# cylinders, heads, and spt are mandatory. In all cases the disk size reported -# from the image must be exactly C*H*S*512. -# -# Default values are: -# mode=flat, biosdetect=auto, translation=auto, model="Generic 1234" -# -# The biosdetect option has currently no effect on the bios -# -# Examples: -# ata0-master: type=disk, mode=flat, path=10M.sample, cylinders=306, heads=4, spt=17 -# ata0-slave: type=disk, mode=flat, path=20M.sample, cylinders=615, heads=4, spt=17 -# ata1-master: type=disk, mode=flat, path=30M.sample, cylinders=615, heads=6, spt=17 -# ata1-slave: type=disk, mode=flat, path=46M.sample, cylinders=940, heads=6, spt=17 -# ata2-master: type=disk, mode=flat, path=62M.sample, cylinders=940, heads=8, spt=17 -# ata2-slave: type=disk, mode=flat, path=112M.sample, cylinders=900, heads=15, spt=17 -# ata3-master: type=disk, mode=flat, path=483M.sample, cylinders=1024, heads=15, spt=63 -# ata3-slave: type=cdrom, path=iso.sample, status=inserted -#======================================================================= -#ata0-master: type=disk, mode=flat, path="30M.sample" -#ata0-master: type=disk, mode=flat, path="30M.sample", cylinders=615, heads=6, spt=17 -#ata0-master: type=disk, mode=flat, path="c.img", cylinders=0 # autodetect -#ata0-slave: type=disk, mode=vvfat, path=/bochs/images/vvfat, journal=vvfat.redolog -#ata0-slave: type=cdrom, path=D:, status=inserted -#ata0-slave: type=cdrom, path=/dev/cdrom, status=inserted -#ata0-slave: type=cdrom, path="drive", status=inserted -#ata0-slave: type=cdrom, path=/dev/rcd0d, status=inserted - -#======================================================================= -# BOOT: -# This defines the boot sequence. Now you can specify up to 3 boot drives, -# which can be 'floppy', 'disk', 'cdrom' or 'network' (boot ROM). -# Legacy 'a' and 'c' are also supported. -# Examples: -# boot: floppy -# boot: cdrom, disk -# boot: network, disk -# boot: cdrom, floppy, disk -#======================================================================= -boot: floppy -#boot: disk - -#======================================================================= -# FLOPPY_BOOTSIG_CHECK: disabled=[0|1] -# Enables or disables the 0xaa55 signature check on boot floppies -# Defaults to disabled=0 -# Examples: -# floppy_bootsig_check: disabled=0 -# floppy_bootsig_check: disabled=1 -#======================================================================= -floppy_bootsig_check: disabled=0 - -#======================================================================= -# LOG: -# Give the path of the log file you'd like Bochs debug and misc. verbiage -# to be written to. If you don't use this option or set the filename to -# '-' the output is written to the console. If you really don't want it, -# make it "/dev/null" (Unix) or "nul" (win32). :^( -# -# Examples: -# log: ./bochs.out -# log: /dev/tty -#======================================================================= -#log: /dev/null -log: .bochs.log - -#======================================================================= -# LOGPREFIX: -# This handles the format of the string prepended to each log line. -# You may use those special tokens : -# %t : 11 decimal digits timer tick -# %i : 8 hexadecimal digits of cpu current eip (ignored in SMP configuration) -# %e : 1 character event type ('i'nfo, 'd'ebug, 'p'anic, 'e'rror) -# %d : 5 characters string of the device, between brackets -# -# Default : %t%e%d -# Examples: -# logprefix: %t-%e-@%i-%d -# logprefix: %i%e%d -#======================================================================= -logprefix: %t%e%d - -#======================================================================= -# LOG CONTROLS -# -# Bochs has four severity levels for event logging. -# panic: cannot proceed. If you choose to continue after a panic, -# don't be surprised if you get strange behavior or crashes. -# error: something went wrong, but it is probably safe to continue the -# simulation. -# info: interesting or useful messages. -# debug: messages useful only when debugging the code. This may -# spit out thousands per second. -# -# For events of each level, you can choose to exit Bochs ('fatal'), 'ask', -# 'warn', 'report' or 'ignore'. The choices 'ask' and 'warn' are not supported -# by all guis, since they should bring up a dialog box. The 'warn' dialog is -# designed to confirm errors and the 'ask' dialog is usually used for panics -# and asks the user how to proceed. -# -# It is also possible to specify the 'action' to do for each Bochs facility -# separately (e.g. crash on panics from everything except the cdrom, and only -# report those). See the 'log function' module list in the user documentation. -# -# If you are experiencing many panics, it can be helpful to change -# the panic action to report instead of fatal. However, be aware -# that anything executed after a panic is uncharted territory and can -# cause bochs to become unstable. The panic is a "graceful exit," so -# if you disable it you may get a spectacular disaster instead. -#======================================================================= -panic: action=ask -error: action=report -info: action=report -debug: action=ignore #, pci=report # report BX_DEBUG from module 'pci' - -#======================================================================= -# DEBUGGER_LOG: -# Give the path of the log file you'd like Bochs to log debugger output. -# If you really don't want it, make it /dev/null or '-'. :^( -# -# Examples: -# debugger_log: ./debugger.out -#======================================================================= -#debugger_log: /dev/null -#debugger_log: debugger.out -debugger_log: - - -#======================================================================= -# COM1, COM2, COM3, COM4: -# This defines a serial port (UART type 16550A). In the 'term' mode you can -# specify a device to use as com1. This can be a real serial line, or a pty. -# To use a pty (under X/Unix), create two windows (xterms, usually). One of -# them will run bochs, and the other will act as com1. Find out the tty the com1 -# window using the `tty' command, and use that as the `dev' parameter. -# Then do `sleep 1000000' in the com1 window to keep the shell from -# messing with things, and run bochs in the other window. Serial I/O to -# com1 (port 0x3f8) will all go to the other window. -# In socket* and pipe* (win32 only) modes Bochs becomes either socket/named pipe -# client or server. In client mode it connects to an already running server (if -# connection fails Bochs treats com port as not connected). In server mode it -# opens socket/named pipe and waits until a client application connects to it -# before starting simulation. This mode is useful for remote debugging (e.g. -# with gdb's "target remote host:port" command or windbg's command line option -# -k com:pipe,port=\\.\pipe\pipename). Socket modes use simple TCP communication, -# pipe modes use duplex byte mode pipes. -# Other serial modes are 'null' (no input/output), 'file' (output to a file -# specified as the 'dev' parameter and changeable at runtime), 'raw' (use the -# real serial port - partly implemented on win32), 'mouse' (standard serial -# mouse - requires mouse option setting 'type=serial', 'type=serial_wheel' or -# 'type=serial_msys'). -# -# Examples: -# com1: enabled=1, mode=null -# com1: enabled=1, mode=mouse -# com2: enabled=1, mode=file, dev=serial.out -# com3: enabled=1, mode=raw, dev=com1 -# com3: enabled=1, mode=socket-client, dev=localhost:8888 -# com3: enabled=1, mode=socket-server, dev=localhost:8888 -# com4: enabled=1, mode=pipe-client, dev=\\.\pipe\mypipe -# com4: enabled=1, mode=pipe-server, dev=\\.\pipe\mypipe -#======================================================================= -com1: enabled=1, mode=file, dev=/dev/stdout - -#======================================================================= -# PARPORT1, PARPORT2: -# This defines a parallel (printer) port. When turned on and an output file is -# defined the emulated printer port sends characters printed by the guest OS -# into the output file. On some platforms a device filename can be used to -# send the data to the real parallel port (e.g. "/dev/lp0" on Linux, "lpt1" on -# win32 platforms). The output file can be changed at runtime. -# -# Examples: -# parport1: enabled=1, file="parport.out" -# parport2: enabled=1, file="/dev/lp0" -# parport1: enabled=0 -#======================================================================= -#parport1: enabled=1, file="parport.out" - -#======================================================================= -# SOUND: -# This defines the lowlevel sound driver(s) for the wave (PCM) input / output -# and the MIDI output feature and (if necessary) the devices to be used. -# It can have several of the following properties. -# All properties are in the format sound: property=value -# -# waveoutdrv: -# This defines the driver to be used for the waveout feature. -# Possible values are 'file' (all wave data sent to file), 'dummy' (no -# output) and the platform-dependant drivers 'alsa', 'oss', 'osx', 'sdl' -# and 'win'. -# waveout: -# This defines the device to be used for wave output (if necessary) or -# the output file for the 'file' driver. -# waveindrv: -# This defines the driver to be used for the wavein feature. -# Possible values are 'dummy' (recording silence) and platform-dependent -# drivers 'alsa', 'oss', 'sdl' and 'win'. -# wavein: -# This defines the device to be used for wave input (if necessary). -# midioutdrv: -# This defines the driver to be used for the MIDI output feature. -# Possible values are 'file' (all MIDI data sent to file), 'dummy' (no -# output) and platform-dependent drivers 'alsa', 'oss', 'osx' and 'win'. -# midiout: -# This defines the device to be used for MIDI output (if necessary). -# driver: -# This defines the driver to be used for all sound features with one -# property. Possible values are 'default' (platform default) and all -# other choices described above. Overriding one or more settings with -# the specific driver parameter is possible. -# -# Example for different drivers: -# sound: waveoutdrv=sdl, waveindrv=alsa, midioutdrv=dummy -#======================================================================= -#sound: driver=default, waveout=/dev/dsp. wavein=, midiout= - -#======================================================================= -# SPEAKER: -# This defines the PC speaker output mode. In the 'sound' mode the beep -# is generated by the square wave generator which is a part of the -# lowlevel sound support. The 'system' mode is only available on Linux -# and Windows. On Linux /dev/console is used for output and on Windows -# the Beep() function. The 'gui' mode forwards the beep to the related -# gui methods (currently only used by the Carbon gui). -#======================================================================= -#speaker: enabled=0, mode=sound - -#======================================================================= -# SB16: -# This defines the SB16 sound emulation. It can have several of the -# following properties. -# All properties are in the format sb16: property=value -# -# enabled: -# This optional property controls the presence of the SB16 emulation. -# The emulation is turned on unless this property is used and set to 0. -# midimode: This parameter specifies what to do with the MIDI output. -# 0 = no output -# 1 = output to device specified with the sound option (system dependent) -# 2 = MIDI or raw data output to file (depends on file name extension) -# 3 = dual output (mode 1 and 2 at the same time) -# midifile: This is the file where the midi output is stored (midimode 2 or 3). -# wavemode: This parameter specifies what to do with the PCM output. -# 0 = no output -# 1 = output to device specified with the sound option (system dependent) -# 2 = VOC, WAV or raw data output to file (depends on file name extension) -# 3 = dual output (mode 1 and 2 at the same time) -# wavefile: This is the file where the wave output is stored (wavemode 2 or 3). -# loglevel: -# 0=no log -# 1=resource changes, midi program and bank changes -# 2=severe errors -# 3=all errors -# 4=all errors plus all port accesses -# 5=all errors and port accesses plus a lot of extra info -# log: The file to write the sb16 emulator messages to. -# dmatimer: -# microseconds per second for a DMA cycle. Make it smaller to fix -# non-continuous sound. 750000 is usually a good value. This needs a -# reasonably correct setting for the IPS parameter of the CPU option. -# -# Examples for output modes: -# sb16: midimode=2, midifile="output.mid", wavemode=1 # MIDI to file -# sb16: midimode=1, wavemode=3, wavefile="output.wav" # wave to file and device -#======================================================================= -#sb16: midimode=1, wavemode=1, loglevel=2, log=sb16.log, dmatimer=600000 - -#======================================================================= -# ES1370: -# This defines the ES1370 sound emulation (recording and playback - except -# DAC1+DAC2 output at the same time). The parameter 'enabled' controls the -# presence of the device. The wave and MIDI output can be sent to device, file -# or both using the parameters 'wavemode', 'wavefile', 'midimode' and -# 'midifile'. See the description of these parameters at the SB16 directive. -# -# Examples: -# es1370: enabled=1, wavemode=1 # use 'sound' parameters -# es1370: enabled=1, wavemode=2, wavefile=output.voc # send output to file -#======================================================================= -#es1370: enabled=1, wavemode=1 - -#======================================================================= -# ne2k: NE2000 compatible ethernet adapter -# -# Format: -# ne2k: enabled=1, ioaddr=IOADDR, irq=IRQ, mac=MACADDR, ethmod=MODULE, -# ethdev=DEVICE, script=SCRIPT, bootrom=BOOTROM -# -# IOADDR, IRQ: You probably won't need to change ioaddr and irq, unless there -# are IRQ conflicts. These arguments are ignored when assign the ne2k to a -# PCI slot. -# -# MAC: The MAC address MUST NOT match the address of any machine on the net. -# Also, the first byte must be an even number (bit 0 set means a multicast -# address), and you cannot use ff:ff:ff:ff:ff:ff because that's the broadcast -# address. For the ethertap module, you must use fe:fd:00:00:00:01. There may -# be other restrictions too. To be safe, just use the b0:c4... address. -# -# ETHDEV: The ethdev value is the name of the network interface on your host -# platform. On UNIX machines, you can get the name by running ifconfig. On -# Windows machines, you must run niclist to get the name of the ethdev. -# Niclist source code is in misc/niclist.c and it is included in Windows -# binary releases. -# The 'socket' module uses this parameter to specify the UDP port for -# receiving packets and (optional) the host to connect. -# -# SCRIPT: The script value is optional, and is the name of a script that -# is executed after bochs initialize the network interface. You can use -# this script to configure this network interface, or enable masquerading. -# This is mainly useful for the tun/tap devices that only exist during -# Bochs execution. The network interface name is supplied to the script -# as first parameter. -# The 'slirp' module uses this parameter to specify a config file for -# setting up an alternative IP configuration or additional features. -# The 'vnet' module uses this parameter to specify an alternative -# log file name. -# -# BOOTROM: The bootrom value is optional, and is the name of the ROM image -# to load. Note that this feature is only implemented for the PCI version of -# the NE2000. -# -# If you don't want to make connections to any physical networks, -# you can use the following 'ethmod's to simulate a virtual network. -# null: All packets are discarded, but logged to a few files. -# vde: Virtual Distributed Ethernet -# vnet: ARP, ICMP-echo(ping), DHCP and read/write TFTP are simulated. -# The virtual host uses 192.168.10.1. -# DHCP assigns 192.168.10.2 to the guest. -# TFTP uses the 'ethdev' value for the root directory and doesn't -# overwrite files. -# socket: Connect up to 6 Bochs instances with external program 'bxhub' -# (simulating an ethernet hub). It provides the same services as the -# 'vnet' module and assigns IP addresses like 'slirp' (10.0.2.x). -# -#======================================================================= -# ne2k: ioaddr=0x300, irq=9, mac=fe:fd:00:00:00:01, ethmod=fbsd, ethdev=en0 #macosx -# ne2k: ioaddr=0x300, irq=9, mac=b0:c4:20:00:00:00, ethmod=fbsd, ethdev=xl0 -# ne2k: ioaddr=0x300, irq=9, mac=b0:c4:20:00:00:00, ethmod=linux, ethdev=eth0 -# ne2k: ioaddr=0x300, irq=9, mac=b0:c4:20:00:00:01, ethmod=win32, ethdev=MYCARD -# ne2k: ioaddr=0x300, irq=9, mac=fe:fd:00:00:00:01, ethmod=tap, ethdev=tap0 -# ne2k: ioaddr=0x300, irq=9, mac=fe:fd:00:00:00:01, ethmod=tuntap, ethdev=/dev/net/tun0, script=./tunconfig -# ne2k: ioaddr=0x300, irq=9, mac=b0:c4:20:00:00:01, ethmod=null, ethdev=eth0 -# ne2k: ioaddr=0x300, irq=9, mac=b0:c4:20:00:00:01, ethmod=vde, ethdev="/tmp/vde.ctl" -# ne2k: ioaddr=0x300, irq=9, mac=b0:c4:20:00:00:01, ethmod=vnet, ethdev="c:/temp" -# ne2k: mac=b0:c4:20:00:00:01, ethmod=socket, ethdev=40000 # use localhost -# ne2k: mac=b0:c4:20:00:00:01, ethmod=socket, ethdev=mymachine:40000 -# ne2k: mac=b0:c4:20:00:00:01, ethmod=slirp, script=slirp.conf, bootrom=ne2k_pci.rom - -#======================================================================= -# pcipnic: Bochs/Etherboot pseudo-NIC -# -# Format: -# pcipnic: enabled=1, mac=MACADDR, ethmod=MODULE, ethdev=DEVICE, script=SCRIPT, -# bootrom=BOOTROM -# -# The pseudo-NIC accepts the same syntax (for mac, ethmod, ethdev, script, -# bootrom) and supports the same networking modules as the NE2000 adapter. -#======================================================================= -#pcipnic: enabled=1, mac=b0:c4:20:00:00:00, ethmod=vnet - -#======================================================================= -# e1000: Intel(R) 82540EM Gigabit Ethernet adapter -# -# Format: -# e1000: enabled=1, mac=MACADDR, ethmod=MODULE, ethdev=DEVICE, script=SCRIPT -# bootrom=BOOTROM -# -# The E1000 accepts the same syntax (for mac, ethmod, ethdev, script, bootrom) -# and supports the same networking modules as the NE2000 adapter. -#======================================================================= -#e1000: enabled=1, mac=52:54:00:12:34:56, ethmod=slirp, script=slirp.conf - -#======================================================================= -# USB_UHCI: -# This option controls the presence of the USB root hub which is a part -# of the i440FX PCI chipset. With the portX parameter you can connect devices -# to the hub (currently supported: 'mouse', 'tablet', 'keypad', 'disk', 'cdrom', -# 'floppy', 'hub' and 'printer'). -# -# If you connect the mouse or tablet to one of the ports, Bochs forwards the -# mouse movement data to the USB device instead of the selected mouse type. -# When connecting the keypad to one of the ports, Bochs forwards the input of -# the numeric keypad to the USB device instead of the PS/2 keyboard. -# -# To connect a 'flat' mode image as a USB hardisk you can use the 'disk' device -# with the path to the image separated with a colon. To use other disk image modes -# similar to ATA disks the syntax 'disk:mode:filename' must be used (see below). -# -# To emulate a USB cdrom you can use the 'cdrom' device name and the path to -# an ISO image or raw device name also separated with a colon. An option to -# insert/eject media is available in the runtime configuration. -# -# To emulate a USB floppy you can use the 'floppy' device with the path to the -# image separated with a colon. To use the VVFAT image mode similar to the -# legacy floppy the syntax 'floppy:vvfat:directory' must be used (see below). -# An option to insert/eject media is available in the runtime configuration. -# -# The device name 'hub' connects an external hub with max. 8 ports (default: 4) -# to the root hub. To specify the number of ports you have to add the value -# separated with a colon. Connecting devices to the external hub ports is only -# available in the runtime configuration. -# -# The device 'printer' emulates the HP Deskjet 920C printer. The PCL data is -# sent to a file specified in bochsrc.txt. The current code appends the PCL -# code to the file if the file already existed. The output file can be -# changed at runtime. -# -# The optionsX parameter can be used to assign specific options to the device -# connected to the corresponding USB port. Currently this feature is used to -# set the speed reported by device ('low', 'full', 'high' or 'super'). The -# availabe speed choices depend on both HC and device. The option 'debug' turns -# on debug output for the device at connection time. -# For the USB 'disk' device the optionsX parameter can be used to specify an -# alternative redolog file (journal) of some image modes. For 'vvfat' mode USB -# disks the optionsX parameter can be used to specify the disk size (range -# 128M ... 128G). If the size is not specified, it defaults to 504M. -# For the USB 'floppy' device the optionsX parameter can be used to specify an -# alternative device ID to be reported. Currently only the model "teac" is -# supported (can fix hw detection in some guest OS). The USB floppy also -# accepts the parameter "write_protected" with valid values 0 and 1 to select -# the access mode (default is 0). -#======================================================================= -#usb_uhci: enabled=1 -#usb_uhci: enabled=1, port1=mouse, port2=disk:usbstick.img -#usb_uhci: enabled=1, port1=hub:7, port2=disk:growing:usbdisk.img -#usb_uhci: enabled=1, port2=disk:undoable:usbdisk.img, options2=journal:redo.log -#usb_uhci: enabled=1, port2=disk:usbdisk2.img, options2=sect_size:1024 -#usb_uhci: enabled=1, port2=disk:vvfat:vvfat, options2="debug,speed:full" -#usb_uhci: enabled=1, port1=printer:printdata.bin, port2=cdrom:image.iso -#usb_uhci: enabled=1, port2=floppy:vvfat:diskette, options2="model:teac" - -#======================================================================= -# USB_OHCI: -# This option controls the presence of the USB OHCI host controller with a -# 2-port hub. The portX parameter accepts the same device types with the same -# syntax as the UHCI controller (see above). The optionsX parameter is also -# available on OHCI. -#======================================================================= -#usb_ohci: enabled=1 -#usb_ohci: enabled=1, port1=printer:usbprinter.bin - -#======================================================================= -# USB_EHCI: -# This option controls the presence of the USB EHCI host controller with a -# 6-port hub. The portX parameter accepts the same device types with the -# same syntax as the UHCI controller (see above). The optionsX parameter is -# also available on EHCI. -#======================================================================= -#usb_ehci: enabled=1 - -#======================================================================= -# USB_XHCI: -# This option controls the presence of the USB xHCI host controller with a -# 4-port hub. The portX parameter accepts the same device types with the -# same syntax as the UHCI controller (see above). The optionsX parameter is -# also available on xHCI. NOTE: port 1 and 2 are USB3 and only support -# super-speed devices, but port 3 and 4 are USB2 and support speed settings -# low, full and high. -#======================================================================= -#usb_xhci: enabled=1 - -#======================================================================= -# PCIDEV: -# PCI host device mapping -# WARNING: This Bochs feature is not maintained yet and may fail. -#======================================================================= -#pcidev: vendor=0x1234, device=0x5678 - -#======================================================================= -# GDBSTUB: -# Enable GDB stub. See user documentation for details. -# Default value is enabled=0. -# WARNING: This Bochs feature is not maintained yet and may fail. -#======================================================================= -#gdbstub: enabled=0, port=1234, text_base=0, data_base=0, bss_base=0 - -#======================================================================= -# MAGIC_BREAK: -# This enables the "magic breakpoint" feature when using the debugger. -# The useless cpu instruction XCHG BX, BX causes Bochs to enter the -# debugger mode. This might be useful for software development. -# -# Example: -# magic_break: enabled=1 -#======================================================================= -magic_break: enabled=1 - -#======================================================================= -# DEBUG_SYMBOLS: -# This loads symbols from the specified file for use in Bochs' internal -# debugger. Symbols are loaded into global context. This is equivalent to -# issuing ldsym debugger command at start up. -# -# Example: -# debug_symbols: file="kernel.sym" -# debug_symbols: file="kernel.sym", offset=0x80000000 -#======================================================================= -#debug_symbols: file="kernel.sym" - -#print_timestamps: enabled=1 - -#======================================================================= -# PORT_E9_HACK: -# The 0xE9 port doesn't exists in normal ISA architecture. However, we -# define a convention here, to display on the console of the system running -# Bochs anything that is written to it. The idea is to provide debug output -# very early when writing BIOS or OS code for example, without having to -# bother with setting up a serial port or etc. Reading from port 0xE9 will -# will return 0xe9 to let you know if the feature is available. -# Leave this 0 unless you have a reason to use it. -# -# Example: -# port_e9_hack: enabled=1 -#======================================================================= -#port_e9_hack: enabled=1 - -#======================================================================= -# other stuff -#======================================================================= -# WARNING: This Bochs feature is not maintained yet. Is it still used ? -# To use it, set BX_LOAD32BITOSHACK in config.h to 1 and recompile Bochs. -#load32bitOSImage: os=nullkernel, path=../kernel.img, iolog=../vga_io.log -#load32bitOSImage: os=linux, path=../linux.img, iolog=../vga_io.log, initrd=../initrd.img - -#======================================================================= -# fullscreen: ONLY IMPLEMENTED ON AMIGA -# Request that Bochs occupy the entire screen instead of a -# window. -# -# Examples: -# fullscreen: enabled=0 -# fullscreen: enabled=1 -#======================================================================= -#fullscreen: enabled=0 -#screenmode: name="sample" - -#======================================================================= -# USER_PLUGIN: -# Load user-defined plugin. This option is available only if Bochs is -# compiled with plugin support. Maximum 8 different plugins are supported. -# See the example in the Bochs sources how to write a plugin device. -#======================================================================= -#user_plugin: name=testdev - -#======================================================================= -# for Macintosh, use the style of pathnames in the following -# examples. -# -# vgaromimage: :bios:VGABIOS-elpin-2.40 -# romimage: file=:bios:BIOS-bochs-latest, address=0xf0000 -# floppya: 1_44=[fd:], status=inserted -#======================================================================= - -#======================================================================= -# MEGS -# Set the number of Megabytes of physical memory you want to emulate. -# The default is 32MB, most OS's won't need more than that. -# The maximum amount of memory supported is 2048Mb. -# The 'MEGS' option is deprecated. Use 'MEMORY' option instead. -#======================================================================= -#megs: 256 -#megs: 128 -#megs: 64 -#megs: 32 -#megs: 16 -#megs: 8 diff --git a/ape/etc/bochsrc.ffs b/ape/etc/bochsrc.ffs deleted file mode 100644 index 3467144ef..000000000 --- a/ape/etc/bochsrc.ffs +++ /dev/null @@ -1,1294 +0,0 @@ -# The default state is the unsafest state with Bochs, which makes it nearly -# impossible to script. Here we'll attempt to turn off as much of its -# functionality as possible, so we can have a non-interactive mode. - -#======================================================================= -# PLUGIN_CTRL: -# Controls the presence of optional device plugins. These plugins are loaded -# directly with this option and some of them install a config option that is -# only available when the plugin device is loaded. The value "1" means to load -# the plugin and "0" will unload it (if loaded before). -# -# These plugins will be loaded by default (if present): 'biosdev', 'extfpuirq', -# 'gameport', 'iodebug','parallel', 'serial', 'speaker' and 'unmapped'. -# -# These plugins are also supported, but they are usually loaded directly with -# their bochsrc option: 'e1000', 'es1370', 'ne2k', 'pcidev', 'pcipnic', 'sb16', -# 'usb_ehci', 'usb_ohci', 'usb_uhci', 'usb_xhci' and 'voodoo'. -#======================================================================= -#plugin_ctrl: unmapped=0, e1000=1 # unload 'unmapped' and load 'e1000' -plugin_ctrl: biosdev=0, iodebug=0 - -#======================================================================= -# CONFIG_INTERFACE -# -# The configuration interface is a series of menus or dialog boxes that -# allows you to change all the settings that control Bochs's behavior. -# Depending on the platform there are up to 3 choices of configuration -# interface: a text mode version called "textconfig" and two graphical versions -# called "win32config" and "wx". The text mode version uses stdin/stdout and -# is always compiled in, unless Bochs is compiled for wx only. The choice -# "win32config" is only available on win32 and it is the default there. -# The choice "wx" is only available when you use "--with-wx" on the configure -# command. If you do not write a config_interface line, Bochs will -# choose a default for you. -# -# NOTE: if you use the "wx" configuration interface, you must also use -# the "wx" display library. -#======================================================================= -#config_interface: textconfig -#config_interface: win32config -#config_interface: wx - -#======================================================================= -# DISPLAY_LIBRARY -# -# The display library is the code that displays the Bochs VGA screen. Bochs -# has a selection of about 10 different display library implementations for -# different platforms. If you run configure with multiple --with-* options, -# the display_library command lets you choose which one you want to run with. -# If you do not write a display_library line, Bochs will choose a default for -# you. -# -# The choices are: -# x use X windows interface, cross platform -# win32 use native win32 libraries -# carbon use Carbon library (for MacOS X) -# macintosh use MacOS pre-10 -# amigaos use native AmigaOS libraries -# sdl use SDL 1.2.x library, cross platform -# sdl2 use SDL 2.x library, cross platform -# svga use SVGALIB library for Linux, allows graphics without X11 -# term text only, uses curses/ncurses library, cross platform -# rfb provides an interface to AT&T's VNC viewer, cross platform -# vncsrv use LibVNCServer for extended RFB(VNC) support -# wx use wxWidgets library, cross platform -# nogui no display at all -# -# NOTE: if you use the "wx" configuration interface, you must also use -# the "wx" display library. -# -# Specific options: -# Some display libraries now support specific options to control their -# behaviour. These options are supported by more than one display library: -# -# "gui_debug" - use GTK debugger gui (sdl, sdl2, x) / Win32 debugger gui (sdl, -# sdl2, win32) -# "hideIPS" - disable IPS output in status bar (rfb, sdl, sdl2, vncsrv, -# win32, wx, x) -# "nokeyrepeat" - turn off host keyboard repeat (sdl, sdl2, win32, x) -# "timeout" - time (in seconds) to wait for client (rfb, vncsrv) -# -# See the examples below for other currently supported options. -#======================================================================= -#display_library: amigaos -#display_library: carbon -#display_library: macintosh -display_library: nogui -#display_library: rfb -#display_library: sdl, options="fullscreen" # startup in fullscreen mode -#display_library: sdl2, options="fullscreen" # startup in fullscreen mode -#display_library: term -#display_library: vncsrv -# "traphotkeys" - system hotkeys not handled by host OS, but sent to guest -# (win32 in mouse capture and fullscreen mode: alt-tab, win, -# alt-space, alt-esc, ctrl-esc) -#display_library: win32, options="traphotkeys" -#display_library: wx -#display_library: x, gui_debug=1 -#display_library: x, options="gui_debug" - -#======================================================================= -# CPU: -# This defines cpu-related parameters inside Bochs: -# -# MODEL: -# Selects CPU configuration to emulate from pre-defined list of all -# supported configurations. When this option is used and the value -# is different from 'bx_generic', the parameters of the CPUID option -# have no effect anymore. -# -# CPU configurations that can be selected: -# ----------------------------------------------------------------- -# pentium Intel Pentium (P54C) -# pentium_mmx Intel Pentium MMX -# amd_k6_2_chomper AMD-K6(tm) 3D processor (Chomper) -# p2_klamath Intel Pentium II (Klamath) -# p3_katmai Intel Pentium III (Katmai) -# p4_willamette Intel(R) Pentium(R) 4 (Willamette) -# core_duo_t2400_yonah Intel(R) Core(TM) Duo CPU T2400 (Yonah) -# atom_n270 Intel(R) Atom(TM) CPU N270 -# p4_prescott_celeron_336 Intel(R) Celeron(R) 336 (Prescott) -# athlon64_clawhammer AMD Athlon(tm) 64 Processor 2800+ (Clawhammer) -# athlon64_venice AMD Athlon(tm) 64 Processor 3000+ (Venice) -# turion64_tyler AMD Turion(tm) 64 X2 Mobile TL-60 (Tyler) -# phenom_8650_toliman AMD Phenom X3 8650 (Toliman) -# core2_penryn_t9600 Intel Mobile Core 2 Duo T9600 (Penryn) -# corei5_lynnfield_750 Intel(R) Core(TM) i5 750 (Lynnfield) -# corei5_arrandale_m520 Intel(R) Core(TM) i5 M 520 (Arrandale) -# corei7_sandy_bridge_2600k Intel(R) Core(TM) i7-2600K (Sandy Bridge) -# zambezi AMD FX(tm)-4100 Quad-Core Processor (Zambezi) -# trinity_apu AMD A8-5600K APU (Trinity) -# ryzen AMD Ryzen 7 1700 -# corei7_ivy_bridge_3770k Intel(R) Core(TM) i7-3770K CPU (Ivy Bridge) -# corei7_haswell_4770 Intel(R) Core(TM) i7-4770 CPU (Haswell) -# broadwell_ult Intel(R) Processor 5Y70 CPU (Broadwell) -# -# COUNT: -# Set the number of processors:cores per processor:threads per core when -# Bochs is compiled for SMP emulation. Bochs currently supports up to -# 14 threads (legacy APIC) or 254 threads (xAPIC or higher) running -# simultaniosly. If Bochs is compiled without SMP support, it won't accept -# values different from 1. -# -# QUANTUM: -# Maximum amount of instructions allowed to execute by processor before -# returning control to another cpu. This option exists only in Bochs -# binary compiled with SMP support. -# -# RESET_ON_TRIPLE_FAULT: -# Reset the CPU when triple fault occur (highly recommended) rather than -# PANIC. Remember that if you trying to continue after triple fault the -# simulation will be completely bogus ! -# -# CPUID_LIMIT_WINNT: -# Determine whether to limit maximum CPUID function to 2. This mode is -# required to workaround WinNT installation and boot issues. -# -# MSRS: -# Define path to user CPU Model Specific Registers (MSRs) specification. -# See example in msrs.def. -# -# IGNORE_BAD_MSRS: -# Ignore MSR references that Bochs does not understand; print a warning -# message instead of generating #GP exception. This option is enabled -# by default but will not be avaiable if configurable MSRs are enabled. -# -# MWAIT_IS_NOP: -# When this option is enabled MWAIT will not put the CPU into a sleep state. -# This option exists only if Bochs compiled with --enable-monitor-mwait. -# -# IPS: -# Emulated Instructions Per Second. This is the number of IPS that bochs -# is capable of running on your machine. You can recompile Bochs with -# --enable-show-ips option enabled, to find your host's capability. -# Measured IPS value will then be logged into your log file or shown -# in the status bar (if supported by the gui). -# -# IPS is used to calibrate many time-dependent events within the bochs -# simulation. For example, changing IPS affects the frequency of VGA -# updates, the duration of time before a key starts to autorepeat, and -# the measurement of BogoMips and other benchmarks. -# -# Examples: -# -# Bochs Machine/Compiler Mips -# ______________________________________________________________________ -# 2.4.6 3.4Ghz Intel Core i7 2600 with Win7x64/g++ 4.5.2 85 to 95 Mips -# 2.3.7 3.2Ghz Intel Core 2 Q9770 with WinXP/g++ 3.4 50 to 55 Mips -# 2.3.7 2.6Ghz Intel Core 2 Duo with WinXP/g++ 3.4 38 to 43 Mips -# 2.2.6 2.6Ghz Intel Core 2 Duo with WinXP/g++ 3.4 21 to 25 Mips -# 2.2.6 2.1Ghz Athlon XP with Linux 2.6/g++ 3.4 12 to 15 Mips -#======================================================================= -#cpu: model=core2_penryn_t9600, count=1, ips=50000000, reset_on_triple_fault=1, ignore_bad_msrs=1, msrs="msrs.def" -#cpu: model=core2_penryn_t9600, count=1, ips=50000000, reset_on_triple_fault=0 cpuid_limit_winnt=1 -#cpu: model=corei7_haswell_4770, count=1, ips=50000000, reset_on_triple_fault=0 -#cpu: model=broadwell_ult, count=1, ips=50000000, reset_on_triple_fault=0 -#cpu: model=corei7_ivy_bridge_3770k, count=1, ips=50000000, reset_on_triple_fault=0 -#cpu: model=corei5_lynnfield_750, count=1, ips=50000000, reset_on_triple_fault=0 - -#======================================================================= -# CPUID: -# -# This defines features and functionality supported by Bochs emulated CPU. -# The option has no offect if CPU model was selected in CPU option. -# -# MMX: -# Select MMX instruction set support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 5. -# -# APIC: -# Select APIC configuration (LEGACY/XAPIC/XAPIC_EXT/X2APIC). -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 5. -# -# SEP: -# Select SYSENTER/SYSEXIT instruction set support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# SIMD: -# Select SIMD instructions support. -# Any of NONE/SSE/SSE2/SSE3/SSSE3/SSE4_1/SSE4_2/AVX/AVX2/AVX512 -# could be selected. -# -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# The AVX choises exists only if Bochs compiled with --enable-avx option. -# -# SSE4A: -# Select AMD SSE4A instructions support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# MISALIGNED_SSE: -# Select AMD Misaligned SSE mode support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# AES: -# Select AES instruction set support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# SHA: -# Select SHA instruction set support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# MOVBE: -# Select MOVBE Intel(R) Atom instruction support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# ADX: -# Select ADCX/ADOX instructions support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# XSAVE: -# Select XSAVE extensions support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# XSAVEOPT: -# Select XSAVEOPT instruction support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# AVX_F16C: -# Select AVX float16 convert instructions support. -# This option exists only if Bochs compiled with --enable-avx option. -# -# AVX_FMA: -# Select AVX fused multiply add (FMA) instructions support. -# This option exists only if Bochs compiled with --enable-avx option. -# -# BMI: -# Select BMI1/BMI2 instructions support. -# This option exists only if Bochs compiled with --enable-avx option. -# -# XOP: -# Select AMD XOP instructions support. -# This option exists only if Bochs compiled with --enable-avx option. -# -# FMA4: -# Select AMD four operand FMA instructions support. -# This option exists only if Bochs compiled with --enable-avx option. -# -# TBM: -# Select AMD Trailing Bit Manipulation (TBM) instructions support. -# This option exists only if Bochs compiled with --enable-avx option. -# -# X86-64: -# Enable x86-64 and long mode support. -# This option exists only if Bochs compiled with x86-64 support. -# -# 1G_PAGES: -# Enable 1G page size support in long mode. -# This option exists only if Bochs compiled with x86-64 support. -# -# PCID: -# Enable Process-Context Identifiers (PCID) support in long mode. -# This option exists only if Bochs compiled with x86-64 support. -# -# FSGSBASE: -# Enable GS/GS BASE access instructions support in long mode. -# This option exists only if Bochs compiled with x86-64 support. -# -# SMEP: -# Enable Supervisor Mode Execution Protection (SMEP) support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# SMAP: -# Enable Supervisor Mode Access Prevention (SMAP) support. -# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. -# -# MWAIT: -# Select MONITOR/MWAIT instructions support. -# This option exists only if Bochs compiled with --enable-monitor-mwait. -# -# VMX: -# Select VMX extensions emulation support. -# This option exists only if Bochs compiled with --enable-vmx option. -# -# SVM: -# Select AMD SVM (Secure Virtual Machine) extensions emulation support. -# This option exists only if Bochs compiled with --enable-svm option. -# -# VENDOR_STRING: -# Set the CPUID vendor string returned by CPUID(0x0). This should be a -# twelve-character ASCII string. -# -# BRAND_STRING: -# Set the CPUID vendor string returned by CPUID(0x80000002 .. 0x80000004). -# This should be at most a forty-eight-character ASCII string. -# -# LEVEL: -# Set emulated CPU level information returned by CPUID. Default value is -# determined by configure option --enable-cpu-level. Currently supported -# values are 5 (for Pentium and similar processors) and 6 (for P6 and -# later processors). -# -# FAMILY: -# Set model information returned by CPUID. Default family value determined -# by configure option --enable-cpu-level. -# -# MODEL: -# Set model information returned by CPUID. Default model value is 3. -# -# STEPPING: -# Set stepping information returned by CPUID. Default stepping value is 3. -#======================================================================= -#cpuid: x86_64=1, mmx=1, sep=1, simd=sse4_2, apic=xapic, aes=1, movbe=1, xsave=1 -#cpuid: family=6, model=0x1a, stepping=5 - -#======================================================================= -# MEMORY -# Set the amount of physical memory you want to emulate. -# -# GUEST: -# Set amount of guest physical memory to emulate. The default is 32MB, -# the maximum amount limited only by physical address space limitations. -# -# HOST: -# Set amount of host memory you want to allocate for guest RAM emulation. -# It is possible to allocate less memory than you want to emulate in guest -# system. This will fake guest to see the non-existing memory. Once guest -# system touches new memory block it will be dynamically taken from the -# memory pool. You will be warned (by FATAL PANIC) in case guest already -# used all allocated host memory and wants more. -# -#======================================================================= -memory: guest=32, host=32 - -#======================================================================= -# ROMIMAGE: -# The ROM BIOS controls what the PC does when it first powers on. -# Normally, you can use a precompiled BIOS in the source or binary -# distribution called BIOS-bochs-latest. The default ROM BIOS is usually loaded -# starting at address 0xfffe0000, and it is exactly 128k long. The legacy -# version of the Bochs BIOS is usually loaded starting at address 0xffff0000, -# and it is exactly 64k long. -# You can use the environment variable $BXSHARE to specify the location -# of the BIOS. -# The usage of external large BIOS images (up to 512k) at memory top is -# now supported, but we still recommend to use the BIOS distributed with Bochs. -# The start address is optional, since it can be calculated from image size. -# The Bochs BIOS currently supports only the option "fastboot" to skip the -# boot menu delay. -#======================================================================= -#romimage: file=$BXSHARE/BIOS-bochs-latest, options=fastboot -romimage: file=$BXSHARE/BIOS-bochs-legacy, options=fastboot -#romimage: file=$BXSHARE/bios.bin-1.7.5 # http://www.seabios.org/SeaBIOS -#romimage: file=mybios.bin, address=0xfff80000 # 512k at memory top -#romimage: file=BIOS-bochs-latest, options=fastboot - -#======================================================================= -# VGAROMIMAGE -# You now need to load a VGA ROM BIOS into C0000. -#======================================================================= -#vgaromimage: file=$BXSHARE/VGABIOS-elpin-2.40 -#vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latest -#vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latest-cirrus -#vgaromimage: file=/usr/local/share/qemu/sgabios.bin - -#======================================================================= -# OPTROMIMAGE[1-4]: -# You may now load up to 4 optional ROM images. Be sure to use a -# read-only area, typically between C8000 and EFFFF. These optional -# ROM images should not overwrite the rombios (located at -# F0000-FFFFF) and the videobios (located at C0000-C7FFF). -# Those ROM images will be initialized by the bios if they contain -# the right signature (0x55AA) and a valid checksum. -# It can also be a convenient way to upload some arbitrary code/data -# in the simulation, that can be retrieved by the boot loader -#======================================================================= -#optromimage1: file=optionalrom.bin, address=0xd0000 -#optromimage2: file=optionalrom.bin, address=0xd1000 -#optromimage3: file=optionalrom.bin, address=0xd2000 -#optromimage4: file=optionalrom.bin, address=0xd3000 - -#optramimage1: file=/path/file1.img, address=0x0010000 -#optramimage2: file=/path/file2.img, address=0x0020000 -#optramimage3: file=/path/file3.img, address=0x0030000 -#optramimage4: file=/path/file4.img, address=0x0040000 - -#======================================================================= -# VGA: -# This defines parameters related to the VGA display -# -# EXTENSION -# Here you can specify the display extension to be used. With the value -# 'none' you can use standard VGA with no extension. Other supported -# values are 'vbe' for Bochs VBE, 'cirrus' for Cirrus SVGA support and -# 'voodoo' for Voodoo Graphics support (see 'voodoo' option). -# -# UPDATE_FREQ -# This parameter specifies the number of display updates per second. -# The VGA update timer by default uses the realtime engine with a value -# of 5. This parameter can be changed at runtime. -# -# REALTIME -# If set to 1 (default), the VGA timer is based on realtime, otherwise it -# is driven by the cpu and depends on the ips setting. If the host is slow -# (low ips, update_freq) and the guest uses HLT appropriately, setting this -# to 0 and "clock: sync=none" may improve the responsiveness of the guest -# GUI when the guest is otherwise idle. -# -# Examples: -# vga: extension=cirrus, update_freq=10 -#======================================================================= -vga: extension=none, update_freq=5, realtime=1 - -#======================================================================= -# VOODOO: -# This defines the Voodoo Graphics emulation (experimental). Currently -# supported models are 'voodoo1', 'voodoo2', 'banshee' and 'voodoo3'. The -# Voodoo2 support is not yet complete, but almost usable. The Banshee and -# Voodoo3 support is under construction, but basicly usable. The 2D/3D cards -# require an external VGA BIOS the vga extension option to be set to 'voodoo'. -# If the i440BX PCI chipset is selected, they can be assigned to AGP (slot #5). -# The gui screen update timing for all models is controlled by the related 'vga' -# options. -# -# Examples: -# voodoo: enabled=1, model=voodoo2 -#======================================================================= -#voodoo: enabled=1, model=voodoo1 - -#======================================================================= -# KEYBOARD: -# This defines parameters related to the emulated keyboard -# -# TYPE: -# Type of keyboard return by a "identify keyboard" command to the -# keyboard controller. It must be one of "xt", "at" or "mf". -# Defaults to "mf". It should be ok for almost everybody. A known -# exception is french macs, that do have a "at"-like keyboard. -# -# SERIAL_DELAY: -# Approximate time in microseconds that it takes one character to -# be transferred from the keyboard to controller over the serial path. -# -# PASTE_DELAY: -# Approximate time in microseconds between attempts to paste -# characters to the keyboard controller. This leaves time for the -# guest os to deal with the flow of characters. The ideal setting -# depends on how your operating system processes characters. The -# default of 100000 usec (.1 seconds) was chosen because it works -# consistently in Windows. -# If your OS is losing characters during a paste, increase the paste -# delay until it stops losing characters. -# -# KEYMAP: -# This enables a remap of a physical localized keyboard to a -# virtualized us keyboard, as the PC architecture expects. -# -# USER_SHORTCUT: -# This defines the keyboard shortcut to be sent when you press the "user" -# button in the headerbar. The shortcut string is a combination of maximum -# 3 key names (listed below) separated with a '-' character. -# Valid key names: -# "alt", "bksl", "bksp", "ctrl", "del", "down", "end", "enter", "esc", -# "f1", ... "f12", "home", "ins", "left", "menu", "minus", "pgdwn", "pgup", -# "plus", "power", "print", "right", "scrlck", "shift", "space", "tab", "up" -# and "win". -# -# Examples: -# keyboard: type=mf, serial_delay=200, paste_delay=100000 -# keyboard: keymap=gui/keymaps/x11-pc-de.map -# keyboard: user_shortcut=ctrl-alt-del -#======================================================================= -keyboard: type=mf, serial_delay=250 - -#======================================================================= -# MOUSE: -# This defines parameters for the emulated mouse type, the initial status -# of the mouse capture and the runtime method to toggle it. -# -# TYPE: -# With the mouse type option you can select the type of mouse to emulate. -# The default value is 'ps2'. The other choices are 'imps2' (wheel mouse -# on PS/2), 'serial', 'serial_wheel', 'serial_msys' (one com port requires -# setting 'mode=mouse') 'inport' and 'bus' (if present). To connect a mouse -# to a USB port, see the 'usb_uhci', 'usb_ohci', 'usb_ehci' or 'usb_xhci' -# options (requires PCI and USB support). -# -# ENABLED: -# The Bochs gui creates mouse "events" unless the 'enabled' option is -# set to 0. The hardware emulation itself is not disabled by this. -# Unless you have a particular reason for enabling the mouse by default, -# it is recommended that you leave it off. You can also toggle the mouse -# usage at runtime (RFB, SDL, Win32, wxWidgets and X11 - see below). -# -# TOGGLE: -# The default method to toggle the mouse capture at runtime is to press the -# CTRL key and the middle mouse button ('ctrl+mbutton'). This option allows -# to change the method to 'ctrl+f10' (like DOSBox), 'ctrl+alt' (like QEMU) -# or 'f12'. -# -# Examples: -# mouse: enabled=1 -# mouse: type=imps2, enabled=1 -# mouse: type=serial, enabled=1 -# mouse: enabled=0, toggle=ctrl+f10 -#======================================================================= -mouse: enabled=0 - -#======================================================================= -# PCI: -# This option controls the presence of a PCI chipset in Bochs. Currently it -# supports the i430FX, i440FX and i440BX chipsets. You can also specify the -# devices connected to PCI slots. Up to 5 slots are available. For these -# combined PCI/ISA devices assigning to slot is mandatory if you want to emulate -# the PCI model: cirrus, ne2k and pcivga. These PCI-only devices are also -# supported, but they are auto-assigned if you don't use the slot configuration: -# e1000, es1370, pcidev, pcipnic, usb_ehci, usb_ohci, usb_xhci and voodoo. -# In case of the i440BX chipset, slot #5 is the AGP slot. Currently only the -# 'voodoo' device can be assigned to AGP. -# -# Example: -# pci: enabled=1, chipset=i440fx, slot1=pcivga, slot2=ne2k -#======================================================================= -pci: enabled=1, chipset=i440fx - -#======================================================================= -# CLOCK: -# This defines the parameters of the clock inside Bochs: -# -# SYNC: -# This defines the method how to synchronize the Bochs internal time -# with realtime. With the value 'none' the Bochs time relies on the IPS -# value and no host time synchronization is used. The 'slowdown' method -# sacrifices performance to preserve reproducibility while allowing host -# time correlation. The 'realtime' method sacrifices reproducibility to -# preserve performance and host-time correlation. -# It is possible to enable both synchronization methods. -# -# RTC_SYNC: -# If this option is enabled together with the realtime synchronization, -# the RTC runs at realtime speed. This feature is disabled by default. -# -# TIME0: -# Specifies the start (boot) time of the virtual machine. Use a time -# value as returned by the time(2) system call or a string as returned -# by the ctime(3) system call. If no time0 value is set or if time0 -# equal to 1 (special case) or if time0 equal 'local', the simulation -# will be started at the current local host time. If time0 equal to 2 -# (special case) or if time0 equal 'utc', the simulation will be started -# at the current utc time. -# -# Syntax: -# clock: sync=[none|slowdown|realtime|both], time0=[timeValue|local|utc] -# -# Example: -# clock: sync=none, time0=local # Now (localtime) -# clock: sync=slowdown, time0=315529200 # Tue Jan 1 00:00:00 1980 -# clock: sync=none, time0="Mon Jan 1 00:00:00 1990" # 631148400 -# clock: sync=realtime, time0=938581955 # Wed Sep 29 07:12:35 1999 -# clock: sync=realtime, time0="Sat Jan 1 00:00:00 2000" # 946681200 -# clock: sync=none, time0=1 # Now (localtime) -# clock: sync=none, time0=utc # Now (utc/gmt) -# -# Default value are sync=none, rtc_sync=0, time0=local -#======================================================================= -clock: sync=none, time0=local - -#======================================================================= -# CMOSIMAGE: -# This defines a binary image file with size 128 bytes that can be loaded into -# the CMOS RAM at startup. The rtc_init parameter controls whether initialize -# the RTC with values stored in the image. By default the time0 argument given -# to the clock option is used. With 'rtc_init=image' the image is the source -# for the initial time. -# -# Example: -# cmosimage: file=cmos.img, rtc_init=image -#======================================================================= -#cmosimage: file=cmos.img, rtc_init=time0 - -#======================================================================= -# private_colormap: Request that the GUI create and use it's own -# non-shared colormap. This colormap will be used -# when in the bochs window. If not enabled, a -# shared colormap scheme may be used. Not implemented -# on all GUI's. -# -# Examples: -# private_colormap: enabled=1 -# private_colormap: enabled=0 -#======================================================================= -private_colormap: enabled=0 - -#======================================================================= -# FLOPPYA: -# Point this to pathname of floppy image file or device -# This should be of a bootable floppy(image/device) if you're -# booting from 'a' (or 'floppy'). -# -# You can set the initial status of the media to 'ejected' or 'inserted'. -# floppya: 2_88=path, status=ejected (2.88M 3.5" media) -# floppya: 1_44=path, status=inserted (1.44M 3.5" media) -# floppya: 1_2=path, status=ejected (1.2M 5.25" media) -# floppya: 720k=path, status=inserted (720K 3.5" media) -# floppya: 360k=path, status=inserted (360K 5.25" media) -# floppya: 320k=path, status=inserted (320K 5.25" media) -# floppya: 180k=path, status=inserted (180K 5.25" media) -# floppya: 160k=path, status=inserted (160K 5.25" media) -# floppya: image=path, status=inserted (guess media type from image size) -# floppya: 1_44=vvfat:path, status=inserted (use directory as VFAT media) -# floppya: type=1_44 (1.44M 3.5" floppy drive, no media) -# -# The path should be the name of a disk image file. On Unix, you can use a raw -# device name such as /dev/fd0 on Linux. On win32 platforms, use drive letters -# such as a: or b: as the path. The parameter 'image' works with image files -# only. In that case the size must match one of the supported types. -# The parameter 'type' can be used to enable the floppy drive without media -# and status specified. Usually the drive type is set up based on the media type. -# The optional parameter 'write_protected' can be used to control the media -# write protect switch. By default it is turned off. -#======================================================================= -#floppya: 1_44=, status=inserted -#floppya: image=../1.44, status=inserted -#floppya: 1_44=/dev/fd0H1440, status=inserted -#floppya: 1_2=../1_2, status=inserted -#floppya: 1_44=a:, status=inserted -#floppya: 1_44=a.img, status=inserted, write_protected=1 -#floppya: 1_44=/dev/rfd0a, status=inserted -#floppya: 1_44=boot.img, status=inserted, write_protected=1 - -#======================================================================= -# FLOPPYB: -# See FLOPPYA above for syntax -#======================================================================= -#floppyb: 1_44=b:, status=inserted -#floppyb: 1_44=b.img, status=inserted - -#======================================================================= -# ATA0, ATA1, ATA2, ATA3 -# ATA controller for hard disks and cdroms -# -# ata[0-3]: enabled=[0|1], ioaddr1=addr, ioaddr2=addr, irq=number -# -# These options enables up to 4 ata channels. For each channel -# the two base io addresses and the irq must be specified. -# -# ata0 and ata1 are enabled by default with the values shown below -# -# Examples: -# ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14 -# ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15 -# ata2: enabled=1, ioaddr1=0x1e8, ioaddr2=0x3e0, irq=11 -# ata3: enabled=1, ioaddr1=0x168, ioaddr2=0x360, irq=9 -#======================================================================= -ata0: enabled=0, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14 -ata1: enabled=0, ioaddr1=0x170, ioaddr2=0x370, irq=15 -ata2: enabled=0, ioaddr1=0x1e8, ioaddr2=0x3e0, irq=11 -ata3: enabled=0, ioaddr1=0x168, ioaddr2=0x360, irq=9 - -#======================================================================= -# ATA[0-3]-MASTER, ATA[0-3]-SLAVE -# -# This defines the type and characteristics of all attached ata devices: -# type= type of attached device [disk|cdrom] -# mode= only valid for disks [flat|concat|external|dll|sparse|vmware3] -# [vmware4|undoable|growing|volatile|vpc] -# [vbox|vvfat] -# path= path of the image / directory -# cylinders= only valid for disks -# heads= only valid for disks -# spt= only valid for disks -# status= only valid for cdroms [inserted|ejected] -# biosdetect= type of biosdetection [auto|cmos|none] -# translation=type of translation of the bios, only for disks [none|lba|large|rechs|auto] -# model= string returned by identify device command -# journal= optional filename of the redolog for undoable, volatile and vvfat disks -# -# Point this at a hard disk image file, cdrom iso file, or physical cdrom -# device. To create a hard disk image, try running bximage. It will help you -# choose the size and then suggest a line that works with it. -# -# In UNIX it may be possible to use a raw device as a Bochs hard disk, -# but WE DON'T RECOMMEND IT. In Windows there is no easy way. -# -# In windows, the drive letter + colon notation should be used for cdroms. -# Depending on versions of windows and drivers, you may only be able to -# access the "first" cdrom in the system. On MacOSX, use path="drive" -# to access the physical drive. -# -# The path is mandatory for hard disks. Disk geometry autodetection works with -# images created by bximage if CHS is set to 0/0/0 (cylinders are calculated -# using heads=16 and spt=63). For other hard disk images and modes the -# cylinders, heads, and spt are mandatory. In all cases the disk size reported -# from the image must be exactly C*H*S*512. -# -# Default values are: -# mode=flat, biosdetect=auto, translation=auto, model="Generic 1234" -# -# The biosdetect option has currently no effect on the bios -# -# Examples: -# ata0-master: type=disk, mode=flat, path=10M.sample, cylinders=306, heads=4, spt=17 -# ata0-slave: type=disk, mode=flat, path=20M.sample, cylinders=615, heads=4, spt=17 -# ata1-master: type=disk, mode=flat, path=30M.sample, cylinders=615, heads=6, spt=17 -# ata1-slave: type=disk, mode=flat, path=46M.sample, cylinders=940, heads=6, spt=17 -# ata2-master: type=disk, mode=flat, path=62M.sample, cylinders=940, heads=8, spt=17 -# ata2-slave: type=disk, mode=flat, path=112M.sample, cylinders=900, heads=15, spt=17 -# ata3-master: type=disk, mode=flat, path=483M.sample, cylinders=1024, heads=15, spt=63 -# ata3-slave: type=cdrom, path=iso.sample, status=inserted -#======================================================================= -#ata0-master: type=disk, mode=flat, path="30M.sample" -#ata0-master: type=disk, mode=flat, path="30M.sample", cylinders=615, heads=6, spt=17 -#ata0-master: type=disk, mode=flat, path="c.img", cylinders=0 # autodetect -#ata0-slave: type=disk, mode=vvfat, path=/bochs/images/vvfat, journal=vvfat.redolog -#ata0-slave: type=cdrom, path=D:, status=inserted -#ata0-slave: type=cdrom, path=/dev/cdrom, status=inserted -#ata0-slave: type=cdrom, path="drive", status=inserted -#ata0-slave: type=cdrom, path=/dev/rcd0d, status=inserted - -#======================================================================= -# BOOT: -# This defines the boot sequence. Now you can specify up to 3 boot drives, -# which can be 'floppy', 'disk', 'cdrom' or 'network' (boot ROM). -# Legacy 'a' and 'c' are also supported. -# Examples: -# boot: floppy -# boot: cdrom, disk -# boot: network, disk -# boot: cdrom, floppy, disk -#======================================================================= -boot: floppy -#boot: disk - -#======================================================================= -# FLOPPY_BOOTSIG_CHECK: disabled=[0|1] -# Enables or disables the 0xaa55 signature check on boot floppies -# Defaults to disabled=0 -# Examples: -# floppy_bootsig_check: disabled=0 -# floppy_bootsig_check: disabled=1 -#======================================================================= -floppy_bootsig_check: disabled=0 - -#======================================================================= -# LOG: -# Give the path of the log file you'd like Bochs debug and misc. verbiage -# to be written to. If you don't use this option or set the filename to -# '-' the output is written to the console. If you really don't want it, -# make it "/dev/null" (Unix) or "nul" (win32). :^( -# -# Examples: -# log: ./bochs.out -# log: /dev/tty -#======================================================================= -#log: /dev/null -#log: .bochs.log -#log: - -log: /dev/stderr - -#======================================================================= -# LOGPREFIX: -# This handles the format of the string prepended to each log line. -# You may use those special tokens : -# %t : 11 decimal digits timer tick -# %i : 8 hexadecimal digits of cpu current eip (ignored in SMP configuration) -# %e : 1 character event type ('i'nfo, 'd'ebug, 'p'anic, 'e'rror) -# %d : 5 characters string of the device, between brackets -# -# Default : %t%e%d -# Examples: -# logprefix: %t-%e-@%i-%d -# logprefix: %i%e%d -#======================================================================= -logprefix: %t%e%d - -#======================================================================= -# LOG CONTROLS -# -# Bochs has four severity levels for event logging. -# panic: cannot proceed. If you choose to continue after a panic, -# don't be surprised if you get strange behavior or crashes. -# error: something went wrong, but it is probably safe to continue the -# simulation. -# info: interesting or useful messages. -# debug: messages useful only when debugging the code. This may -# spit out thousands per second. -# -# For events of each level, you can choose to exit Bochs ('fatal'), 'ask', -# 'warn', 'report' or 'ignore'. The choices 'ask' and 'warn' are not supported -# by all guis, since they should bring up a dialog box. The 'warn' dialog is -# designed to confirm errors and the 'ask' dialog is usually used for panics -# and asks the user how to proceed. -# -# It is also possible to specify the 'action' to do for each Bochs facility -# separately (e.g. crash on panics from everything except the cdrom, and only -# report those). See the 'log function' module list in the user documentation. -# -# If you are experiencing many panics, it can be helpful to change -# the panic action to report instead of fatal. However, be aware -# that anything executed after a panic is uncharted territory and can -# cause bochs to become unstable. The panic is a "graceful exit," so -# if you disable it you may get a spectacular disaster instead. -#======================================================================= -panic: action=fatal -error: action=ignore -info: action=ignore -debug: action=ignore #, pci=report # report BX_DEBUG from module 'pci' - -#======================================================================= -# DEBUGGER_LOG: -# Give the path of the log file you'd like Bochs to log debugger output. -# If you really don't want it, make it /dev/null or '-'. :^( -# -# Examples: -# debugger_log: ./debugger.out -#======================================================================= -#debugger_log: /dev/null -#debugger_log: debugger.out -#debugger_log: - -debugger_log: /dev/null - -#======================================================================= -# COM1, COM2, COM3, COM4: -# This defines a serial port (UART type 16550A). In the 'term' mode you can -# specify a device to use as com1. This can be a real serial line, or a pty. -# To use a pty (under X/Unix), create two windows (xterms, usually). One of -# them will run bochs, and the other will act as com1. Find out the tty the com1 -# window using the `tty' command, and use that as the `dev' parameter. -# Then do `sleep 1000000' in the com1 window to keep the shell from -# messing with things, and run bochs in the other window. Serial I/O to -# com1 (port 0x3f8) will all go to the other window. -# In socket* and pipe* (win32 only) modes Bochs becomes either socket/named pipe -# client or server. In client mode it connects to an already running server (if -# connection fails Bochs treats com port as not connected). In server mode it -# opens socket/named pipe and waits until a client application connects to it -# before starting simulation. This mode is useful for remote debugging (e.g. -# with gdb's "target remote host:port" command or windbg's command line option -# -k com:pipe,port=\\.\pipe\pipename). Socket modes use simple TCP communication, -# pipe modes use duplex byte mode pipes. -# Other serial modes are 'null' (no input/output), 'file' (output to a file -# specified as the 'dev' parameter and changeable at runtime), 'raw' (use the -# real serial port - partly implemented on win32), 'mouse' (standard serial -# mouse - requires mouse option setting 'type=serial', 'type=serial_wheel' or -# 'type=serial_msys'). -# -# Examples: -# com1: enabled=1, mode=null -# com1: enabled=1, mode=mouse -# com2: enabled=1, mode=file, dev=serial.out -# com3: enabled=1, mode=raw, dev=com1 -# com3: enabled=1, mode=socket-client, dev=localhost:8888 -# com3: enabled=1, mode=socket-server, dev=localhost:8888 -# com4: enabled=1, mode=pipe-client, dev=\\.\pipe\mypipe -# com4: enabled=1, mode=pipe-server, dev=\\.\pipe\mypipe -#======================================================================= -com1: enabled=1, mode=file, dev=/proc/self/fd/4 - -#======================================================================= -# PARPORT1, PARPORT2: -# This defines a parallel (printer) port. When turned on and an output file is -# defined the emulated printer port sends characters printed by the guest OS -# into the output file. On some platforms a device filename can be used to -# send the data to the real parallel port (e.g. "/dev/lp0" on Linux, "lpt1" on -# win32 platforms). The output file can be changed at runtime. -# -# Examples: -# parport1: enabled=1, file="parport.out" -# parport2: enabled=1, file="/dev/lp0" -# parport1: enabled=0 -#======================================================================= -#parport1: enabled=1, file="parport.out" -parport1: enabled=0 - -#======================================================================= -# SOUND: -# This defines the lowlevel sound driver(s) for the wave (PCM) input / output -# and the MIDI output feature and (if necessary) the devices to be used. -# It can have several of the following properties. -# All properties are in the format sound: property=value -# -# waveoutdrv: -# This defines the driver to be used for the waveout feature. -# Possible values are 'file' (all wave data sent to file), 'dummy' (no -# output) and the platform-dependant drivers 'alsa', 'oss', 'osx', 'sdl' -# and 'win'. -# waveout: -# This defines the device to be used for wave output (if necessary) or -# the output file for the 'file' driver. -# waveindrv: -# This defines the driver to be used for the wavein feature. -# Possible values are 'dummy' (recording silence) and platform-dependent -# drivers 'alsa', 'oss', 'sdl' and 'win'. -# wavein: -# This defines the device to be used for wave input (if necessary). -# midioutdrv: -# This defines the driver to be used for the MIDI output feature. -# Possible values are 'file' (all MIDI data sent to file), 'dummy' (no -# output) and platform-dependent drivers 'alsa', 'oss', 'osx' and 'win'. -# midiout: -# This defines the device to be used for MIDI output (if necessary). -# driver: -# This defines the driver to be used for all sound features with one -# property. Possible values are 'default' (platform default) and all -# other choices described above. Overriding one or more settings with -# the specific driver parameter is possible. -# -# Example for different drivers: -# sound: waveoutdrv=sdl, waveindrv=alsa, midioutdrv=dummy -#======================================================================= -#sound: driver=default, waveout=/dev/dsp. wavein=, midiout= - -#======================================================================= -# SPEAKER: -# This defines the PC speaker output mode. In the 'sound' mode the beep -# is generated by the square wave generator which is a part of the -# lowlevel sound support. The 'system' mode is only available on Linux -# and Windows. On Linux /dev/console is used for output and on Windows -# the Beep() function. The 'gui' mode forwards the beep to the related -# gui methods (currently only used by the Carbon gui). -#======================================================================= -#speaker: enabled=0, mode=sound -speaker: enabled=0 - -#======================================================================= -# SB16: -# This defines the SB16 sound emulation. It can have several of the -# following properties. -# All properties are in the format sb16: property=value -# -# enabled: -# This optional property controls the presence of the SB16 emulation. -# The emulation is turned on unless this property is used and set to 0. -# midimode: This parameter specifies what to do with the MIDI output. -# 0 = no output -# 1 = output to device specified with the sound option (system dependent) -# 2 = MIDI or raw data output to file (depends on file name extension) -# 3 = dual output (mode 1 and 2 at the same time) -# midifile: This is the file where the midi output is stored (midimode 2 or 3). -# wavemode: This parameter specifies what to do with the PCM output. -# 0 = no output -# 1 = output to device specified with the sound option (system dependent) -# 2 = VOC, WAV or raw data output to file (depends on file name extension) -# 3 = dual output (mode 1 and 2 at the same time) -# wavefile: This is the file where the wave output is stored (wavemode 2 or 3). -# loglevel: -# 0=no log -# 1=resource changes, midi program and bank changes -# 2=severe errors -# 3=all errors -# 4=all errors plus all port accesses -# 5=all errors and port accesses plus a lot of extra info -# log: The file to write the sb16 emulator messages to. -# dmatimer: -# microseconds per second for a DMA cycle. Make it smaller to fix -# non-continuous sound. 750000 is usually a good value. This needs a -# reasonably correct setting for the IPS parameter of the CPU option. -# -# Examples for output modes: -# sb16: midimode=2, midifile="output.mid", wavemode=1 # MIDI to file -# sb16: midimode=1, wavemode=3, wavefile="output.wav" # wave to file and device -#======================================================================= -#sb16: midimode=1, wavemode=1, loglevel=2, log=sb16.log, dmatimer=600000 -#sb16: enabled=0 - -#======================================================================= -# ES1370: -# This defines the ES1370 sound emulation (recording and playback - except -# DAC1+DAC2 output at the same time). The parameter 'enabled' controls the -# presence of the device. The wave and MIDI output can be sent to device, file -# or both using the parameters 'wavemode', 'wavefile', 'midimode' and -# 'midifile'. See the description of these parameters at the SB16 directive. -# -# Examples: -# es1370: enabled=1, wavemode=1 # use 'sound' parameters -# es1370: enabled=1, wavemode=2, wavefile=output.voc # send output to file -#======================================================================= -#es1370: enabled=1, wavemode=1 -#es1370: enabled=0 - -#======================================================================= -# ne2k: NE2000 compatible ethernet adapter -# -# Format: -# ne2k: enabled=1, ioaddr=IOADDR, irq=IRQ, mac=MACADDR, ethmod=MODULE, -# ethdev=DEVICE, script=SCRIPT, bootrom=BOOTROM -# -# IOADDR, IRQ: You probably won't need to change ioaddr and irq, unless there -# are IRQ conflicts. These arguments are ignored when assign the ne2k to a -# PCI slot. -# -# MAC: The MAC address MUST NOT match the address of any machine on the net. -# Also, the first byte must be an even number (bit 0 set means a multicast -# address), and you cannot use ff:ff:ff:ff:ff:ff because that's the broadcast -# address. For the ethertap module, you must use fe:fd:00:00:00:01. There may -# be other restrictions too. To be safe, just use the b0:c4... address. -# -# ETHDEV: The ethdev value is the name of the network interface on your host -# platform. On UNIX machines, you can get the name by running ifconfig. On -# Windows machines, you must run niclist to get the name of the ethdev. -# Niclist source code is in misc/niclist.c and it is included in Windows -# binary releases. -# The 'socket' module uses this parameter to specify the UDP port for -# receiving packets and (optional) the host to connect. -# -# SCRIPT: The script value is optional, and is the name of a script that -# is executed after bochs initialize the network interface. You can use -# this script to configure this network interface, or enable masquerading. -# This is mainly useful for the tun/tap devices that only exist during -# Bochs execution. The network interface name is supplied to the script -# as first parameter. -# The 'slirp' module uses this parameter to specify a config file for -# setting up an alternative IP configuration or additional features. -# The 'vnet' module uses this parameter to specify an alternative -# log file name. -# -# BOOTROM: The bootrom value is optional, and is the name of the ROM image -# to load. Note that this feature is only implemented for the PCI version of -# the NE2000. -# -# If you don't want to make connections to any physical networks, -# you can use the following 'ethmod's to simulate a virtual network. -# null: All packets are discarded, but logged to a few files. -# vde: Virtual Distributed Ethernet -# vnet: ARP, ICMP-echo(ping), DHCP and read/write TFTP are simulated. -# The virtual host uses 192.168.10.1. -# DHCP assigns 192.168.10.2 to the guest. -# TFTP uses the 'ethdev' value for the root directory and doesn't -# overwrite files. -# socket: Connect up to 6 Bochs instances with external program 'bxhub' -# (simulating an ethernet hub). It provides the same services as the -# 'vnet' module and assigns IP addresses like 'slirp' (10.0.2.x). -# -#======================================================================= -# ne2k: ioaddr=0x300, irq=9, mac=fe:fd:00:00:00:01, ethmod=fbsd, ethdev=en0 #macosx -# ne2k: ioaddr=0x300, irq=9, mac=b0:c4:20:00:00:00, ethmod=fbsd, ethdev=xl0 -# ne2k: ioaddr=0x300, irq=9, mac=b0:c4:20:00:00:00, ethmod=linux, ethdev=eth0 -# ne2k: ioaddr=0x300, irq=9, mac=b0:c4:20:00:00:01, ethmod=win32, ethdev=MYCARD -# ne2k: ioaddr=0x300, irq=9, mac=fe:fd:00:00:00:01, ethmod=tap, ethdev=tap0 -# ne2k: ioaddr=0x300, irq=9, mac=fe:fd:00:00:00:01, ethmod=tuntap, ethdev=/dev/net/tun0, script=./tunconfig -# ne2k: ioaddr=0x300, irq=9, mac=b0:c4:20:00:00:01, ethmod=null, ethdev=eth0 -# ne2k: ioaddr=0x300, irq=9, mac=b0:c4:20:00:00:01, ethmod=vde, ethdev="/tmp/vde.ctl" -# ne2k: ioaddr=0x300, irq=9, mac=b0:c4:20:00:00:01, ethmod=vnet, ethdev="c:/temp" -# ne2k: mac=b0:c4:20:00:00:01, ethmod=socket, ethdev=40000 # use localhost -# ne2k: mac=b0:c4:20:00:00:01, ethmod=socket, ethdev=mymachine:40000 -# ne2k: mac=b0:c4:20:00:00:01, ethmod=slirp, script=slirp.conf, bootrom=ne2k_pci.rom - -#======================================================================= -# pcipnic: Bochs/Etherboot pseudo-NIC -# -# Format: -# pcipnic: enabled=1, mac=MACADDR, ethmod=MODULE, ethdev=DEVICE, script=SCRIPT, -# bootrom=BOOTROM -# -# The pseudo-NIC accepts the same syntax (for mac, ethmod, ethdev, script, -# bootrom) and supports the same networking modules as the NE2000 adapter. -#======================================================================= -#pcipnic: enabled=1, mac=b0:c4:20:00:00:00, ethmod=vnet - -#======================================================================= -# e1000: Intel(R) 82540EM Gigabit Ethernet adapter -# -# Format: -# e1000: enabled=1, mac=MACADDR, ethmod=MODULE, ethdev=DEVICE, script=SCRIPT -# bootrom=BOOTROM -# -# The E1000 accepts the same syntax (for mac, ethmod, ethdev, script, bootrom) -# and supports the same networking modules as the NE2000 adapter. -#======================================================================= -#e1000: enabled=1, mac=52:54:00:12:34:56, ethmod=slirp, script=slirp.conf - -#======================================================================= -# USB_UHCI: -# This option controls the presence of the USB root hub which is a part -# of the i440FX PCI chipset. With the portX parameter you can connect devices -# to the hub (currently supported: 'mouse', 'tablet', 'keypad', 'disk', 'cdrom', -# 'floppy', 'hub' and 'printer'). -# -# If you connect the mouse or tablet to one of the ports, Bochs forwards the -# mouse movement data to the USB device instead of the selected mouse type. -# When connecting the keypad to one of the ports, Bochs forwards the input of -# the numeric keypad to the USB device instead of the PS/2 keyboard. -# -# To connect a 'flat' mode image as a USB hardisk you can use the 'disk' device -# with the path to the image separated with a colon. To use other disk image modes -# similar to ATA disks the syntax 'disk:mode:filename' must be used (see below). -# -# To emulate a USB cdrom you can use the 'cdrom' device name and the path to -# an ISO image or raw device name also separated with a colon. An option to -# insert/eject media is available in the runtime configuration. -# -# To emulate a USB floppy you can use the 'floppy' device with the path to the -# image separated with a colon. To use the VVFAT image mode similar to the -# legacy floppy the syntax 'floppy:vvfat:directory' must be used (see below). -# An option to insert/eject media is available in the runtime configuration. -# -# The device name 'hub' connects an external hub with max. 8 ports (default: 4) -# to the root hub. To specify the number of ports you have to add the value -# separated with a colon. Connecting devices to the external hub ports is only -# available in the runtime configuration. -# -# The device 'printer' emulates the HP Deskjet 920C printer. The PCL data is -# sent to a file specified in bochsrc.txt. The current code appends the PCL -# code to the file if the file already existed. The output file can be -# changed at runtime. -# -# The optionsX parameter can be used to assign specific options to the device -# connected to the corresponding USB port. Currently this feature is used to -# set the speed reported by device ('low', 'full', 'high' or 'super'). The -# availabe speed choices depend on both HC and device. The option 'debug' turns -# on debug output for the device at connection time. -# For the USB 'disk' device the optionsX parameter can be used to specify an -# alternative redolog file (journal) of some image modes. For 'vvfat' mode USB -# disks the optionsX parameter can be used to specify the disk size (range -# 128M ... 128G). If the size is not specified, it defaults to 504M. -# For the USB 'floppy' device the optionsX parameter can be used to specify an -# alternative device ID to be reported. Currently only the model "teac" is -# supported (can fix hw detection in some guest OS). The USB floppy also -# accepts the parameter "write_protected" with valid values 0 and 1 to select -# the access mode (default is 0). -#======================================================================= -#usb_uhci: enabled=1 -#usb_uhci: enabled=1, port1=mouse, port2=disk:usbstick.img -#usb_uhci: enabled=1, port1=hub:7, port2=disk:growing:usbdisk.img -#usb_uhci: enabled=1, port2=disk:undoable:usbdisk.img, options2=journal:redo.log -#usb_uhci: enabled=1, port2=disk:usbdisk2.img, options2=sect_size:1024 -#usb_uhci: enabled=1, port2=disk:vvfat:vvfat, options2="debug,speed:full" -#usb_uhci: enabled=1, port1=printer:printdata.bin, port2=cdrom:image.iso -#usb_uhci: enabled=1, port2=floppy:vvfat:diskette, options2="model:teac" - -#======================================================================= -# USB_OHCI: -# This option controls the presence of the USB OHCI host controller with a -# 2-port hub. The portX parameter accepts the same device types with the same -# syntax as the UHCI controller (see above). The optionsX parameter is also -# available on OHCI. -#======================================================================= -#usb_ohci: enabled=1 -#usb_ohci: enabled=1, port1=printer:usbprinter.bin - -#======================================================================= -# USB_EHCI: -# This option controls the presence of the USB EHCI host controller with a -# 6-port hub. The portX parameter accepts the same device types with the -# same syntax as the UHCI controller (see above). The optionsX parameter is -# also available on EHCI. -#======================================================================= -#usb_ehci: enabled=1 - -#======================================================================= -# USB_XHCI: -# This option controls the presence of the USB xHCI host controller with a -# 4-port hub. The portX parameter accepts the same device types with the -# same syntax as the UHCI controller (see above). The optionsX parameter is -# also available on xHCI. NOTE: port 1 and 2 are USB3 and only support -# super-speed devices, but port 3 and 4 are USB2 and support speed settings -# low, full and high. -#======================================================================= -#usb_xhci: enabled=1 - -#======================================================================= -# PCIDEV: -# PCI host device mapping -# WARNING: This Bochs feature is not maintained yet and may fail. -#======================================================================= -#pcidev: vendor=0x1234, device=0x5678 - -#======================================================================= -# GDBSTUB: -# Enable GDB stub. See user documentation for details. -# Default value is enabled=0. -# WARNING: This Bochs feature is not maintained yet and may fail. -#======================================================================= -#gdbstub: enabled=0, port=1234, text_base=0, data_base=0, bss_base=0 - -#======================================================================= -# MAGIC_BREAK: -# This enables the "magic breakpoint" feature when using the debugger. -# The useless cpu instruction XCHG BX, BX causes Bochs to enter the -# debugger mode. This might be useful for software development. -# -# Example: -# magic_break: enabled=1 -#======================================================================= -magic_break: enabled=0 - -#======================================================================= -# DEBUG_SYMBOLS: -# This loads symbols from the specified file for use in Bochs' internal -# debugger. Symbols are loaded into global context. This is equivalent to -# issuing ldsym debugger command at start up. -# -# Example: -# debug_symbols: file="kernel.sym" -# debug_symbols: file="kernel.sym", offset=0x80000000 -#======================================================================= -#debug_symbols: file="kernel.sym" - -print_timestamps: enabled=1 - -#======================================================================= -# PORT_E9_HACK: -# The 0xE9 port doesn't exists in normal ISA architecture. However, we -# define a convention here, to display on the console of the system running -# Bochs anything that is written to it. The idea is to provide debug output -# very early when writing BIOS or OS code for example, without having to -# bother with setting up a serial port or etc. Reading from port 0xE9 will -# will return 0xe9 to let you know if the feature is available. -# Leave this 0 unless you have a reason to use it. -# -# Example: -# port_e9_hack: enabled=1 -#======================================================================= -#port_e9_hack: enabled=1 - -#======================================================================= -# other stuff -#======================================================================= -# WARNING: This Bochs feature is not maintained yet. Is it still used ? -# To use it, set BX_LOAD32BITOSHACK in config.h to 1 and recompile Bochs. -#load32bitOSImage: os=nullkernel, path=../kernel.img, iolog=../vga_io.log -#load32bitOSImage: os=linux, path=../linux.img, iolog=../vga_io.log, initrd=../initrd.img - -#======================================================================= -# fullscreen: ONLY IMPLEMENTED ON AMIGA -# Request that Bochs occupy the entire screen instead of a -# window. -# -# Examples: -# fullscreen: enabled=0 -# fullscreen: enabled=1 -#======================================================================= -#fullscreen: enabled=0 -#screenmode: name="sample" - -#======================================================================= -# USER_PLUGIN: -# Load user-defined plugin. This option is available only if Bochs is -# compiled with plugin support. Maximum 8 different plugins are supported. -# See the example in the Bochs sources how to write a plugin device. -#======================================================================= -#user_plugin: name=testdev - -#======================================================================= -# for Macintosh, use the style of pathnames in the following -# examples. -# -# vgaromimage: :bios:VGABIOS-elpin-2.40 -# romimage: file=:bios:BIOS-bochs-latest, address=0xf0000 -# floppya: 1_44=[fd:], status=inserted -#======================================================================= -#:bios:VGABIOS-elpin-2.40 - -#======================================================================= -# MEGS -# Set the number of Megabytes of physical memory you want to emulate. -# The default is 32MB, most OS's won't need more than that. -# The maximum amount of memory supported is 2048Mb. -# The 'MEGS' option is deprecated. Use 'MEMORY' option instead. -#======================================================================= -#megs: 256 -#megs: 128 -#megs: 64 -#megs: 32 -#megs: 16 -#megs: 8 diff --git a/build/bootstrap/compile.com b/build/bootstrap/compile.com index 8ca98c4da7a7e9827da3da63d28755a2803d8549..6c337da07e4dfc00b5c6c4191a9dac2aaa012dbc 100755 GIT binary patch delta 22667 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 EUy$)W^esr-uJiecmMhBS{IjRS3SFG*RHBv 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 Toc}+&w)p0&OxK7PvPS<4%VLQu diff --git a/build/bootstrap/zipobj.com b/build/bootstrap/zipobj.com index 5947dda14d6da135f4484e232c773e114d6ef0ff..4b2a1564612aa246feadd264e730a2af4d0aaa2f 100755 GIT binary patch delta 58901 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 z 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, addr->ai_socktype, - addr->ai_protocol))); + CHECK_NE(-1, (sock = GoodSocket(addr->ai_family, addr->ai_socktype, + addr->ai_protocol, false, + &(struct timeval){-60}))); CHECK_NE(-1, connect(sock, addr->ai_addr, addr->ai_addrlen)); freeaddrinfo(addr); if (usessl) { diff --git a/libc/calls/addrusage.c b/libc/calls/addrusage.c new file mode 100644 index 000000000..41cd654ed --- /dev/null +++ b/libc/calls/addrusage.c @@ -0,0 +1,39 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ 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. │ +│ │ +│ 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/math.h" +#include "libc/macros.internal.h" + +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; +} diff --git a/libc/calls/addtimeval.c b/libc/calls/addtimeval.c new file mode 100644 index 000000000..38ae4e369 --- /dev/null +++ b/libc/calls/addtimeval.c @@ -0,0 +1,28 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ 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. │ +│ │ +│ 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/math.h" + +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; + } +} diff --git a/libc/calls/math.h b/libc/calls/math.h new file mode 100644 index 000000000..e99faa9db --- /dev/null +++ b/libc/calls/math.h @@ -0,0 +1,13 @@ +#ifndef COSMOPOLITAN_LIBC_CALLS_MATH_H_ +#define COSMOPOLITAN_LIBC_CALLS_MATH_H_ +#include "libc/calls/struct/rusage.h" +#include "libc/calls/struct/timeval.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +void AddTimeval(struct timeval *, const struct timeval *); +void AddRusage(struct rusage *, const struct rusage *); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_CALLS_MATH_H_ */ diff --git a/libc/fmt/conv.h b/libc/fmt/conv.h index 7b4f47d8f..ae10df609 100644 --- a/libc/fmt/conv.h +++ b/libc/fmt/conv.h @@ -39,6 +39,7 @@ long sizetol(const char *, long) paramsnonnull() libcesque; │ cosmopolitan § conversion » time ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ +struct timespec WindowsTimeToTime(uint64_t); int64_t DosDateTimeToUnix(unsigned, unsigned); struct timespec FileTimeToTimeSpec(struct NtFileTime); struct NtFileTime TimeSpecToFileTime(struct timespec); @@ -48,6 +49,11 @@ void FileTimeToTimeVal(struct timeval *, struct NtFileTime) nothrow; struct NtFileTime TimeValToFileTime(const struct timeval *) nosideeffect; long convertmicros(const struct timeval *, long) paramsnonnull() nosideeffect; +/* forceinline struct timespec WindowsTimeToTime(uint64_t x) { */ +/* return (struct timespec){x / HECTONANOSECONDS - MODERNITYSECONDS, */ +/* x % HECTONANOSECONDS * 100}; */ +/* } */ + /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § conversion » manipulation ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ diff --git a/libc/fmt/fmt.mk b/libc/fmt/fmt.mk index 221f6367c..b91b201f8 100644 --- a/libc/fmt/fmt.mk +++ b/libc/fmt/fmt.mk @@ -58,6 +58,7 @@ $(LIBC_FMT_A_OBJS): \ OVERRIDE_CFLAGS += \ -fno-jump-tables +o/$(MODE)/libc/fmt/windowstimetotime.o \ o/$(MODE)/libc/fmt/dosdatetimetounix.o \ o/$(MODE)/libc/fmt/itoa64radix10.greg.o \ o/$(MODE)/libc/fmt/timetofiletime.o \ diff --git a/libc/sock/goodsocket.c b/libc/sock/goodsocket.c new file mode 100644 index 000000000..6ead34788 --- /dev/null +++ b/libc/sock/goodsocket.c @@ -0,0 +1,59 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ 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. │ +│ │ +│ 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/struct/timeval.h" +#include "libc/sock/goodsocket.internal.h" +#include "libc/sock/sock.h" +#include "libc/sysv/consts/so.h" +#include "libc/sysv/consts/sol.h" +#include "libc/sysv/consts/tcp.h" + +static bool Tune(int fd, int a, int b, int x) { + if (!b) return false; + return setsockopt(fd, a, b, &x, sizeof(x)) != -1; +} + +/** + * Returns new socket with modern goodness enabled. + */ +int GoodSocket(int family, int type, int protocol, bool isserver, + const struct timeval *timeout) { + 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) { + 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; +} diff --git a/libc/sock/goodsocket.internal.h b/libc/sock/goodsocket.internal.h new file mode 100644 index 000000000..8c2509893 --- /dev/null +++ b/libc/sock/goodsocket.internal.h @@ -0,0 +1,11 @@ +#ifndef COSMOPOLITAN_LIBC_SOCK_GOODSOCKET_H_ +#define COSMOPOLITAN_LIBC_SOCK_GOODSOCKET_H_ +#include "libc/calls/struct/timeval.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +int GoodSocket(int, int, int, bool, const struct timeval *); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_SOCK_GOODSOCKET_H_ */ diff --git a/libc/str/compareslices.c b/libc/str/compareslices.c new file mode 100644 index 000000000..32346593f --- /dev/null +++ b/libc/str/compareslices.c @@ -0,0 +1,28 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ 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. │ +│ │ +│ 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/macros.internal.h" +#include "libc/str/slice.h" + +int CompareSlices(const char *a, size_t n, const char *b, size_t m) { + int c; + if ((c = memcmp(a, b, MIN(n, m)))) return c; + if (n < m) return -1; + if (n > m) return +1; + return 0; +} diff --git a/libc/str/compareslicescase.c b/libc/str/compareslicescase.c new file mode 100644 index 000000000..a1b7a6e1b --- /dev/null +++ b/libc/str/compareslicescase.c @@ -0,0 +1,28 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ 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. │ +│ │ +│ 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/macros.internal.h" +#include "libc/str/slice.h" + +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; +} diff --git a/libc/str/getzipcfiletimestamps.c b/libc/str/getzipcfiletimestamps.c new file mode 100644 index 000000000..af6bd3d98 --- /dev/null +++ b/libc/str/getzipcfiletimestamps.c @@ -0,0 +1,101 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ 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. │ +│ │ +│ 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/conv.h" +#include "libc/zip.h" + +static inline int pop(int x) { + return !!(x & 1) + !!(x & 2) + !!(x & 4); +} + +/** + * Extracts modified/access/creation timestamps from zip entry. + * + * @param cf is pointer to central directory header for file + * @param mtim optionally receives last modified timestamp + * @param atim optionally receives modified timestamp + * @param ctim optionally receives creation timestamp + * @param gmtoff is seconds adjustment for legacy dos timestamps + */ +void GetZipCfileTimestamps(const uint8_t *cf, struct timespec *mtim, + struct timespec *atim, struct timespec *ctim, + int gmtoff) { + const uint8_t *p, *pe; + if (mtim) *mtim = (struct timespec){0}; + if (atim) *atim = (struct timespec){0}; + if (ctim) *ctim = (struct timespec){0}; + for (p = ZIP_CFILE_EXTRA(cf), pe = p + ZIP_CFILE_EXTRASIZE(cf); p + 4 <= pe; + p += ZIP_EXTRA_SIZE(p)) { + if (ZIP_EXTRA_HEADERID(p) == kZipExtraNtfs && + ZIP_EXTRA_CONTENTSIZE(p) >= 4 + 4 + 8 && + READ16LE(ZIP_EXTRA_CONTENT(p) + 4) == 1 && + READ16LE(ZIP_EXTRA_CONTENT(p) + 6) >= 8) { + if (mtim) { + *mtim = WindowsTimeToTime(READ64LE(ZIP_EXTRA_CONTENT(p) + 8)); + } + if (atim && ZIP_EXTRA_CONTENTSIZE(p) >= 4 + 4 + 8 * 2 && + READ16LE(ZIP_EXTRA_CONTENT(p) + 6) >= 16) { + *atim = WindowsTimeToTime(READ64LE(ZIP_EXTRA_CONTENT(p) + 8 * 2)); + } + if (ctim && ZIP_EXTRA_CONTENTSIZE(p) >= 4 + 4 + 8 * 3 && + READ16LE(ZIP_EXTRA_CONTENT(p) + 6) >= 24) { + *ctim = WindowsTimeToTime(READ64LE(ZIP_EXTRA_CONTENT(p) + 8 * 3)); + } + return; + } + } + for (p = ZIP_CFILE_EXTRA(cf), pe = p + ZIP_CFILE_EXTRASIZE(cf); p + 4 <= pe; + p += ZIP_EXTRA_SIZE(p)) { + if (ZIP_EXTRA_HEADERID(p) == kZipExtraExtendedTimestamp && + ZIP_EXTRA_CONTENTSIZE(p) > 1 && + ZIP_EXTRA_CONTENTSIZE(p) == 1 + 4 * pop(*ZIP_EXTRA_CONTENT(p) & 7)) { + if (mtim) { + if (*ZIP_EXTRA_CONTENT(p) & 1) { + mtim->tv_sec = (int32_t)READ32LE(ZIP_EXTRA_CONTENT(p) + 1); + } else { + mtim->tv_sec = DosDateTimeToUnix(ZIP_CFILE_LASTMODIFIEDDATE(cf), + ZIP_CFILE_LASTMODIFIEDTIME(cf)) - + gmtoff; + } + } + if (atim && (*ZIP_EXTRA_CONTENT(p) & 2)) { + atim->tv_sec = (int32_t)READ32LE(ZIP_EXTRA_CONTENT(p) + 1 + + 4 * (*ZIP_EXTRA_CONTENT(p) & 1)); + } + if (ctim && (*ZIP_EXTRA_CONTENT(p) & 4)) { + ctim->tv_sec = (int32_t)READ32LE(ZIP_EXTRA_CONTENT(p) + 1 + + 4 * pop(*ZIP_EXTRA_CONTENT(p) & 3)); + } + return; + } + } + for (p = ZIP_CFILE_EXTRA(cf), pe = p + ZIP_CFILE_EXTRASIZE(cf); p + 4 <= pe; + p += ZIP_EXTRA_SIZE(p)) { + if (ZIP_EXTRA_HEADERID(p) == kZipExtraUnix && + ZIP_EXTRA_CONTENTSIZE(p) >= 4 + 4) { + if (atim) atim->tv_sec = (int32_t)READ32LE(ZIP_EXTRA_CONTENT(p) + 0); + if (mtim) mtim->tv_sec = (int32_t)READ32LE(ZIP_EXTRA_CONTENT(p) + 4); + return; + } + } + if (mtim) { + mtim->tv_sec = DosDateTimeToUnix(ZIP_CFILE_LASTMODIFIEDDATE(cf), + ZIP_CFILE_LASTMODIFIEDTIME(cf)) - + gmtoff; + } +} diff --git a/libc/str/istext.c b/libc/str/istext.c new file mode 100644 index 000000000..0a75f6021 --- /dev/null +++ b/libc/str/istext.c @@ -0,0 +1,32 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ 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. │ +│ │ +│ 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +/** + * Returns true if buffer is most likely plaintext. + */ +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; +} diff --git a/libc/str/isutf8.c b/libc/str/isutf8.c new file mode 100644 index 000000000..983ce7282 --- /dev/null +++ b/libc/str/isutf8.c @@ -0,0 +1,38 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ 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. │ +│ │ +│ 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +/** + * Returns true if text data is most likely utf-8. + * + * This function will return false if a pure ascii string is passed. + */ +bool IsUtf8(const void *data, size_t size) { + const unsigned char *p, *pe; + for (p = data, pe = p + size; p + 2 <= pe; ++p) { + if (p[0] >= 0300) { + if (p[1] >= 0200 && p[1] < 0300) { + return true; + } else { + return false; + } + } + } + return false; +} diff --git a/libc/str/slice.h b/libc/str/slice.h new file mode 100644 index 000000000..a7ced4564 --- /dev/null +++ b/libc/str/slice.h @@ -0,0 +1,22 @@ +#ifndef COSMOPOLITAN_LIBC_STR_SLICE_H_ +#define COSMOPOLITAN_LIBC_STR_SLICE_H_ +#include "libc/str/str.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +forceinline bool SlicesEqual(const char *a, size_t n, const char *b, size_t m) { + return n == m && !memcmp(a, b, n); +} + +forceinline bool SlicesEqualCase(const void *a, size_t n, const void *b, + size_t m) { + return n == m && !memcasecmp(a, b, n); +} + +int CompareSlices(const char *, size_t, const char *, size_t); +int CompareSlicesCase(const char *, size_t, const char *, size_t); +bool StartsWithIgnoreCase(const char *, const char *); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_STR_SLICE_H_ */ diff --git a/libc/str/startswithi.c b/libc/str/startswithi.c new file mode 100644 index 000000000..737280807 --- /dev/null +++ b/libc/str/startswithi.c @@ -0,0 +1,27 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ 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. │ +│ │ +│ 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +bool startswithi(const char *s, const char *prefix) { + for (;;) { + if (!*prefix) return true; + if (!*s) return false; + if (kToLower[*s++ & 255] != (*prefix++ & 255)) return false; + } +} diff --git a/libc/str/str.h b/libc/str/str.h index c864e5842..fede6a58d 100644 --- a/libc/str/str.h +++ b/libc/str/str.h @@ -171,6 +171,7 @@ wchar_t *wcstok(wchar_t *, const wchar_t *, wchar_t **) paramsnonnull((2, 3)); char *wstrtrunc(uint16_t *) memcpyesque; char *wstrntrunc(uint16_t *, size_t) memcpyesque; bool startswith(const char *, const char *) strlenesque; +bool startswithi(const char *, const char *) strlenesque; bool startswith16(const char16_t *, const char16_t *) strlenesque; bool wcsstartswith(const wchar_t *, const wchar_t *) strlenesque; bool endswith(const char *, const char *) strlenesque; @@ -197,6 +198,8 @@ char *strtoupper(char *) paramsnonnull(); char *chomp(char *); char16_t *chomp16(char16_t *); wchar_t *wchomp(wchar_t *); +bool IsText(const void *, size_t); +bool IsUtf8(const void *, size_t); bool escapedos(char16_t *, unsigned, const char16_t *, unsigned); diff --git a/libc/str/str.mk b/libc/str/str.mk index 1b47c9e96..e40f81e0e 100644 --- a/libc/str/str.mk +++ b/libc/str/str.mk @@ -48,6 +48,14 @@ o/$(MODE)/libc/str/memmem.o: \ OVERRIDE_CPPFLAGS += \ -DSTACK_FRAME_UNLIMITED +o//libc/str/bzero.o: \ + OVERRIDE_CFLAGS += \ + -O2 + +o/$(MODE)/libc/str/dosdatetimetounix.o: \ + OVERRIDE_CFLAGS += \ + -O3 + o/$(MODE)/libc/str/getzipcdir.o \ o/$(MODE)/libc/str/getzipcdircomment.o \ o/$(MODE)/libc/str/getzipcdircommentsize.o \ @@ -58,20 +66,21 @@ o/$(MODE)/libc/str/getzipcfilemode.o \ o/$(MODE)/libc/str/getzipcfileoffset.o \ o/$(MODE)/libc/str/getzipcfileuncompressedsize.o \ o/$(MODE)/libc/str/getziplfilecompressedsize.o \ -o/$(MODE)/libc/str/getziplfileuncompressedsize.o: \ +o/$(MODE)/libc/str/getziplfileuncompressedsize.o \ +o/$(MODE)/libc/str/getzipcfiletimestamps.o: \ OVERRIDE_CFLAGS += \ -Os -o//libc/str/bzero.o: \ - OVERRIDE_CFLAGS += \ - -O2 - o/$(MODE)/libc/str/iswpunct.o \ o/$(MODE)/libc/str/iswupper.o \ o/$(MODE)/libc/str/iswlower.o: \ OVERRIDE_CFLAGS += \ -fno-jump-tables +o/$(MODE)/libc/str/windowstimetotime.o: \ + OVERRIDE_CFLAGS += \ + -O3 + LIBC_STR_LIBS = $(foreach x,$(LIBC_STR_ARTIFACTS),$($(x))) LIBC_STR_SRCS = $(foreach x,$(LIBC_STR_ARTIFACTS),$($(x)_SRCS)) LIBC_STR_HDRS = $(foreach x,$(LIBC_STR_ARTIFACTS),$($(x)_HDRS)) diff --git a/libc/str/windowstimetotime.c b/libc/str/windowstimetotime.c new file mode 100644 index 000000000..1e36ad328 --- /dev/null +++ b/libc/str/windowstimetotime.c @@ -0,0 +1,24 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ 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. │ +│ │ +│ 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/conv.h" + +struct timespec WindowsTimeToTime(uint64_t x) { + return (struct timespec){x / HECTONANOSECONDS - MODERNITYSECONDS, + x % HECTONANOSECONDS * 100}; +} diff --git a/libc/zip.h b/libc/zip.h index 359a5298e..be640422d 100644 --- a/libc/zip.h +++ b/libc/zip.h @@ -1,6 +1,7 @@ #ifndef COSMOPOLITAN_LIBC_ZIP_H_ #define COSMOPOLITAN_LIBC_ZIP_H_ #include "libc/bits/bits.h" +#include "libc/calls/struct/timespec.h" #include "libc/macros.internal.h" #include "libc/str/str.h" @@ -199,6 +200,8 @@ uint64_t GetZipCfileOffset(const uint8_t *); uint64_t GetZipLfileUncompressedSize(const uint8_t *); uint64_t GetZipLfileCompressedSize(const uint8_t *); uint8_t *zipfindcentraldir(const uint8_t *, size_t); +void GetZipCfileTimestamps(const uint8_t *, struct timespec *, + struct timespec *, struct timespec *, int); #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_ZIP_H_ */ diff --git a/libc/zipos/stat-impl.c b/libc/zipos/stat-impl.c index 702737c63..4d8a28451 100644 --- a/libc/zipos/stat-impl.c +++ b/libc/zipos/stat-impl.c @@ -18,75 +18,12 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/safemacros.internal.h" #include "libc/calls/calls.h" -#include "libc/calls/struct/stat.h" #include "libc/fmt/conv.h" #include "libc/str/str.h" #include "libc/sysv/errfuns.h" #include "libc/zip.h" #include "libc/zipos/zipos.internal.h" -static int popcnt3(int x) { - return !!(x & 1) + !!(x & 2) + !!(x & 4); -} - -static struct timespec WindowsTimeToTime(uint64_t x) { - return (struct timespec){x / HECTONANOSECONDS - MODERNITYSECONDS, - x % HECTONANOSECONDS * 100}; -} - -static void GetZipCfileTimestamps(const uint8_t *zcf, struct stat *st) { - 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 && - READ16LE(ZIP_EXTRA_CONTENT(p) + 4) == 1 && - READ16LE(ZIP_EXTRA_CONTENT(p) + 6) >= 8) { - st->st_mtim = WindowsTimeToTime(READ64LE(ZIP_EXTRA_CONTENT(p) + 8)); - if (ZIP_EXTRA_CONTENTSIZE(p) >= 4 + 4 + 8 * 2 && - READ16LE(ZIP_EXTRA_CONTENT(p) + 6) >= 16) { - st->st_atim = WindowsTimeToTime(READ64LE(ZIP_EXTRA_CONTENT(p) + 8 * 2)); - } - if (ZIP_EXTRA_CONTENTSIZE(p) >= 4 + 4 + 8 * 3 && - READ16LE(ZIP_EXTRA_CONTENT(p) + 6) >= 24) { - st->st_ctim = WindowsTimeToTime(READ64LE(ZIP_EXTRA_CONTENT(p) + 8 * 3)); - } - return; - } - } - 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_CONTENTSIZE(p) == - 1 + 4 * popcnt3(*ZIP_EXTRA_CONTENT(p) & 7)) { - if (*ZIP_EXTRA_CONTENT(p) & 1) { - st->st_mtim.tv_sec = READ32LE(ZIP_EXTRA_CONTENT(p) + 1); - } - if (*ZIP_EXTRA_CONTENT(p) & 2) { - st->st_atim.tv_sec = READ32LE(ZIP_EXTRA_CONTENT(p) + 1 + - 4 * (*ZIP_EXTRA_CONTENT(p) & 1)); - } - if (*ZIP_EXTRA_CONTENT(p) & 4) { - st->st_ctim.tv_sec = READ32LE(ZIP_EXTRA_CONTENT(p) + 1 + - 4 * popcnt3(*ZIP_EXTRA_CONTENT(p) & 3)); - } - return; - } - } - 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) { - st->st_atim.tv_sec = (int32_t)READ32LE(ZIP_EXTRA_CONTENT(p) + 0); - st->st_mtim.tv_sec = (int32_t)READ32LE(ZIP_EXTRA_CONTENT(p) + 4); - return; - } - } - st->st_mtim.tv_sec = DosDateTimeToUnix(ZIP_CFILE_LASTMODIFIEDDATE(zcf), - ZIP_CFILE_LASTMODIFIEDTIME(zcf)); -} - int __zipos_stat_impl(struct Zipos *zipos, size_t cf, struct stat *st) { size_t lf; if (zipos && st) { @@ -100,7 +37,8 @@ int __zipos_stat_impl(struct Zipos *zipos, size_t cf, struct stat *st) { st->st_size = GetZipLfileUncompressedSize(zipos->map + lf); st->st_blocks = roundup(GetZipLfileCompressedSize(zipos->map + lf), 512) / 512; - GetZipCfileTimestamps(zipos->map + cf, st); + GetZipCfileTimestamps(zipos->map + cf, &st->st_mtim, &st->st_atim, + &st->st_ctim, 0); return 0; } else { return einval(); diff --git a/net/https/certhashost.c b/net/https/certhashost.c new file mode 100644 index 000000000..efabb542d --- /dev/null +++ b/net/https/certhashost.c @@ -0,0 +1,32 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ 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. │ +│ │ +│ 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/slice.h" +#include "net/https/https.h" + +bool CertHasHost(const mbedtls_x509_crt *cert, const void *s, size_t n) { + const mbedtls_x509_sequence *cur; + for (cur = &cert->subject_alt_names; cur; cur = cur->next) { + if ((cur->buf.tag & MBEDTLS_ASN1_TAG_VALUE_MASK) == + MBEDTLS_X509_SAN_DNS_NAME && + SlicesEqualCase(s, n, cur->buf.p, cur->buf.len)) { + return true; + } + } + return false; +} diff --git a/net/https/certhasip.c b/net/https/certhasip.c new file mode 100644 index 000000000..e229037ba --- /dev/null +++ b/net/https/certhasip.c @@ -0,0 +1,32 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ 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. │ +│ │ +│ 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/bits.h" +#include "net/https/https.h" + +bool CertHasIp(const mbedtls_x509_crt *cert, uint32_t ip) { + const mbedtls_x509_sequence *cur; + for (cur = &cert->subject_alt_names; cur; cur = cur->next) { + if ((cur->buf.tag & MBEDTLS_ASN1_TAG_VALUE_MASK) == + MBEDTLS_X509_SAN_IP_ADDRESS && + cur->buf.len == 4 && ip == READ32BE(cur->buf.p)) { + return true; + } + } + return false; +} diff --git a/net/https/chaincertificate.c b/net/https/chaincertificate.c new file mode 100644 index 000000000..6825dc60b --- /dev/null +++ b/net/https/chaincertificate.c @@ -0,0 +1,35 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ 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. │ +│ │ +│ 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/log/log.h" +#include "libc/runtime/gc.internal.h" +#include "net/https/https.h" + +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; + } +} diff --git a/net/https/choosecertificatelifetime.c b/net/https/choosecertificatelifetime.c new file mode 100644 index 000000000..310f34649 --- /dev/null +++ b/net/https/choosecertificatelifetime.c @@ -0,0 +1,33 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ 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. │ +│ │ +│ 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/time/struct/tm.h" +#include "libc/time/time.h" +#include "net/https/https.h" + +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; + FormatSslTime(notbefore, gmtime_r(&past, &tm)); + FormatSslTime(notafter, gmtime_r(&future, &tm)); +} diff --git a/net/https/finishcertificate.c b/net/https/finishcertificate.c new file mode 100644 index 000000000..e94fa6710 --- /dev/null +++ b/net/https/finishcertificate.c @@ -0,0 +1,46 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ 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. │ +│ │ +│ 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/log/log.h" +#include "libc/runtime/gc.internal.h" +#include "libc/x/x.h" +#include "net/https/https.h" + +struct Cert FinishCertificate(struct Cert *ca, mbedtls_x509write_cert *wcert, + mbedtls_ctr_drbg_context *kr, + mbedtls_pk_context *key) { + int i, n, rc; + unsigned char *p; + mbedtls_x509_crt *cert; + p = malloc((n = FRAMESIZE)); + i = mbedtls_x509write_crt_der(wcert, p, n, mbedtls_ctr_drbg_random, kr); + if (i < 0) FATALF("write key (grep -0x%04x)", -i); + cert = calloc(1, sizeof(mbedtls_x509_crt)); + mbedtls_x509_crt_parse(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))) { + FATALF("generate key (grep -0x%04x)", -rc); + } + LogCertificate( + gc(xasprintf("generated %s certificate", mbedtls_pk_get_name(&cert->pk))), + cert); + return (struct Cert){cert, key}; +} diff --git a/net/https/formatx509name.c b/net/https/formatx509name.c new file mode 100644 index 000000000..b5084d8da --- /dev/null +++ b/net/https/formatx509name.c @@ -0,0 +1,26 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ 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. │ +│ │ +│ 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/log/check.h" +#include "net/https/https.h" + +char *FormatX509Name(mbedtls_x509_name *name) { + char *s = calloc(1, 1000); + CHECK_GT(mbedtls_x509_dn_gets(s, 1000, name), 0); + return s; +} diff --git a/net/https/generatecertificateserial.c b/net/https/generatecertificateserial.c new file mode 100644 index 000000000..a90decb39 --- /dev/null +++ b/net/https/generatecertificateserial.c @@ -0,0 +1,30 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ 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. │ +│ │ +│ 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "net/https/https.h" +#include "third_party/mbedtls/bignum.h" +#include "third_party/mbedtls/ctr_drbg.h" + +void GenerateCertificateSerial(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); +} diff --git a/net/https/getentropy.c b/net/https/getentropy.c new file mode 100644 index 000000000..dee1c7a5e --- /dev/null +++ b/net/https/getentropy.c @@ -0,0 +1,26 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ 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. │ +│ │ +│ 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/log/check.h" +#include "libc/rand/rand.h" +#include "net/https/https.h" + +int GetEntropy(void *c, unsigned char *p, size_t n) { + CHECK_EQ(n, getrandom(p, n, 0)); + return 0; +} diff --git a/net/https/https.h b/net/https/https.h index dbacf4651..32c9e443c 100644 --- a/net/https/https.h +++ b/net/https/https.h @@ -1,14 +1,44 @@ #ifndef COSMOPOLITAN_NET_HTTPS_HTTPS_H_ #define COSMOPOLITAN_NET_HTTPS_HTTPS_H_ #include "libc/time/struct/tm.h" +#include "third_party/mbedtls/ctr_drbg.h" +#include "third_party/mbedtls/ecp.h" +#include "third_party/mbedtls/md.h" +#include "third_party/mbedtls/pk.h" #include "third_party/mbedtls/ssl_ciphersuites.h" #include "third_party/mbedtls/x509_crt.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ +struct Cert { + mbedtls_x509_crt *cert; + mbedtls_pk_context *key; +}; + +char *TlsError(int); char *DescribeSslVerifyFailure(int); mbedtls_x509_crt *GetSslRoots(void); +void InitializeRng(mbedtls_ctr_drbg_context *); +int GetEntropy(void *, unsigned char *, size_t); void FormatSslTime(char[restrict hasatleast 16], struct tm *); +void ChooseCertificateLifetime(char[16], char[16]); +void LogCertificate(const char *, mbedtls_x509_crt *); +bool IsSelfSigned(mbedtls_x509_crt *); +char *FormatX509Name(mbedtls_x509_name *); +void TlsDie(const char *, int) wontreturn; +bool ChainCertificate(mbedtls_x509_crt *, mbedtls_x509_crt *); +bool CertHasIp(const mbedtls_x509_crt *, uint32_t); +bool CertHasHost(const mbedtls_x509_crt *, const void *, size_t); +bool CertHasCommonName(const mbedtls_x509_crt *, const void *, size_t); +bool IsServerCert(const struct Cert *, mbedtls_pk_type_t); +void TlsDebug(void *, int, const char *, int, const char *); + +void GenerateCertificateSerial(mbedtls_x509write_cert *, + mbedtls_ctr_drbg_context *); +mbedtls_pk_context *InitializeKey(struct Cert *, mbedtls_x509write_cert *, + mbedtls_md_type_t, int); +struct Cert FinishCertificate(struct Cert *, mbedtls_x509write_cert *, + mbedtls_ctr_drbg_context *, mbedtls_pk_context *); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/net/https/https.mk b/net/https/https.mk index 1b7e01ce2..35094697d 100644 --- a/net/https/https.mk +++ b/net/https/https.mk @@ -27,11 +27,13 @@ NET_HTTPS_A_DIRECTDEPS = \ LIBC_LOG \ LIBC_MEM \ LIBC_NEXGEN32E \ + LIBC_RAND \ LIBC_RUNTIME \ LIBC_STDIO \ LIBC_STR \ LIBC_STUBS \ LIBC_SYSV \ + LIBC_TIME \ LIBC_X \ LIBC_ZIPOS \ THIRD_PARTY_MBEDTLS diff --git a/net/https/initializekey.c b/net/https/initializekey.c new file mode 100644 index 000000000..22cbdcb45 --- /dev/null +++ b/net/https/initializekey.c @@ -0,0 +1,35 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ 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. │ +│ │ +│ 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/log/check.h" +#include "net/https/https.h" + +mbedtls_pk_context *InitializeKey(struct Cert *ca, + mbedtls_x509write_cert *wcert, + mbedtls_md_type_t md_alg, 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, md_alg); + 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; +} diff --git a/net/https/initializerng.c b/net/https/initializerng.c new file mode 100644 index 000000000..737af255a --- /dev/null +++ b/net/https/initializerng.c @@ -0,0 +1,30 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ 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. │ +│ │ +│ 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/log/check.h" +#include "libc/rand/rand.h" +#include "net/https/https.h" +#include "third_party/mbedtls/ctr_drbg.h" + +void InitializeRng(mbedtls_ctr_drbg_context *r) { + volatile unsigned char b[64]; + mbedtls_ctr_drbg_init(r); + CHECK(getrandom(b, 64, 0) == 64); + CHECK(!mbedtls_ctr_drbg_seed(r, GetEntropy, 0, b, 64)); + mbedtls_platform_zeroize(b, 64); +} diff --git a/net/https/isselfsigned.c b/net/https/isselfsigned.c new file mode 100644 index 000000000..79c8008ba --- /dev/null +++ b/net/https/isselfsigned.c @@ -0,0 +1,23 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ 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. │ +│ │ +│ 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "net/https/https.h" + +bool IsSelfSigned(mbedtls_x509_crt *cert) { + return !mbedtls_x509_name_cmp(&cert->issuer, &cert->subject); +} diff --git a/net/https/isservercert.c b/net/https/isservercert.c new file mode 100644 index 000000000..ea2683b74 --- /dev/null +++ b/net/https/isservercert.c @@ -0,0 +1,29 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ 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. │ +│ │ +│ 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "net/https/https.h" +#include "third_party/mbedtls/asn1.h" +#include "third_party/mbedtls/oid.h" + +bool IsServerCert(const struct Cert *cert, mbedtls_pk_type_t type) { + return cert->cert && cert->key && !cert->cert->ca_istrue && + mbedtls_pk_get_type(cert->key) == type && + !mbedtls_x509_crt_check_extended_key_usage( + cert->cert, MBEDTLS_OID_SERVER_AUTH, + MBEDTLS_OID_SIZE(MBEDTLS_OID_SERVER_AUTH)); +} diff --git a/net/https/logcertificate.c b/net/https/logcertificate.c new file mode 100644 index 000000000..cf46521e4 --- /dev/null +++ b/net/https/logcertificate.c @@ -0,0 +1,33 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ 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. │ +│ │ +│ 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/log/log.h" +#include "net/https/https.h" + +void LogCertificate(const char *msg, mbedtls_x509_crt *cert) { + char *s; + size_t n; + if (LOGGABLE(kLogDebug)) { + if ((s = malloc((n = 15000)))) { + if (mbedtls_x509_crt_info(s, n, " ", cert) > 0) { + DEBUGF("%s\n%s", msg, chomp(s)); + } + free(s); + } + } +} diff --git a/net/https/tlsdebug.c b/net/https/tlsdebug.c new file mode 100644 index 000000000..0605ceb00 --- /dev/null +++ b/net/https/tlsdebug.c @@ -0,0 +1,24 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ 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. │ +│ │ +│ 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/log/log.h" +#include "net/https/https.h" + +void TlsDebug(void *c, int v, const char *f, int l, const char *s) { + flogf(v, f, l, 0, "TLS %s", s); +} diff --git a/net/https/tlsdie.c b/net/https/tlsdie.c new file mode 100644 index 000000000..3eaee5f08 --- /dev/null +++ b/net/https/tlsdie.c @@ -0,0 +1,28 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ 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. │ +│ │ +│ 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "net/https/https.h" + +void TlsDie(const char *s, int r) { + if (IsTiny()) { + (fprintf)(stderr, "error: %s (-0x%04x %s)\n", s, -r, TlsError(r)); + } else { + (fprintf)(stderr, "error: %s (grep -0x%04x)\n", s, -r); + } + exit(1); +} diff --git a/net/https/tlserror.c b/net/https/tlserror.c new file mode 100644 index 000000000..4af0c4f80 --- /dev/null +++ b/net/https/tlserror.c @@ -0,0 +1,26 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ 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. │ +│ │ +│ 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "net/https/https.h" +#include "third_party/mbedtls/error.h" + +char *TlsError(int r) { + static char b[128]; + mbedtls_strerror(r, b, sizeof(b)); + return b; +} diff --git a/test/tool/net/redbean_test.c b/test/tool/net/redbean_test.c index c69707dcd..909a76ede 100644 --- a/test/tool/net/redbean_test.c +++ b/test/tool/net/redbean_test.c @@ -22,6 +22,7 @@ #include "libc/log/check.h" #include "libc/runtime/gc.internal.h" #include "libc/runtime/runtime.h" +#include "libc/sock/goodsocket.internal.h" #include "libc/sock/sock.h" #include "libc/stdio/stdio.h" #include "libc/sysv/consts/af.h" @@ -60,18 +61,8 @@ void SetUpOnce(void) { close(fdin); } -void Tune(int fd, int a, int b, int x) { - if (!b) return; - setsockopt(fd, a, b, &x, sizeof(x)); -} - int Socket(void) { - int fd; - if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) != -1) { - Tune(fd, IPPROTO_TCP, TCP_CORK, 0); - Tune(fd, IPPROTO_TCP, TCP_NODELAY, 1); - } - return fd; + return GoodSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, false, 0); } char *SendHttpRequest(const char *s) { diff --git a/third_party/getopt/getopt.c b/third_party/getopt/getopt.c index dee19a621..c526f0f28 100644 --- a/third_party/getopt/getopt.c +++ b/third_party/getopt/getopt.c @@ -156,9 +156,10 @@ int getopt(int nargc, char *const nargv[], const char *ostr) { /* option-argument absent */ getopt_place = kGetoptEmsg; if (*ostr == ':') return (BADARG); - if (opterr) + if (opterr) { fprintf(stderr, "%s%s%c\n", program_invocation_name, ": option requires an argument -- ", optopt); + } return (BADCH); } getopt_place = kGetoptEmsg; diff --git a/third_party/mbedtls/check.h b/third_party/mbedtls/check.inc similarity index 97% rename from third_party/mbedtls/check.h rename to third_party/mbedtls/check.inc index aebf48665..4603ae108 100644 --- a/third_party/mbedtls/check.h +++ b/third_party/mbedtls/check.inc @@ -1,5 +1,3 @@ -#ifndef MBEDTLS_CHECK_H -#define MBEDTLS_CHECK_H /* clang-format off */ #if defined(TARGET_LIKE_MBED) && \ @@ -16,10 +14,6 @@ #error "MBEDTLS_HAVE_TIME_DATE without MBEDTLS_HAVE_TIME does not make sense" #endif -#if defined(MBEDTLS_AESNI_C) && !defined(MBEDTLS_HAVE_ASM) -#error "MBEDTLS_AESNI_C defined, but not all prerequisites" -#endif - #if defined(MBEDTLS_CTR_DRBG_C) && !defined(MBEDTLS_AES_C) #error "MBEDTLS_CTR_DRBG_C defined, but not all prerequisites" #endif @@ -283,10 +277,6 @@ #error "MBEDTLS_MEMORY_DEBUG defined, but not all prerequesites" #endif -#if defined(MBEDTLS_PADLOCK_C) && !defined(MBEDTLS_HAVE_ASM) -#error "MBEDTLS_PADLOCK_C defined, but not all prerequisites" -#endif - #if defined(MBEDTLS_PEM_PARSE_C) && !defined(MBEDTLS_BASE64_C) #error "MBEDTLS_PEM_PARSE_C defined, but not all prerequisites" #endif @@ -525,11 +515,6 @@ #error "MBEDTLS_HAVE_INT32 and MBEDTLS_HAVE_INT64 cannot be defined simultaneously" #endif /* MBEDTLS_HAVE_INT32 && MBEDTLS_HAVE_INT64 */ -#if ( defined(MBEDTLS_HAVE_INT32) || defined(MBEDTLS_HAVE_INT64) ) && \ - defined(MBEDTLS_HAVE_ASM) -#error "MBEDTLS_HAVE_INT32/MBEDTLS_HAVE_INT64 and MBEDTLS_HAVE_ASM cannot be defined simultaneously" -#endif /* (MBEDTLS_HAVE_INT32 || MBEDTLS_HAVE_INT64) && MBEDTLS_HAVE_ASM */ - #if defined(MBEDTLS_SSL_PROTO_SSL3) #if defined(MBEDTLS_DEPRECATED_REMOVED) #error "MBEDTLS_SSL_PROTO_SSL3 is deprecated and will be removed in a future version of Mbed TLS" @@ -707,5 +692,3 @@ * #if defined(MBEDTLS_xxx_C) that results in empty translation units. */ typedef int mbedtls_iso_c_forbids_empty_translation_units; - -#endif /* MBEDTLS_CHECK_H */ diff --git a/third_party/mbedtls/config.h b/third_party/mbedtls/config.h index 76a21a29c..367242e09 100644 --- a/third_party/mbedtls/config.h +++ b/third_party/mbedtls/config.h @@ -109,13 +109,12 @@ #define MBEDTLS_ENTROPY_MAX_SOURCES 4 #define MBEDTLS_X509_MAX_INTERMEDIATE_CA 8 -#ifndef TINY /* * Boosts performance from 230k qps to 330k * Hardens against against sbox side channels */ #define MBEDTLS_AESNI_C -#define MBEDTLS_HAVE_ASM +#ifndef TINY #define MBEDTLS_HAVE_X86_64 #define MBEDTLS_HAVE_SSE2 #endif @@ -1263,5 +1262,5 @@ #define MBEDTLS_HAVE_UDBL #include "libc/dce.h" -#include "third_party/mbedtls/check.h" +#include "third_party/mbedtls/check.inc" #endif /* MBEDTLS_CONFIG_H_ */ diff --git a/third_party/mbedtls/ecp.c b/third_party/mbedtls/ecp.c index 557394433..39aa2c10d 100644 --- a/third_party/mbedtls/ecp.c +++ b/third_party/mbedtls/ecp.c @@ -22,6 +22,7 @@ #include "third_party/mbedtls/bignum.h" #include "third_party/mbedtls/bignum_internal.h" #include "third_party/mbedtls/common.h" +#include "third_party/mbedtls/config.h" #include "third_party/mbedtls/ctr_drbg.h" #include "third_party/mbedtls/ecp.h" #include "third_party/mbedtls/ecp_internal.h" @@ -3656,10 +3657,6 @@ cleanup: return( ret ); } -#ifndef MBEDTLS_ECP_DP_SECP192R1_ENABLED -#undef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED /* >:\ */ -#endif - /** * \brief The ECP checkup routine. * diff --git a/third_party/mbedtls/test/test_suite_asn1parse.c b/third_party/mbedtls/test/test_suite_asn1parse.c index 5aea897a9..7eb1a2034 100644 --- a/third_party/mbedtls/test/test_suite_asn1parse.c +++ b/third_party/mbedtls/test/test_suite_asn1parse.c @@ -689,7 +689,6 @@ void test_get_sequence_of( const data_t *input, int tag, mbedtls_test_set_step( step ); TEST_ASSERT( cur != NULL ); TEST_EQUAL( cur->buf.tag, tag ); - printf("yo %`'s\n", rest); n = strtoul( rest, (char **) &rest, 0 ); TEST_EQUAL( n, (size_t)( cur->buf.p - input->x ) ); ++rest; diff --git a/tool/build/build.mk b/tool/build/build.mk index 4ef7a41cf..ae345b52c 100644 --- a/tool/build/build.mk +++ b/tool/build/build.mk @@ -36,8 +36,8 @@ TOOL_BUILD_DIRECTDEPS = \ LIBC_MEM \ LIBC_NEXGEN32E \ LIBC_NT_KERNEL32 \ - LIBC_NT_WS2_32 \ LIBC_NT_USER32 \ + LIBC_NT_WS2_32 \ LIBC_RAND \ LIBC_RUNTIME \ LIBC_SOCK \ @@ -50,12 +50,13 @@ TOOL_BUILD_DIRECTDEPS = \ LIBC_TINYMATH \ LIBC_UNICODE \ LIBC_X \ + NET_HTTPS \ THIRD_PARTY_COMPILER_RT \ THIRD_PARTY_GDTOA \ THIRD_PARTY_GETOPT \ + THIRD_PARTY_MBEDTLS \ THIRD_PARTY_STB \ THIRD_PARTY_XED \ - THIRD_PARTY_MBEDTLS \ THIRD_PARTY_ZLIB \ TOOL_BUILD_LIB diff --git a/tool/build/compile.c b/tool/build/compile.c index ba48ce5ee..0ee578a76 100644 --- a/tool/build/compile.c +++ b/tool/build/compile.c @@ -33,6 +33,7 @@ #include "libc/nexgen32e/kcpuids.h" #include "libc/runtime/runtime.h" #include "libc/stdio/append.internal.h" +#include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/rlimit.h" #include "libc/sysv/consts/sig.h" @@ -353,7 +354,7 @@ int main(int argc, char *argv[]) { /* * parse prefix arguments */ - while ((opt = getopt(argc, argv, "?hntC:M:F:A:T:V:")) != -1) { + while ((opt = getopt(argc, argv, "hntC:M:F:A:T:V:")) != -1) { switch (opt) { case 'n': exit(0); @@ -378,17 +379,16 @@ int main(int argc, char *argv[]) { case 'F': fszquota = sizetol(optarg, 1000); break; - case '?': case 'h': - write(1, MANUAL, sizeof(MANUAL) - 1); + fputs(MANUAL, stdout); exit(0); default: - write(2, MANUAL, sizeof(MANUAL) - 1); + fputs(MANUAL, stderr); exit(1); } } if (optind == argc) { - write(2, MANUAL, sizeof(MANUAL) - 1); + fputs("error: missing arguments\n", stderr); exit(1); } diff --git a/tool/build/lib/buildlib.mk b/tool/build/lib/buildlib.mk index a3133eb9e..5f3012b60 100644 --- a/tool/build/lib/buildlib.mk +++ b/tool/build/lib/buildlib.mk @@ -45,6 +45,7 @@ TOOL_BUILD_LIB_A_DIRECTDEPS = \ LIBC_TINYMATH \ LIBC_UNICODE \ LIBC_X \ + NET_HTTPS \ THIRD_PARTY_COMPILER_RT \ THIRD_PARTY_MBEDTLS \ THIRD_PARTY_XED diff --git a/tool/build/lib/eztls.c b/tool/build/lib/eztls.c index f871e21d6..8815a18a1 100644 --- a/tool/build/lib/eztls.c +++ b/tool/build/lib/eztls.c @@ -25,9 +25,11 @@ #include "libc/sock/sock.h" #include "libc/sysv/consts/sig.h" #include "libc/x/x.h" +#include "net/https/https.h" #include "third_party/mbedtls/ctr_drbg.h" #include "third_party/mbedtls/ecp.h" #include "third_party/mbedtls/error.h" +#include "third_party/mbedtls/platform.h" #include "third_party/mbedtls/ssl.h" #include "tool/build/lib/eztls.h" #include "tool/build/lib/psk.h" @@ -37,34 +39,6 @@ mbedtls_ssl_config ezconf; mbedtls_ssl_context ezssl; mbedtls_ctr_drbg_context ezrng; -static char *EzTlsError(int r) { - static char b[128]; - mbedtls_strerror(r, b, sizeof(b)); - return b; -} - -void EzTlsDie(const char *s, int r) { - if (IsTiny()) { - fprintf(stderr, "error: %s (-0x%04x %s)\n", s, -r, EzTlsError(r)); - } else { - fprintf(stderr, "error: %s (grep -0x%04x)\n", s, -r); - } - exit(1); -} - -static int EzGetEntropy(void *c, unsigned char *p, size_t n) { - CHECK_EQ(n, getrandom(p, n, 0)); - return 0; -} - -static void EzInitializeRng(mbedtls_ctr_drbg_context *r) { - volatile unsigned char b[64]; - mbedtls_ctr_drbg_init(r); - CHECK(getrandom(b, 64, 0) == 64); - CHECK(!mbedtls_ctr_drbg_seed(r, EzGetEntropy, 0, b, 64)); - mbedtls_platform_zeroize(b, 64); -} - static ssize_t EzWritevAll(int fd, struct iovec *iov, int iovlen) { int i; ssize_t rc; @@ -165,34 +139,38 @@ static int EzTlsRecv(void *ctx, unsigned char *buf, size_t len, uint32_t tmo) { return EzTlsRecvImpl(ctx, buf, len, tmo); } +void EzFd(int fd) { + mbedtls_ssl_session_reset(&ezssl); + mbedtls_platform_zeroize(&ezbio, sizeof(ezbio)); + ezbio.fd = fd; +} + void EzHandshake(void) { int rc; while ((rc = mbedtls_ssl_handshake(&ezssl))) { if (rc != MBEDTLS_ERR_SSL_WANT_READ) { - EzTlsDie("handshake failed", rc); + TlsDie("handshake failed", rc); } } while ((rc = EzTlsFlush(&ezbio, 0, 0))) { if (rc != MBEDTLS_ERR_SSL_WANT_READ) { - EzTlsDie("handshake flush failed", rc); + TlsDie("handshake flush failed", rc); } } } -/* - * openssl s_client -connect 127.0.0.1:31337 \ - * -psk $(hex <~/.runit.psk) \ - * -psk_identity runit - */ - -void SetupPresharedKeySsl(int endpoint) { +void EzInitialize(void) { xsigaction(SIGPIPE, SIG_IGN, 0, 0, 0); - EzInitializeRng(&ezrng); ezconf.disable_compression = 1; /* TODO(jart): Why does it behave weirdly? */ - mbedtls_ssl_config_defaults(&ezconf, endpoint, MBEDTLS_SSL_TRANSPORT_STREAM, - MBEDTLS_SSL_PRESET_SUITEC); + InitializeRng(&ezrng); +} + +void EzSetup(char psk[32]) { + int rc; mbedtls_ssl_conf_rng(&ezconf, mbedtls_ctr_drbg_random, &ezrng); - DCHECK_EQ(0, mbedtls_ssl_conf_psk(&ezconf, GetRunitPsk(), 32, "runit", 5)); - DCHECK_EQ(0, mbedtls_ssl_setup(&ezssl, &ezconf)); + if ((rc = mbedtls_ssl_conf_psk(&ezconf, psk, 32, "runit", 5)) || + (rc = mbedtls_ssl_setup(&ezssl, &ezconf))) { + TlsDie("EzSetup", rc); + } mbedtls_ssl_set_bio(&ezssl, &ezbio, EzTlsSend, 0, EzTlsRecv); } diff --git a/tool/build/lib/eztls.h b/tool/build/lib/eztls.h index f0ee84350..5c659f227 100644 --- a/tool/build/lib/eztls.h +++ b/tool/build/lib/eztls.h @@ -2,6 +2,7 @@ #define COSMOPOLITAN_TOOL_BUILD_LIB_EZTLS_H_ #include "third_party/mbedtls/ctr_drbg.h" #include "third_party/mbedtls/ssl.h" +#include "third_party/mbedtls/x509_crt.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -17,11 +18,24 @@ extern mbedtls_ssl_config ezconf; extern mbedtls_ssl_context ezssl; extern mbedtls_ctr_drbg_context ezrng; +void EzFd(int); void EzHandshake(void); -void SetupPresharedKeySsl(int); -void EzTlsDie(const char *, int) wontreturn; +void EzSetup(char[32]); +void EzInitialize(void); int EzTlsFlush(struct EzTlsBio *, const unsigned char *, size_t); +/* + * openssl s_client -connect 127.0.0.1:31337 \ + * -psk $(hex <~/.runit.psk) \ + * -psk_identity runit + */ +forceinline void SetupPresharedKeySsl(int endpoint, char psk[32]) { + EzInitialize(); + mbedtls_ssl_config_defaults(&ezconf, endpoint, MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_SUITEC); + EzSetup(psk); +} + COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_TOOL_BUILD_LIB_EZTLS_H_ */ diff --git a/tool/build/runit.c b/tool/build/runit.c index 34ddc1a7b..00ccf8414 100644 --- a/tool/build/runit.c +++ b/tool/build/runit.c @@ -42,8 +42,10 @@ #include "libc/sysv/consts/sock.h" #include "libc/time/time.h" #include "libc/x/x.h" +#include "net/https/https.h" #include "third_party/mbedtls/ssl.h" #include "tool/build/lib/eztls.h" +#include "tool/build/lib/psk.h" #include "tool/build/runit.h" /** @@ -336,7 +338,7 @@ bool Recv(unsigned char *p, size_t n) { usleep((backoff = (backoff + 1000) * 2)); return false; } else if (rc < 0) { - EzTlsDie("read response failed", rc); + TlsDie("read response failed", rc); } } return true; @@ -387,9 +389,8 @@ int RunOnHost(char *spec) { 1); if (!strchr(g_hostname, '.')) strcat(g_hostname, ".test."); do { - mbedtls_ssl_session_reset(&ezssl); Connect(); - ezbio.fd = g_sock; + EzFd(g_sock); EzHandshake(); SendRequest(); } while ((rc = ReadResponse()) == -1); @@ -454,7 +455,7 @@ int RunRemoteTestsInParallel(char *hosts[], int count) { int main(int argc, char *argv[]) { showcrashreports(); - SetupPresharedKeySsl(MBEDTLS_SSL_IS_CLIENT); + SetupPresharedKeySsl(MBEDTLS_SSL_IS_CLIENT, GetRunitPsk()); /* __log_level = kLogDebug; */ if (argc > 1 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) { diff --git a/tool/build/runitd.c b/tool/build/runitd.c index b1de47ce4..0af4f97da 100644 --- a/tool/build/runitd.c +++ b/tool/build/runitd.c @@ -45,9 +45,11 @@ #include "libc/sysv/consts/w.h" #include "libc/time/time.h" #include "libc/x/x.h" +#include "net/https/https.h" #include "third_party/getopt/getopt.h" #include "third_party/mbedtls/ssl.h" #include "tool/build/lib/eztls.h" +#include "tool/build/lib/psk.h" #include "tool/build/runit.h" /** @@ -264,13 +266,13 @@ void HandleClient(void) { close(g_clifd); return; } - ezbio.fd = g_clifd; + EzFd(g_clifd); EzHandshake(); addrstr = gc(DescribeAddress(&addr)); DEBUGF("%s %s %s", gc(DescribeAddress(&g_servaddr)), "accepted", addrstr); while ((got = mbedtls_ssl_read(&ezssl, (p = g_buf), sizeof(g_buf))) < 0) { if (got != MBEDTLS_ERR_SSL_WANT_READ) { - EzTlsDie("ssl read failed", got); + TlsDie("ssl read failed", got); } } CHECK_GE(got, kMinMsgSize); @@ -302,7 +304,7 @@ void HandleClient(void) { while (remaining) { while ((got = mbedtls_ssl_read(&ezssl, g_buf, sizeof(g_buf))) < 0) { if (got != MBEDTLS_ERR_SSL_WANT_READ) { - EzTlsDie("ssl read failed", got); + TlsDie("ssl read failed", got); } } CHECK_LE(got, remaining); @@ -443,7 +445,7 @@ void Daemonize(void) { int main(int argc, char *argv[]) { showcrashreports(); - SetupPresharedKeySsl(MBEDTLS_SSL_IS_SERVER); + SetupPresharedKeySsl(MBEDTLS_SSL_IS_SERVER, GetRunitPsk()); /* __log_level = kLogDebug; */ GetOpts(argc, argv); CHECK_NE(-1, (g_devnullfd = open("/dev/null", O_RDWR))); diff --git a/tool/build/zipobj.c b/tool/build/zipobj.c index 1271b2891..3b6a79e38 100644 --- a/tool/build/zipobj.c +++ b/tool/build/zipobj.c @@ -124,34 +124,10 @@ void GetOpts(int *argc, char ***argv) { CHECK_NOTNULL(outpath_); } -bool IsUtf8(const void *data, size_t size) { - const unsigned char *p, *pe; - for (p = data, pe = p + size; p + 2 <= pe; ++p) { - if (p[0] >= 0300) { - if (p[1] >= 0200 && p[1] < 0300) { - return true; - } else { - return false; - } - } - } - return false; -} - -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; -} - bool ShouldCompress(const char *name, size_t namesize, const unsigned char *data, size_t datasize) { return !nocompress_ && datasize >= 64 && !IsNoCompressExt(name, namesize) && - (datasize < 1000 || MeasureEntropy((void *)data, 1000) < 6); + (datasize < 1000 || MeasureEntropy((void *)data, 1000) < 7); } void GetDosLocalTime(int64_t utcunixts, uint16_t *out_time, diff --git a/tool/net/redbean.c b/tool/net/redbean.c index a936e2113..371886586 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -21,6 +21,7 @@ #include "libc/bits/popcnt.h" #include "libc/bits/safemacros.internal.h" #include "libc/calls/calls.h" +#include "libc/calls/math.h" #include "libc/calls/sigbits.h" #include "libc/calls/struct/dirent.h" #include "libc/calls/struct/flock.h" @@ -50,10 +51,12 @@ #include "libc/runtime/directmap.internal.h" #include "libc/runtime/gc.internal.h" #include "libc/runtime/runtime.h" +#include "libc/sock/goodsocket.internal.h" #include "libc/sock/sock.h" #include "libc/stdio/append.internal.h" #include "libc/stdio/hex.internal.h" #include "libc/stdio/stdio.h" +#include "libc/str/slice.h" #include "libc/str/str.h" #include "libc/str/undeflate.h" #include "libc/sysv/consts/af.h" @@ -106,6 +109,7 @@ #include "third_party/mbedtls/entropy_poll.h" #include "third_party/mbedtls/error.h" #include "third_party/mbedtls/iana.h" +#include "third_party/mbedtls/md.h" #include "third_party/mbedtls/md5.h" #include "third_party/mbedtls/oid.h" #include "third_party/mbedtls/pk.h" @@ -267,10 +271,7 @@ static struct Suites { static struct Certs { size_t n; - struct Cert { - mbedtls_x509_crt *cert; - mbedtls_pk_context *key; - } * p; + struct Cert *p; } certs; static struct Redirects { @@ -480,39 +481,6 @@ static long ParseInt(const char *s) { return strtol(s, 0, 0); } -forceinline bool SlicesEqual(const char *a, size_t n, const char *b, size_t m) { - return n == m && !memcmp(a, b, n); -} - -forceinline bool SlicesEqualCase(const void *a, size_t n, const void *b, - size_t m) { - return n == m && !memcasecmp(a, b, n); -} - -static int CompareSlices(const char *a, size_t n, const char *b, size_t m) { - int c; - if ((c = memcmp(a, b, MIN(n, m)))) return c; - if (n < m) return -1; - if (n > m) return +1; - return 0; -} - -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; -} - -static bool StartsWithIgnoreCase(const char *s, const char *prefix) { - for (;;) { - if (!*prefix) return true; - if (!*s) return false; - if (kToLower[*s++ & 255] != (*prefix++ & 255)) return false; - } -} - static void *FreeLater(void *p) { if (p) { if (++freelist.n > freelist.c) { @@ -587,28 +555,6 @@ static long FindRedirect(const char *s, size_t n) { return -1; } -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) { @@ -628,20 +574,6 @@ static void UseCertificate(mbedtls_ssl_config *c, struct Cert *kp, CHECK_EQ(0, mbedtls_ssl_conf_own_cert(c, kp->cert, kp->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 AppendCert(mbedtls_x509_crt *cert, mbedtls_pk_context *key) { certs.p = realloc(certs.p, ++certs.n * sizeof(*certs.p)); certs.p[certs.n - 1].cert = cert; @@ -1179,34 +1111,6 @@ static void ReportWorkerExit(int pid, int ws) { } } -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) { char *s, *b = 0; if (logrusage || LOGGABLE(kLogDebug)) { @@ -1389,11 +1293,6 @@ static int TlsRecv(void *ctx, unsigned char *buf, size_t len, uint32_t tmo) { return TlsRecvImpl(ctx, buf, len, tmo); } -static void TlsDebug(void *ctx, int level, const char *file, int line, - const char *message) { - flogf(level, file, line, 0, "TLS %s", message); -} - static ssize_t SslRead(int fd, void *buf, size_t size) { int rc; rc = mbedtls_ssl_read(&ssl, buf, size); @@ -1473,12 +1372,11 @@ static void WipeServingKeys(void) { } } -static bool CertHasCommonName(const mbedtls_x509_crt *cert, - const unsigned char *host, size_t size) { +bool CertHasCommonName(const mbedtls_x509_crt *cert, const void *s, size_t n) { const mbedtls_x509_name *name; for (name = &cert->subject; name; name = name->next) { if (!MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &name->oid)) { - if (SlicesEqualCase(host, size, name->val.p, name->val.len)) { + if (SlicesEqualCase(s, n, name->val.p, name->val.len)) { return true; } break; @@ -1487,44 +1385,11 @@ static bool CertHasCommonName(const mbedtls_x509_crt *cert, return false; } -static bool CertHasHost(const mbedtls_x509_crt *cert, const unsigned char *host, - size_t size) { - const mbedtls_x509_sequence *cur; - for (cur = &cert->subject_alt_names; cur; cur = cur->next) { - if ((cur->buf.tag & MBEDTLS_ASN1_TAG_VALUE_MASK) == - MBEDTLS_X509_SAN_DNS_NAME && - SlicesEqualCase(host, size, cur->buf.p, cur->buf.len)) { - return true; - } - } - return false; -} - -static bool CertHasIp(const mbedtls_x509_crt *cert, uint32_t ip) { - const mbedtls_x509_sequence *cur; - for (cur = &cert->subject_alt_names; cur; cur = cur->next) { - if ((cur->buf.tag & MBEDTLS_ASN1_TAG_VALUE_MASK) == - MBEDTLS_X509_SAN_IP_ADDRESS && - cur->buf.len == 4 && ip == READ32BE(cur->buf.p)) { - return true; - } - } - return false; -} - -static bool IsServerCert(mbedtls_pk_type_t type, int i) { - return certs.p[i].cert && certs.p[i].key && !certs.p[i].cert->ca_istrue && - mbedtls_pk_get_type(certs.p[i].key) == type && - !mbedtls_x509_crt_check_extended_key_usage( - certs.p[i].cert, MBEDTLS_OID_SERVER_AUTH, - MBEDTLS_OID_SIZE(MBEDTLS_OID_SERVER_AUTH)); -} - static bool TlsRouteFind(mbedtls_pk_type_t type, mbedtls_ssl_context *ssl, const unsigned char *host, size_t size, int64_t ip) { int i; for (i = 0; i < certs.n; ++i) { - if (IsServerCert(type, i) && + if (IsServerCert(certs.p + i, type) && (((certs.p[i].cert->ext_types & MBEDTLS_X509_EXT_SUBJECT_ALT_NAME) && (ip == -1 ? CertHasHost(certs.p[i].cert, host, size) : CertHasIp(certs.p[i].cert, ip))) || @@ -1543,7 +1408,7 @@ static bool TlsRouteFind(mbedtls_pk_type_t type, mbedtls_ssl_context *ssl, static bool TlsRouteFirst(mbedtls_pk_type_t type, mbedtls_ssl_context *ssl) { int i; for (i = 0; i < certs.n; ++i) { - if (IsServerCert(type, i)) { + if (IsServerCert(certs.p + i, type)) { CHECK_EQ( 0, mbedtls_ssl_set_hs_own_cert(ssl, certs.p[i].cert, certs.p[i].key)); DEBUGF("TlsRoute(%s) %s %`'s", mbedtls_pk_type_name(type), @@ -1679,40 +1544,6 @@ static bool TlsSetup(void) { } } -static int GetEntropy(void *c, unsigned char *p, size_t n) { - CHECK_EQ(n, getrandom(p, n, 0)); - return 0; -} - -static void InitializeRng(mbedtls_ctr_drbg_context *r) { - volatile unsigned char b[64]; - mbedtls_ctr_drbg_init(r); - CHECK(getrandom(b, 64, 0) == 64); - CHECK(!mbedtls_ctr_drbg_seed(r, GetEntropy, 0, b, 64)); - 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; - FormatSslTime(notbefore, gmtime_r(&past, &tm)); - FormatSslTime(notafter, gmtime_r(&future, &tm)); -} - static void ConfigureCertificate(mbedtls_x509write_cert *cw, struct Cert *ca, int usage, int type) { int r; @@ -1766,8 +1597,10 @@ static void ConfigureCertificate(mbedtls_x509write_cert *cw, struct Cert *ca, 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)) || +#if defined(MBEDTLS_SHA1_C) (r = mbedtls_x509write_crt_set_subject_key_identifier(cw)) || (r = mbedtls_x509write_crt_set_authority_key_identifier(cw)) || +#endif (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)) || @@ -1794,57 +1627,18 @@ static struct Cert GetKeySigningKey(void) { return (struct Cert){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; - unsigned char *p; - mbedtls_x509_crt *cert; - p = malloc((n = FRAMESIZE)); - i = mbedtls_x509write_crt_der(wcert, p, n, mbedtls_ctr_drbg_random, kr); - if (i < 0) FATALF("write key (grep -0x%04x)", -i); - cert = calloc(1, sizeof(mbedtls_x509_crt)); - mbedtls_x509_crt_parse(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))) { - FATALF("generate key (grep -0x%04x)", -rc); - } - LogCertificate( - gc(xasprintf("generated %s certificate", mbedtls_pk_get_name(&cert->pk))), - cert); - return (struct Cert){cert, key}; -} - static struct Cert GenerateEcpCertificate(struct Cert *ca) { mbedtls_pk_context *key; + mbedtls_md_type_t md_alg; mbedtls_ctr_drbg_context kr; mbedtls_x509write_cert wcert; InitializeRng(&kr); - key = InitializeKey(ca, &wcert, MBEDTLS_PK_ECKEY); + md_alg = suiteb ? MBEDTLS_MD_SHA384 : MBEDTLS_MD_SHA256; + key = InitializeKey(ca, &wcert, md_alg, MBEDTLS_PK_ECKEY); 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); + GenerateCertificateSerial(&wcert, &kr); ConfigureCertificate(&wcert, ca, MBEDTLS_X509_KU_DIGITAL_SIGNATURE, MBEDTLS_X509_NS_CERT_TYPE_SSL_SERVER | MBEDTLS_X509_NS_CERT_TYPE_SSL_CLIENT); @@ -1853,13 +1647,15 @@ static struct Cert GenerateEcpCertificate(struct Cert *ca) { static struct Cert GenerateRsaCertificate(struct Cert *ca) { mbedtls_pk_context *key; + mbedtls_md_type_t md_alg; mbedtls_ctr_drbg_context kr; mbedtls_x509write_cert wcert; InitializeRng(&kr); - key = InitializeKey(ca, &wcert, MBEDTLS_PK_RSA); + md_alg = suiteb ? MBEDTLS_MD_SHA384 : MBEDTLS_MD_SHA256; + key = InitializeKey(ca, &wcert, md_alg, MBEDTLS_PK_RSA); CHECK_EQ(0, mbedtls_rsa_gen_key(mbedtls_pk_rsa(*key), mbedtls_ctr_drbg_random, &kr, suiteb ? 4096 : 2048, 65537)); - GenerateSerial(&wcert, &kr); + GenerateCertificateSerial(&wcert, &kr); ConfigureCertificate( &wcert, ca, MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_KEY_ENCIPHERMENT, @@ -1942,65 +1738,6 @@ static int64_t GetGmtOffset(int64_t t) { return tm.tm_gmtoff; } -static int64_t LocoTimeToZulu(int64_t x) { - return x - gmtoff; -} - -static int64_t GetZipCfileLastModified(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 && - READ16LE(ZIP_EXTRA_CONTENT(p) + 4) == 1 && - READ16LE(ZIP_EXTRA_CONTENT(p) + 6) >= 8) { - return READ64LE(ZIP_EXTRA_CONTENT(p) + 8) / HECTONANOSECONDS - - MODERNITYSECONDS; /* TODO(jart): update access time */ - } - } - 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 + 4 && (*ZIP_EXTRA_CONTENT(p) & 1)) { - return (int32_t)READ32LE(ZIP_EXTRA_CONTENT(p) + 1); - } - } - 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))); -} - -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); -} - forceinline bool IsCompressed(struct Asset *a) { return !a->file && ZIP_LFILE_COMPRESSIONMETHOD(zbase + a->lf) == kZipCompressionDeflate; @@ -2054,9 +1791,9 @@ static void FreeStrings(struct Strings *l) { } static void IndexAssets(void) { - int64_t lm; uint64_t cf; struct Asset *p; + struct timespec lm; uint32_t i, n, m, step, hash; DEBUGF("indexing assets (inode %#lx)", zst.st_ino); CHECK_GE(HASH_LOAD_FACTOR, 2); @@ -2080,13 +1817,13 @@ static void IndexAssets(void) { i = (hash + (step * (step + 1)) >> 1) & (m - 1); ++step; } while (p[i].hash); - lm = GetZipCfileLastModified(zbase + cf); + GetZipCfileTimestamps(zbase + cf, &lm, 0, 0, gmtoff); p[i].hash = hash; p[i].cf = cf; p[i].lf = GetZipCfileOffset(zbase + cf); p[i].istext = !!(ZIP_CFILE_INTERNALATTRIBUTES(zbase + cf) & kZipIattrText); - p[i].lastmodified = lm; - p[i].lastmodifiedstr = FormatUnixHttpDateTime(xmalloc(30), lm); + p[i].lastmodified = lm.tv_sec; + p[i].lastmodifiedstr = FormatUnixHttpDateTime(xmalloc(30), lm.tv_sec); } assets.p = p; assets.n = m; @@ -2891,9 +2628,9 @@ static char *ServeListing(void) { uint8_t *zcf; struct tm tm; const char *and; - int64_t lastmod; struct rusage ru; char *p, *q, *path; + struct timespec lastmod; char rb[8], tb[20], *rp[6]; size_t i, n, pathlen, rn[6]; LockInc(&shared->c.listingrequests); @@ -2951,8 +2688,8 @@ td { padding-right: 3em; }\r\n\ ZIP_CFILE_COMMENT(zcf), strnlen(ZIP_CFILE_COMMENT(zcf), ZIP_CFILE_COMMENTSIZE(zcf)), &rn[3]); rp[4] = EscapeHtml(rp[0], rn[0], &rn[4]); - lastmod = GetZipCfileLastModified(zcf); - localtime_r(&lastmod, &tm); + GetZipCfileTimestamps(zcf, &lastmod, 0, 0, gmtoff); + localtime_r(&lastmod.tv_sec, &tm); iso8601(tb, &tm); if (IsCompressionMethodSupported(ZIP_CFILE_COMPRESSIONMETHOD(zcf)) && IsAcceptablePath(path, pathlen)) { @@ -3421,30 +3158,6 @@ static void GetDosLocalTime(int64_t utcunixts, uint16_t *out_time, *out_date = DOS_DATE(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday + 1); } -static bool IsUtf8(const void *data, size_t size) { - const unsigned char *p, *pe; - for (p = data, pe = p + size; p + 2 <= pe; ++p) { - if (p[0] >= 0300) { - if (p[1] >= 0200 && p[1] < 0300) { - 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; -} - static int LuaStoreAsset(lua_State *L) { int64_t ft; int i, mode; @@ -3646,12 +3359,6 @@ static void ReseedRng(mbedtls_ctr_drbg_context *r, const char *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)); @@ -3663,35 +3370,6 @@ static wontreturn void LuaThrowTlsError(lua_State *L, const char *s, int r) { 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; @@ -3924,8 +3602,8 @@ static int LuaFetch(lua_State *L) { 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))); + CHECK_NE(-1, (sock = GoodSocket(addr->ai_family, addr->ai_socktype, + addr->ai_protocol, false, &timeout))); if (connect(sock, addr->ai_addr, addr->ai_addrlen) == -1) { close(sock); luaL_error(L, "connect(%s:%s) failed: %s", host, port, strerror(errno)); @@ -5168,13 +4846,17 @@ static int LuaGetLastModifiedTime(lua_State *L) { size_t pathlen; struct Asset *a; const char *path; + struct timespec lm; + int64_t zuluseconds; path = LuaCheckPath(L, 1, &pathlen); if ((a = GetAsset(path, pathlen))) { if (a->file) { - lua_pushinteger(L, a->file->st.st_mtim.tv_sec); + zuluseconds = a->file->st.st_mtim.tv_sec; } else { - lua_pushinteger(L, GetZipCfileLastModified(zbase + a->cf)); + GetZipCfileTimestamps(zbase + a->cf, &lm, 0, 0, gmtoff); + zuluseconds = lm.tv_sec; } + lua_pushinteger(L, zuluseconds); } else { lua_pushnil(L); } @@ -6216,8 +5898,8 @@ static char *ServeAsset(struct Asset *a, const char *path, size_t pathlen) { } } else if (!IsTiny() && msg.method != kHttpHead && !IsSslCompressed() && ClientAcceptsGzip() && - ((contentlength >= 100 && StartsWithIgnoreCase(ct, "text/")) || - (contentlength >= 1000 && MeasureEntropy(content, 1000) < 6))) { + ((contentlength >= 100 && startswithi(ct, "text/")) || + (contentlength >= 1000 && MeasureEntropy(content, 1000) < 7))) { p = ServeAssetCompressed(a); } else { p = ServeAssetIdentity(a, ct); @@ -6725,8 +6407,8 @@ static void Listen(void) { 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]); - if ((servers.p[n].fd = Socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, - IPPROTO_TCP, true)) == -1) { + if ((servers.p[n].fd = GoodSocket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, + IPPROTO_TCP, true, &timeout)) == -1) { perror("socket"); exit(1); } diff --git a/tool/net/wb.c b/tool/net/wb.c index 2ae4cd5d0..923a48415 100644 --- a/tool/net/wb.c +++ b/tool/net/wb.c @@ -27,9 +27,11 @@ #include "libc/mem/mem.h" #include "libc/rand/rand.h" #include "libc/runtime/gc.internal.h" +#include "libc/sock/goodsocket.internal.h" #include "libc/sock/sock.h" #include "libc/stdio/append.internal.h" #include "libc/stdio/stdio.h" +#include "libc/str/slice.h" #include "libc/str/str.h" #include "libc/sysv/consts/af.h" #include "libc/sysv/consts/ex.h" @@ -109,37 +111,6 @@ void OnInt(int sig) { isdone = true; } -static inline bool SlicesEqualCase(const char *a, size_t n, const char *b, - size_t m) { - return n == m && !memcasecmp(a, b, n); -} - -static int GetEntropy(void *c, unsigned char *p, size_t n) { - rngset(p, n, rand64, -1); - return 0; -} - -static bool TuneSocket(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) { - int fd; - if ((fd = socket(family, type, protocol)) != -1) { - /* TuneSocket(fd, SOL_SOCKET, SO_KEEPALIVE, 1); */ - /* if (protocol == SOL_TCP) { */ - /* TuneSocket(fd, SOL_TCP, TCP_KEEPIDLE, 60); */ - /* TuneSocket(fd, SOL_TCP, TCP_KEEPINTVL, 60); */ - /* TuneSocket(fd, SOL_TCP, TCP_FASTOPEN_CONNECT, 1); */ - /* if (!TuneSocket(fd, SOL_TCP, TCP_QUICKACK, 1)) { */ - /* TuneSocket(fd, SOL_TCP, TCP_NODELAY, 1); */ - /* } */ - /* } */ - } - return fd; -} - static int TlsSend(void *c, const unsigned char *p, size_t n) { int rc; if ((rc = write(*(int *)c, p, n)) == -1) { @@ -174,21 +145,6 @@ static int TlsRecv(void *c, unsigned char *p, size_t n, uint32_t o) { return r; } -static char *TlsError(int r) { - static char b[128]; - mbedtls_strerror(r, b, sizeof(b)); - return b; -} - -static wontreturn void TlsDie(const char *s, int r) { - if (IsTiny()) { - fprintf(stderr, "error: %s (-0x%04x %s)\n", s, -r, TlsError(r)); - } else { - fprintf(stderr, "error: %s (grep -0x%04x)\n", s, -r); - } - exit(1); -} - static wontreturn void PrintUsage(FILE *f, int rc) { fprintf(f, "usage: %s [-ksvV] URL\n", program_invocation_name); exit(rc); @@ -219,8 +175,8 @@ int fetch(void) { */ InitHttpMessage(&msg, kHttpResponse); ip = ntohl(((struct sockaddr_in *)addr->ai_addr)->sin_addr.s_addr); - CHECK_NE(-1, (sock = Socket(addr->ai_family, addr->ai_socktype, - addr->ai_protocol))); + CHECK_NE(-1, (sock = GoodSocket(addr->ai_family, addr->ai_socktype, + addr->ai_protocol, false, 0))); if (connect(sock, addr->ai_addr, addr->ai_addrlen) == -1) { goto TransportError; }