parent
a1782233ca
commit
453543e0c8
56 changed files with 3821 additions and 49 deletions
@ -1,3 +1,6 @@ |
|||||||
/build/ |
/build/ |
||||||
*.o |
*.o |
||||||
/thirdparty/ |
/thirdparty/ |
||||||
|
/vendor |
||||||
|
/.idea |
||||||
|
/composer.lock |
||||||
@ -0,0 +1,17 @@ |
|||||||
|
{ |
||||||
|
"name": "swoole/typephp-os", |
||||||
|
"description": "TypePHP-OS experimental system image", |
||||||
|
"type": "project", |
||||||
|
"license": "BSD-3-Clause", |
||||||
|
"minimum-stability": "dev", |
||||||
|
"prefer-stable": true, |
||||||
|
"repositories": [ |
||||||
|
{"type": "path", "url": "../..", "options": {"symlink": true}}, |
||||||
|
{"type": "path", "url": "../../../phpx", "options": {"symlink": true}}, |
||||||
|
{"type": "path", "url": "../../../php-nano", "options": {"symlink": true}} |
||||||
|
], |
||||||
|
"require": { |
||||||
|
"swoole/typephp": "dev-master", |
||||||
|
"swoole/php-nano": "dev-master" |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,23 @@ |
|||||||
|
#ifndef TYPEPHP_OS_LWIP_ARCH_CC_H |
||||||
|
#define TYPEPHP_OS_LWIP_ARCH_CC_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#include <limits.h> |
||||||
|
|
||||||
|
typedef long ssize_t; |
||||||
|
#ifndef SSIZE_MAX |
||||||
|
#define SSIZE_MAX LONG_MAX |
||||||
|
#endif |
||||||
|
|
||||||
|
#define BYTE_ORDER LITTLE_ENDIAN |
||||||
|
#define LWIP_NO_UNISTD_H 1 |
||||||
|
#define LWIP_NO_CTYPE_H 1 |
||||||
|
#define LWIP_PLATFORM_DIAG(message) do { } while (0) |
||||||
|
|
||||||
|
void typephp_network_assert(const char *message); |
||||||
|
uint32_t typephp_network_random_u32(void); |
||||||
|
|
||||||
|
#define LWIP_PLATFORM_ASSERT(message) typephp_network_assert(message) |
||||||
|
#define LWIP_RAND() typephp_network_random_u32() |
||||||
|
|
||||||
|
#endif |
||||||
@ -0,0 +1,326 @@ |
|||||||
|
/* Intel 82540EM (QEMU e1000) driver using its indirect I/O BAR. */ |
||||||
|
|
||||||
|
#include "e1000.h" |
||||||
|
#include "../vm.h" |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
enum { |
||||||
|
E1000_VENDOR = 0x8086, |
||||||
|
E1000_DEVICE = 0x100e, |
||||||
|
PCI_ADDRESS = 0xcf8, |
||||||
|
PCI_DATA = 0xcfc, |
||||||
|
PCI_COMMAND = 0x04, |
||||||
|
PCI_BAR_BEGIN = 0x10, |
||||||
|
PCI_BAR_END = 0x28, |
||||||
|
PCI_COMMAND_MEMORY = 2, |
||||||
|
PCI_COMMAND_BUS_MASTER = 4, |
||||||
|
REG_CTRL = 0x0000, |
||||||
|
REG_STATUS = 0x0008, |
||||||
|
REG_EERD = 0x0014, |
||||||
|
REG_ICR = 0x00c0, |
||||||
|
REG_IMC = 0x00d8, |
||||||
|
REG_RCTL = 0x0100, |
||||||
|
REG_TCTL = 0x0400, |
||||||
|
REG_TIPG = 0x0410, |
||||||
|
REG_RDBAL = 0x2800, |
||||||
|
REG_RDBAH = 0x2804, |
||||||
|
REG_RDLEN = 0x2808, |
||||||
|
REG_RDH = 0x2810, |
||||||
|
REG_RDT = 0x2818, |
||||||
|
REG_TDBAL = 0x3800, |
||||||
|
REG_TDBAH = 0x3804, |
||||||
|
REG_TDLEN = 0x3808, |
||||||
|
REG_TDH = 0x3810, |
||||||
|
REG_TDT = 0x3818, |
||||||
|
REG_RAL = 0x5400, |
||||||
|
REG_RAH = 0x5404, |
||||||
|
CTRL_RST = 1u << 26, |
||||||
|
CTRL_SLU = 1u << 6, |
||||||
|
EERD_START = 1, |
||||||
|
EERD_DONE = 1u << 4, |
||||||
|
RCTL_EN = 1u << 1, |
||||||
|
RCTL_BAM = 1u << 15, |
||||||
|
RCTL_SECRC = 1u << 26, |
||||||
|
TCTL_EN = 1u << 1, |
||||||
|
TCTL_PSP = 1u << 3, |
||||||
|
RX_DESCRIPTOR_DONE = 1, |
||||||
|
TX_DESCRIPTOR_DONE = 1, |
||||||
|
TX_COMMAND_EOP = 1, |
||||||
|
TX_COMMAND_IFCS = 1u << 1, |
||||||
|
TX_COMMAND_RS = 1u << 3, |
||||||
|
DESCRIPTOR_COUNT = 256, |
||||||
|
BUFFER_SIZE = 2048, |
||||||
|
}; |
||||||
|
|
||||||
|
typedef struct __attribute__((packed)) { |
||||||
|
uint64_t address; |
||||||
|
uint16_t length; |
||||||
|
uint16_t checksum; |
||||||
|
uint8_t status; |
||||||
|
uint8_t errors; |
||||||
|
uint16_t special; |
||||||
|
} rx_descriptor; |
||||||
|
|
||||||
|
typedef struct __attribute__((packed)) { |
||||||
|
uint64_t address; |
||||||
|
uint16_t length; |
||||||
|
uint8_t checksum_offset; |
||||||
|
uint8_t command; |
||||||
|
uint8_t status; |
||||||
|
uint8_t checksum_start; |
||||||
|
uint16_t special; |
||||||
|
} tx_descriptor; |
||||||
|
|
||||||
|
static rx_descriptor rx_descriptors[DESCRIPTOR_COUNT] |
||||||
|
__attribute__((aligned(4096))); |
||||||
|
static tx_descriptor tx_descriptors[DESCRIPTOR_COUNT] |
||||||
|
__attribute__((aligned(4096))); |
||||||
|
static uint8_t rx_buffers[DESCRIPTOR_COUNT][BUFFER_SIZE] |
||||||
|
__attribute__((aligned(4096))); |
||||||
|
static uint8_t tx_buffers[DESCRIPTOR_COUNT][BUFFER_SIZE] |
||||||
|
__attribute__((aligned(4096))); |
||||||
|
static uint8_t mac_address[6]; |
||||||
|
static volatile uint8_t *mmio_base; |
||||||
|
static uint16_t rx_index; |
||||||
|
static uint16_t tx_index; |
||||||
|
static int initialized; |
||||||
|
|
||||||
|
static inline void outl(uint16_t port, uint32_t value) |
||||||
|
{ |
||||||
|
__asm__ volatile("outl %0, %1" : : "a"(value), "Nd"(port)); |
||||||
|
} |
||||||
|
|
||||||
|
static inline uint32_t inl(uint16_t port) |
||||||
|
{ |
||||||
|
uint32_t value; |
||||||
|
__asm__ volatile("inl %1, %0" : "=a"(value) : "Nd"(port)); |
||||||
|
return value; |
||||||
|
} |
||||||
|
|
||||||
|
static uint32_t pci_read32( |
||||||
|
uint8_t bus, uint8_t device, uint8_t function, uint8_t offset) |
||||||
|
{ |
||||||
|
const uint32_t address = UINT32_C(0x80000000) |
||||||
|
| ((uint32_t) bus << 16u) | ((uint32_t) device << 11u) |
||||||
|
| ((uint32_t) function << 8u) | (offset & 0xfcu); |
||||||
|
outl(PCI_ADDRESS, address); |
||||||
|
return inl(PCI_DATA); |
||||||
|
} |
||||||
|
|
||||||
|
static void pci_write32(uint8_t bus, uint8_t device, uint8_t function, |
||||||
|
uint8_t offset, uint32_t value) |
||||||
|
{ |
||||||
|
const uint32_t address = UINT32_C(0x80000000) |
||||||
|
| ((uint32_t) bus << 16u) | ((uint32_t) device << 11u) |
||||||
|
| ((uint32_t) function << 8u) | (offset & 0xfcu); |
||||||
|
outl(PCI_ADDRESS, address); |
||||||
|
outl(PCI_DATA, value); |
||||||
|
} |
||||||
|
|
||||||
|
static uint32_t register_read(uint32_t offset) |
||||||
|
{ |
||||||
|
return *(volatile uint32_t *) (mmio_base + offset); |
||||||
|
} |
||||||
|
|
||||||
|
static void register_write(uint32_t offset, uint32_t value) |
||||||
|
{ |
||||||
|
*(volatile uint32_t *) (mmio_base + offset) = value; |
||||||
|
} |
||||||
|
|
||||||
|
static int read_eeprom_word(uint8_t address, uint16_t *value) |
||||||
|
{ |
||||||
|
register_write(REG_EERD, ((uint32_t) address << 8u) | EERD_START); |
||||||
|
for (unsigned int attempt = 0; attempt < 1000000u; ++attempt) { |
||||||
|
const uint32_t result = register_read(REG_EERD); |
||||||
|
if ((result & EERD_DONE) != 0) { |
||||||
|
*value = (uint16_t) (result >> 16u); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
__asm__ volatile("pause"); |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int find_device(uint8_t *found_bus, uint8_t *found_device, |
||||||
|
uint8_t *found_function) |
||||||
|
{ |
||||||
|
for (uint16_t bus = 0; bus < 256; ++bus) { |
||||||
|
for (uint8_t device = 0; device < 32; ++device) { |
||||||
|
for (uint8_t function = 0; function < 8; ++function) { |
||||||
|
const uint32_t identity = pci_read32( |
||||||
|
(uint8_t) bus, device, function, 0); |
||||||
|
if ((identity & 0xffffu) == E1000_VENDOR |
||||||
|
&& (identity >> 16u) == E1000_DEVICE) { |
||||||
|
*found_bus = (uint8_t) bus; |
||||||
|
*found_device = device; |
||||||
|
*found_function = function; |
||||||
|
return 1; |
||||||
|
} |
||||||
|
if (function == 0 |
||||||
|
&& (pci_read32((uint8_t) bus, device, 0, 0x0c) |
||||||
|
& UINT32_C(0x00800000)) == 0) { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
int typephp_e1000_init(void) |
||||||
|
{ |
||||||
|
uint8_t bus; |
||||||
|
uint8_t device; |
||||||
|
uint8_t function; |
||||||
|
if (!find_device(&bus, &device, &function)) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
uint64_t memory_base = 0; |
||||||
|
for (uint8_t offset = PCI_BAR_BEGIN; offset < PCI_BAR_END; offset += 4) { |
||||||
|
const uint32_t bar = pci_read32(bus, device, function, offset); |
||||||
|
if (bar != 0 && bar != UINT32_MAX && (bar & 1u) == 0) { |
||||||
|
memory_base = bar & UINT32_C(0xfffffff0); |
||||||
|
if ((bar & 6u) == 4u && offset + 4 < PCI_BAR_END) { |
||||||
|
memory_base |= (uint64_t) pci_read32( |
||||||
|
bus, device, function, (uint8_t) (offset + 4)) << 32u; |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
if (memory_base == 0) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
mmio_base = (volatile uint8_t *) typephp_vm_map_mmio( |
||||||
|
memory_base, UINT64_C(128) * 1024u); |
||||||
|
if (mmio_base == 0) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
uint32_t command = pci_read32(bus, device, function, PCI_COMMAND); |
||||||
|
pci_write32(bus, device, function, PCI_COMMAND, |
||||||
|
command | PCI_COMMAND_MEMORY | PCI_COMMAND_BUS_MASTER); |
||||||
|
|
||||||
|
register_write(REG_IMC, UINT32_MAX); |
||||||
|
register_write(REG_CTRL, register_read(REG_CTRL) | CTRL_RST); |
||||||
|
for (volatile unsigned int delay = 0; delay < 1000000u; ++delay) { |
||||||
|
__asm__ volatile("pause"); |
||||||
|
} |
||||||
|
register_write(REG_IMC, UINT32_MAX); |
||||||
|
(void) register_read(REG_ICR); |
||||||
|
register_write(REG_CTRL, register_read(REG_CTRL) | CTRL_SLU); |
||||||
|
|
||||||
|
uint32_t ral = register_read(REG_RAL); |
||||||
|
uint32_t rah = register_read(REG_RAH); |
||||||
|
if ((rah & UINT32_C(0x80000000)) != 0) { |
||||||
|
mac_address[0] = (uint8_t) ral; |
||||||
|
mac_address[1] = (uint8_t) (ral >> 8u); |
||||||
|
mac_address[2] = (uint8_t) (ral >> 16u); |
||||||
|
mac_address[3] = (uint8_t) (ral >> 24u); |
||||||
|
mac_address[4] = (uint8_t) rah; |
||||||
|
mac_address[5] = (uint8_t) (rah >> 8u); |
||||||
|
} else { |
||||||
|
uint16_t word[3]; |
||||||
|
if (!read_eeprom_word(0, &word[0]) |
||||||
|
|| !read_eeprom_word(1, &word[1]) |
||||||
|
|| !read_eeprom_word(2, &word[2])) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
for (unsigned int index = 0; index < 3; ++index) { |
||||||
|
mac_address[index * 2] = (uint8_t) word[index]; |
||||||
|
mac_address[index * 2 + 1] = (uint8_t) (word[index] >> 8u); |
||||||
|
} |
||||||
|
ral = (uint32_t) mac_address[0] |
||||||
|
| ((uint32_t) mac_address[1] << 8u) |
||||||
|
| ((uint32_t) mac_address[2] << 16u) |
||||||
|
| ((uint32_t) mac_address[3] << 24u); |
||||||
|
rah = (uint32_t) mac_address[4] |
||||||
|
| ((uint32_t) mac_address[5] << 8u) |
||||||
|
| UINT32_C(0x80000000); |
||||||
|
register_write(REG_RAL, ral); |
||||||
|
register_write(REG_RAH, rah); |
||||||
|
} |
||||||
|
|
||||||
|
memset(rx_descriptors, 0, sizeof(rx_descriptors)); |
||||||
|
memset(tx_descriptors, 0, sizeof(tx_descriptors)); |
||||||
|
for (unsigned int index = 0; index < DESCRIPTOR_COUNT; ++index) { |
||||||
|
rx_descriptors[index].address = |
||||||
|
(uint64_t) (uintptr_t) rx_buffers[index]; |
||||||
|
tx_descriptors[index].address = |
||||||
|
(uint64_t) (uintptr_t) tx_buffers[index]; |
||||||
|
tx_descriptors[index].status = TX_DESCRIPTOR_DONE; |
||||||
|
} |
||||||
|
rx_index = 0; |
||||||
|
tx_index = 0; |
||||||
|
|
||||||
|
const uint64_t rx_address = (uint64_t) (uintptr_t) rx_descriptors; |
||||||
|
register_write(REG_RDBAL, (uint32_t) rx_address); |
||||||
|
register_write(REG_RDBAH, (uint32_t) (rx_address >> 32u)); |
||||||
|
register_write(REG_RDLEN, sizeof(rx_descriptors)); |
||||||
|
register_write(REG_RDH, 0); |
||||||
|
register_write(REG_RDT, DESCRIPTOR_COUNT - 1u); |
||||||
|
register_write(REG_RCTL, RCTL_EN | RCTL_BAM | RCTL_SECRC); |
||||||
|
|
||||||
|
const uint64_t tx_address = (uint64_t) (uintptr_t) tx_descriptors; |
||||||
|
register_write(REG_TDBAL, (uint32_t) tx_address); |
||||||
|
register_write(REG_TDBAH, (uint32_t) (tx_address >> 32u)); |
||||||
|
register_write(REG_TDLEN, sizeof(tx_descriptors)); |
||||||
|
register_write(REG_TDH, 0); |
||||||
|
register_write(REG_TDT, 0); |
||||||
|
register_write(REG_TCTL, |
||||||
|
TCTL_EN | TCTL_PSP | (0x10u << 4u) | (0x40u << 12u)); |
||||||
|
register_write(REG_TIPG, UINT32_C(0x0060200a)); |
||||||
|
initialized = (register_read(REG_STATUS) & 2u) != 0; |
||||||
|
return initialized; |
||||||
|
} |
||||||
|
|
||||||
|
const uint8_t *typephp_e1000_mac(void) |
||||||
|
{ |
||||||
|
return mac_address; |
||||||
|
} |
||||||
|
|
||||||
|
int typephp_e1000_transmit(const void *data, size_t length) |
||||||
|
{ |
||||||
|
if (!initialized || data == 0 || length == 0 || length > BUFFER_SIZE) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
tx_descriptor *descriptor = &tx_descriptors[tx_index]; |
||||||
|
unsigned int attempts = 0; |
||||||
|
while ((descriptor->status & TX_DESCRIPTOR_DONE) == 0) { |
||||||
|
if (++attempts == 10000000u) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
__asm__ volatile("pause"); |
||||||
|
} |
||||||
|
memcpy(tx_buffers[tx_index], data, length); |
||||||
|
descriptor->length = (uint16_t) length; |
||||||
|
descriptor->command = TX_COMMAND_EOP | TX_COMMAND_IFCS | TX_COMMAND_RS; |
||||||
|
descriptor->status = 0; |
||||||
|
__asm__ volatile("mfence" : : : "memory"); |
||||||
|
tx_index = (uint16_t) ((tx_index + 1u) % DESCRIPTOR_COUNT); |
||||||
|
register_write(REG_TDT, tx_index); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
int typephp_e1000_receive(void *data, size_t capacity, size_t *length) |
||||||
|
{ |
||||||
|
rx_descriptor *descriptor; |
||||||
|
if (!initialized || data == 0 || length == 0) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
descriptor = &rx_descriptors[rx_index]; |
||||||
|
if ((descriptor->status & RX_DESCRIPTOR_DONE) == 0) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
*length = descriptor->length; |
||||||
|
if (descriptor->errors != 0 || *length == 0 || *length > capacity) { |
||||||
|
*length = 0; |
||||||
|
} else { |
||||||
|
memcpy(data, rx_buffers[rx_index], *length); |
||||||
|
} |
||||||
|
descriptor->status = 0; |
||||||
|
__asm__ volatile("mfence" : : : "memory"); |
||||||
|
register_write(REG_RDT, rx_index); |
||||||
|
rx_index = (uint16_t) ((rx_index + 1u) % DESCRIPTOR_COUNT); |
||||||
|
return *length != 0; |
||||||
|
} |
||||||
@ -0,0 +1,12 @@ |
|||||||
|
#ifndef TYPEPHP_OS_E1000_H |
||||||
|
#define TYPEPHP_OS_E1000_H |
||||||
|
|
||||||
|
#include <stddef.h> |
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
int typephp_e1000_init(void); |
||||||
|
const uint8_t *typephp_e1000_mac(void); |
||||||
|
int typephp_e1000_transmit(const void *data, size_t length); |
||||||
|
int typephp_e1000_receive(void *data, size_t capacity, size_t *length); |
||||||
|
|
||||||
|
#endif |
||||||
@ -0,0 +1,70 @@ |
|||||||
|
#ifndef TYPEPHP_OS_LWIPOPTS_H |
||||||
|
#define TYPEPHP_OS_LWIPOPTS_H |
||||||
|
|
||||||
|
/* The network stack targets a desktop-class x86-64 machine. These values are
|
||||||
|
* deliberately sized for throughput and concurrent libcurl handles rather |
||||||
|
* than for a microcontroller RAM budget. */ |
||||||
|
#define NO_SYS 1 |
||||||
|
#define SYS_LIGHTWEIGHT_PROT 0 |
||||||
|
#define LWIP_NETCONN 0 |
||||||
|
#define LWIP_SOCKET 0 |
||||||
|
|
||||||
|
#define LWIP_IPV4 1 |
||||||
|
#define LWIP_IPV6 0 |
||||||
|
#define LWIP_ARP 1 |
||||||
|
#define LWIP_ETHERNET 1 |
||||||
|
#define LWIP_ICMP 1 |
||||||
|
#define LWIP_RAW 0 |
||||||
|
#define LWIP_UDP 1 |
||||||
|
#define LWIP_TCP 1 |
||||||
|
#define LWIP_DNS 1 |
||||||
|
#define LWIP_DHCP 0 |
||||||
|
#define LWIP_AUTOIP 0 |
||||||
|
#define IP_FORWARD 0 |
||||||
|
#define IP_REASSEMBLY 1 |
||||||
|
#define IP_FRAG 1 |
||||||
|
|
||||||
|
#define MEM_ALIGNMENT 16 |
||||||
|
#define MEM_SIZE (16 * 1024 * 1024) |
||||||
|
#define MEMP_NUM_PBUF 2048 |
||||||
|
#define MEMP_NUM_UDP_PCB 64 |
||||||
|
#define MEMP_NUM_TCP_PCB 256 |
||||||
|
#define MEMP_NUM_TCP_PCB_LISTEN 0 |
||||||
|
#define MEMP_NUM_TCP_SEG 4096 |
||||||
|
#define MEMP_NUM_SYS_TIMEOUT 128 |
||||||
|
#define PBUF_POOL_SIZE 1024 |
||||||
|
#define PBUF_POOL_BUFSIZE 2048 |
||||||
|
|
||||||
|
#define TCP_MSS 1460 |
||||||
|
#define TCP_SND_BUF (1024 * 1024) |
||||||
|
#define TCP_SND_QUEUELEN (4 * TCP_SND_BUF / TCP_MSS) |
||||||
|
#define TCP_SNDLOWAT (65535 - 4 * TCP_MSS - 1) |
||||||
|
#define TCP_WND (1024 * 1024) |
||||||
|
#define LWIP_WND_SCALE 1 |
||||||
|
#define TCP_RCV_SCALE 5 |
||||||
|
#define TCP_QUEUE_OOSEQ 1 |
||||||
|
#define TCP_OVERSIZE TCP_MSS |
||||||
|
#define TCP_LISTEN_BACKLOG 0 |
||||||
|
|
||||||
|
#define ARP_TABLE_SIZE 256 |
||||||
|
#define ARP_QUEUEING 1 |
||||||
|
#define DNS_TABLE_SIZE 32 |
||||||
|
#define DNS_MAX_NAME_LENGTH 256 |
||||||
|
#define DNS_MAX_SERVERS 2 |
||||||
|
#define LWIP_DNS_SECURE 7 |
||||||
|
#define LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS 1 |
||||||
|
|
||||||
|
#define LWIP_CHECKSUM_CTRL_PER_NETIF 0 |
||||||
|
#define CHECKSUM_GEN_IP 1 |
||||||
|
#define CHECKSUM_GEN_UDP 1 |
||||||
|
#define CHECKSUM_GEN_TCP 1 |
||||||
|
#define CHECKSUM_CHECK_IP 1 |
||||||
|
#define CHECKSUM_CHECK_UDP 1 |
||||||
|
#define CHECKSUM_CHECK_TCP 1 |
||||||
|
|
||||||
|
#define LWIP_STATS 0 |
||||||
|
#define LWIP_DEBUG 0 |
||||||
|
#define LWIP_TIMERS 1 |
||||||
|
#define LWIP_TIMERS_CUSTOM 0 |
||||||
|
|
||||||
|
#endif |
||||||
@ -0,0 +1,209 @@ |
|||||||
|
#include "network.h" |
||||||
|
#include "e1000.h" |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
#include "lwip/dns.h" |
||||||
|
#include "lwip/etharp.h" |
||||||
|
#include "lwip/init.h" |
||||||
|
#include "lwip/netif.h" |
||||||
|
#include "lwip/pbuf.h" |
||||||
|
#include "lwip/timeouts.h" |
||||||
|
#include "netif/ethernet.h" |
||||||
|
|
||||||
|
#include <errno.h> |
||||||
|
|
||||||
|
extern void typephp_os_panic(const char *message); |
||||||
|
extern uint64_t typephp_os_timer_milliseconds(void); |
||||||
|
|
||||||
|
static struct netif primary_netif; |
||||||
|
static int ready; |
||||||
|
static volatile unsigned long dns_generation; |
||||||
|
static volatile int dns_result; |
||||||
|
static uint32_t dns_address; |
||||||
|
|
||||||
|
enum { |
||||||
|
DNS_PENDING = 0, |
||||||
|
DNS_RESOLVED = 1, |
||||||
|
DNS_FAILED = 2, |
||||||
|
DNS_ATTEMPTS = 3, |
||||||
|
DNS_ATTEMPT_TIMEOUT_MS = 5000, |
||||||
|
}; |
||||||
|
|
||||||
|
void typephp_network_assert(const char *message) |
||||||
|
{ |
||||||
|
(void) message; |
||||||
|
typephp_os_panic("lwIP assertion failed\n"); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t typephp_network_random_u32(void) |
||||||
|
{ |
||||||
|
uint32_t low; |
||||||
|
uint32_t high; |
||||||
|
__asm__ volatile("rdtsc" : "=a"(low), "=d"(high)); |
||||||
|
low ^= high + (uint32_t) typephp_network_milliseconds(); |
||||||
|
low ^= low << 13u; |
||||||
|
low ^= low >> 17u; |
||||||
|
low ^= low << 5u; |
||||||
|
return low; |
||||||
|
} |
||||||
|
|
||||||
|
uint64_t typephp_network_milliseconds(void) |
||||||
|
{ |
||||||
|
return typephp_os_timer_milliseconds(); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t sys_now(void) |
||||||
|
{ |
||||||
|
return (uint32_t) typephp_network_milliseconds(); |
||||||
|
} |
||||||
|
|
||||||
|
static err_t link_output(struct netif *netif, struct pbuf *packet) |
||||||
|
{ |
||||||
|
uint8_t frame[2048]; |
||||||
|
(void) netif; |
||||||
|
if (packet->tot_len > sizeof(frame) |
||||||
|
|| pbuf_copy_partial(packet, frame, packet->tot_len, 0) |
||||||
|
!= packet->tot_len |
||||||
|
|| !typephp_e1000_transmit(frame, packet->tot_len)) { |
||||||
|
return ERR_IF; |
||||||
|
} |
||||||
|
return ERR_OK; |
||||||
|
} |
||||||
|
|
||||||
|
static err_t interface_init(struct netif *netif) |
||||||
|
{ |
||||||
|
const uint8_t *mac = typephp_e1000_mac(); |
||||||
|
netif->name[0] = 'e'; |
||||||
|
netif->name[1] = '0'; |
||||||
|
netif->output = etharp_output; |
||||||
|
netif->linkoutput = link_output; |
||||||
|
netif->mtu = 1500; |
||||||
|
netif->hwaddr_len = 6; |
||||||
|
for (unsigned int index = 0; index < 6; ++index) { |
||||||
|
netif->hwaddr[index] = mac[index]; |
||||||
|
} |
||||||
|
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP |
||||||
|
| NETIF_FLAG_ETHERNET | NETIF_FLAG_LINK_UP; |
||||||
|
return ERR_OK; |
||||||
|
} |
||||||
|
|
||||||
|
int typephp_network_init(void) |
||||||
|
{ |
||||||
|
ip4_addr_t address; |
||||||
|
ip4_addr_t netmask; |
||||||
|
ip4_addr_t gateway; |
||||||
|
ip_addr_t dns_server; |
||||||
|
if (ready) { |
||||||
|
return 1; |
||||||
|
} |
||||||
|
if (!typephp_e1000_init()) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
lwip_init(); |
||||||
|
IP4_ADDR(&address, 10, 0, 2, 15); |
||||||
|
IP4_ADDR(&netmask, 255, 255, 255, 0); |
||||||
|
IP4_ADDR(&gateway, 10, 0, 2, 2); |
||||||
|
if (netif_add(&primary_netif, &address, &netmask, &gateway, |
||||||
|
0, interface_init, ethernet_input) == 0) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
netif_set_default(&primary_netif); |
||||||
|
netif_set_up(&primary_netif); |
||||||
|
netif_set_link_up(&primary_netif); |
||||||
|
IP_ADDR4(&dns_server, 10, 0, 2, 3); |
||||||
|
dns_setserver(0, &dns_server); |
||||||
|
ready = 1; |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
int typephp_network_ready(void) |
||||||
|
{ |
||||||
|
return ready; |
||||||
|
} |
||||||
|
|
||||||
|
void typephp_network_poll(void) |
||||||
|
{ |
||||||
|
uint8_t frame[2048]; |
||||||
|
size_t length; |
||||||
|
if (!ready) { |
||||||
|
return; |
||||||
|
} |
||||||
|
while (typephp_e1000_receive(frame, sizeof(frame), &length)) { |
||||||
|
struct pbuf *packet = pbuf_alloc(PBUF_RAW, (u16_t) length, PBUF_POOL); |
||||||
|
if (packet != 0) { |
||||||
|
if (pbuf_take(packet, frame, length) == ERR_OK |
||||||
|
&& primary_netif.input(packet, &primary_netif) == ERR_OK) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
pbuf_free(packet); |
||||||
|
} |
||||||
|
} |
||||||
|
sys_check_timeouts(); |
||||||
|
} |
||||||
|
|
||||||
|
static void dns_callback(const char *hostname, const ip_addr_t *address, |
||||||
|
void *argument) |
||||||
|
{ |
||||||
|
const unsigned long generation = (unsigned long) (uintptr_t) argument; |
||||||
|
(void) hostname; |
||||||
|
if (generation != dns_generation || dns_result != DNS_PENDING) { |
||||||
|
return; |
||||||
|
} |
||||||
|
if (address == 0 || !IP_IS_V4(address)) { |
||||||
|
dns_result = DNS_FAILED; |
||||||
|
return; |
||||||
|
} |
||||||
|
dns_address = ip_2_ip4(address)->addr; |
||||||
|
dns_result = DNS_RESOLVED; |
||||||
|
} |
||||||
|
|
||||||
|
long typephp_network_resolve_ipv4(const char *hostname, uint32_t *address) |
||||||
|
{ |
||||||
|
ip_addr_t immediate; |
||||||
|
int attempt; |
||||||
|
long result = -EHOSTUNREACH; |
||||||
|
if (!ready) { |
||||||
|
return -ENETDOWN; |
||||||
|
} |
||||||
|
if (hostname == 0 || hostname[0] == '\0' || address == 0) { |
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
|
||||||
|
/* QEMU user networking can reject the first DNS request while its
|
||||||
|
* virtual link is still settling. Keep that transient out of userspace; |
||||||
|
* later lookups normally hit lwIP's cache. */ |
||||||
|
for (attempt = 0; attempt < DNS_ATTEMPTS; ++attempt) { |
||||||
|
const uint64_t started = typephp_network_milliseconds(); |
||||||
|
const unsigned long generation = ++dns_generation; |
||||||
|
const err_t error = dns_gethostbyname(hostname, &immediate, |
||||||
|
dns_callback, (void *) (uintptr_t) generation); |
||||||
|
dns_result = DNS_PENDING; |
||||||
|
if (error == ERR_OK) { |
||||||
|
if (!IP_IS_V4(&immediate)) { |
||||||
|
return -EAFNOSUPPORT; |
||||||
|
} |
||||||
|
*address = ip_2_ip4(&immediate)->addr; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
if (error == ERR_INPROGRESS) { |
||||||
|
while (dns_result == DNS_PENDING |
||||||
|
&& typephp_network_milliseconds() - started |
||||||
|
< DNS_ATTEMPT_TIMEOUT_MS) { |
||||||
|
typephp_network_poll(); |
||||||
|
__asm__ volatile("sti; hlt; cli" : : : "memory"); |
||||||
|
} |
||||||
|
if (dns_result == DNS_RESOLVED) { |
||||||
|
*address = dns_address; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
result = dns_result == DNS_FAILED |
||||||
|
? -EHOSTUNREACH : -ETIMEDOUT; |
||||||
|
} else { |
||||||
|
result = error == ERR_MEM ? -ENOMEM : -EHOSTUNREACH; |
||||||
|
} |
||||||
|
++dns_generation; |
||||||
|
typephp_network_poll(); |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
@ -0,0 +1,12 @@ |
|||||||
|
#ifndef TYPEPHP_OS_NETWORK_H |
||||||
|
#define TYPEPHP_OS_NETWORK_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
int typephp_network_init(void); |
||||||
|
int typephp_network_ready(void); |
||||||
|
void typephp_network_poll(void); |
||||||
|
uint64_t typephp_network_milliseconds(void); |
||||||
|
long typephp_network_resolve_ipv4(const char *hostname, uint32_t *address); |
||||||
|
|
||||||
|
#endif |
||||||
@ -0,0 +1,648 @@ |
|||||||
|
/*
|
||||||
|
+----------------------------------------------------------------------+ |
||||||
|
| TypePHP OS | |
||||||
|
+----------------------------------------------------------------------+ |
||||||
|
| Linux-compatible userspace sockets backed by kernel-owned lwIP TCP. | |
||||||
|
| SPDX-License-Identifier: BSD-3-Clause | |
||||||
|
+----------------------------------------------------------------------+ |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "socket.h" |
||||||
|
#include "net/network.h" |
||||||
|
|
||||||
|
#include <errno.h> |
||||||
|
#include <fcntl.h> |
||||||
|
#include <stdint.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#include "lwip/ip_addr.h" |
||||||
|
#include "lwip/pbuf.h" |
||||||
|
#include "lwip/tcp.h" |
||||||
|
|
||||||
|
enum { |
||||||
|
SOCKET_FD_BEGIN = 128, |
||||||
|
SOCKET_COUNT = 256, |
||||||
|
LINUX_AF_INET = 2, |
||||||
|
LINUX_SOCK_STREAM = 1, |
||||||
|
LINUX_SOCK_TYPE_MASK = 15, |
||||||
|
LINUX_SOCK_NONBLOCK = 04000, |
||||||
|
LINUX_SOCK_CLOEXEC = 02000000, |
||||||
|
LINUX_IPPROTO_TCP = 6, |
||||||
|
LINUX_SOL_SOCKET = 1, |
||||||
|
LINUX_SO_ERROR = 4, |
||||||
|
LINUX_SO_KEEPALIVE = 9, |
||||||
|
LINUX_SO_RCVBUF = 8, |
||||||
|
LINUX_SO_SNDBUF = 7, |
||||||
|
LINUX_TCP_NODELAY = 1, |
||||||
|
LINUX_F_GETFD = 1, |
||||||
|
LINUX_F_SETFD = 2, |
||||||
|
LINUX_F_GETFL = 3, |
||||||
|
LINUX_F_SETFL = 4, |
||||||
|
LINUX_FD_CLOEXEC = 1, |
||||||
|
LINUX_O_RDWR = 2, |
||||||
|
LINUX_O_NONBLOCK = 04000, |
||||||
|
LINUX_MSG_PEEK = 2, |
||||||
|
LINUX_MSG_DONTWAIT = 0x40, |
||||||
|
LINUX_MSG_NOSIGNAL = 0x4000, |
||||||
|
LINUX_MSG_MORE = 0x8000, |
||||||
|
LINUX_POLLIN = 0x001, |
||||||
|
LINUX_POLLOUT = 0x004, |
||||||
|
LINUX_POLLERR = 0x008, |
||||||
|
LINUX_POLLHUP = 0x010, |
||||||
|
LINUX_POLLNVAL = 0x020, |
||||||
|
SOCKET_NEW = 0, |
||||||
|
SOCKET_CONNECTING = 1, |
||||||
|
SOCKET_CONNECTED = 2, |
||||||
|
SOCKET_PEER_CLOSED = 3, |
||||||
|
SOCKET_FAILED = 4, |
||||||
|
}; |
||||||
|
|
||||||
|
typedef struct __attribute__((packed)) { |
||||||
|
uint16_t family; |
||||||
|
uint16_t port; |
||||||
|
uint32_t address; |
||||||
|
uint8_t zero[8]; |
||||||
|
} linux_sockaddr_in; |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
int allocated; |
||||||
|
int domain; |
||||||
|
int type; |
||||||
|
int protocol; |
||||||
|
int status_flags; |
||||||
|
int descriptor_flags; |
||||||
|
int pending_error; |
||||||
|
int state; |
||||||
|
unsigned long owner; |
||||||
|
struct tcp_pcb *pcb; |
||||||
|
struct pbuf *received; |
||||||
|
} kernel_socket; |
||||||
|
|
||||||
|
static kernel_socket sockets[SOCKET_COUNT]; |
||||||
|
|
||||||
|
static kernel_socket *socket_for_fd(int fd) |
||||||
|
{ |
||||||
|
if (fd < SOCKET_FD_BEGIN || fd >= SOCKET_FD_BEGIN + SOCKET_COUNT) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
kernel_socket *socket = &sockets[fd - SOCKET_FD_BEGIN]; |
||||||
|
return socket->allocated ? socket : 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int network_errno(err_t error) |
||||||
|
{ |
||||||
|
switch (error) { |
||||||
|
case ERR_OK: return 0; |
||||||
|
case ERR_MEM: return ENOMEM; |
||||||
|
case ERR_BUF: return ENOBUFS; |
||||||
|
case ERR_TIMEOUT: return ETIMEDOUT; |
||||||
|
case ERR_RTE: return ENETUNREACH; |
||||||
|
case ERR_INPROGRESS: return EINPROGRESS; |
||||||
|
case ERR_VAL: return EINVAL; |
||||||
|
case ERR_WOULDBLOCK: return EAGAIN; |
||||||
|
case ERR_USE: return EADDRINUSE; |
||||||
|
case ERR_ALREADY: return EALREADY; |
||||||
|
case ERR_ISCONN: return EISCONN; |
||||||
|
case ERR_ABRT: return ECONNABORTED; |
||||||
|
case ERR_RST: return ECONNRESET; |
||||||
|
case ERR_CLSD: return ENOTCONN; |
||||||
|
case ERR_CONN: return ENOTCONN; |
||||||
|
case ERR_ARG: return EINVAL; |
||||||
|
case ERR_IF: |
||||||
|
default: |
||||||
|
return ENETDOWN; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void wait_for_network_event(void) |
||||||
|
{ |
||||||
|
typephp_network_poll(); |
||||||
|
__asm__ volatile("sti; hlt; cli" : : : "memory"); |
||||||
|
typephp_network_poll(); |
||||||
|
} |
||||||
|
|
||||||
|
static err_t connected_callback(void *argument, struct tcp_pcb *pcb, err_t error) |
||||||
|
{ |
||||||
|
kernel_socket *socket = (kernel_socket *) argument; |
||||||
|
(void) pcb; |
||||||
|
if (error == ERR_OK) { |
||||||
|
socket->state = SOCKET_CONNECTED; |
||||||
|
socket->pending_error = 0; |
||||||
|
} else { |
||||||
|
socket->state = SOCKET_FAILED; |
||||||
|
socket->pending_error = network_errno(error); |
||||||
|
} |
||||||
|
return ERR_OK; |
||||||
|
} |
||||||
|
|
||||||
|
static err_t received_callback(void *argument, struct tcp_pcb *pcb, |
||||||
|
struct pbuf *packet, err_t error) |
||||||
|
{ |
||||||
|
kernel_socket *socket = (kernel_socket *) argument; |
||||||
|
(void) pcb; |
||||||
|
if (error != ERR_OK) { |
||||||
|
if (packet != 0) { |
||||||
|
pbuf_free(packet); |
||||||
|
} |
||||||
|
socket->pending_error = network_errno(error); |
||||||
|
return ERR_OK; |
||||||
|
} |
||||||
|
if (packet == 0) { |
||||||
|
socket->state = SOCKET_PEER_CLOSED; |
||||||
|
return ERR_OK; |
||||||
|
} |
||||||
|
if (socket->received == 0) { |
||||||
|
socket->received = packet; |
||||||
|
} else { |
||||||
|
pbuf_cat(socket->received, packet); |
||||||
|
} |
||||||
|
return ERR_OK; |
||||||
|
} |
||||||
|
|
||||||
|
static void error_callback(void *argument, err_t error) |
||||||
|
{ |
||||||
|
kernel_socket *socket = (kernel_socket *) argument; |
||||||
|
socket->pcb = 0; |
||||||
|
socket->state = SOCKET_FAILED; |
||||||
|
socket->pending_error = network_errno(error); |
||||||
|
} |
||||||
|
|
||||||
|
static void install_callbacks(kernel_socket *socket) |
||||||
|
{ |
||||||
|
tcp_arg(socket->pcb, socket); |
||||||
|
tcp_recv(socket->pcb, received_callback); |
||||||
|
tcp_err(socket->pcb, error_callback); |
||||||
|
} |
||||||
|
|
||||||
|
static void release_socket(kernel_socket *socket) |
||||||
|
{ |
||||||
|
if (socket->pcb != 0) { |
||||||
|
tcp_arg(socket->pcb, 0); |
||||||
|
tcp_recv(socket->pcb, 0); |
||||||
|
tcp_err(socket->pcb, 0); |
||||||
|
if (tcp_close(socket->pcb) != ERR_OK) { |
||||||
|
tcp_abort(socket->pcb); |
||||||
|
} |
||||||
|
} |
||||||
|
if (socket->received != 0) { |
||||||
|
pbuf_free(socket->received); |
||||||
|
} |
||||||
|
memset(socket, 0, sizeof(*socket)); |
||||||
|
} |
||||||
|
|
||||||
|
int typephp_socket_is_fd(int fd) |
||||||
|
{ |
||||||
|
return socket_for_fd(fd) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
long typephp_socket_open(unsigned long owner, int domain, int type, int protocol) |
||||||
|
{ |
||||||
|
const int base_type = type & LINUX_SOCK_TYPE_MASK; |
||||||
|
const int flags = type & ~LINUX_SOCK_TYPE_MASK; |
||||||
|
if (domain != LINUX_AF_INET) { |
||||||
|
return -EAFNOSUPPORT; |
||||||
|
} |
||||||
|
if (base_type != LINUX_SOCK_STREAM) { |
||||||
|
return -ESOCKTNOSUPPORT; |
||||||
|
} |
||||||
|
if ((flags & ~(LINUX_SOCK_NONBLOCK | LINUX_SOCK_CLOEXEC)) != 0) { |
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
if (protocol != 0 && protocol != LINUX_IPPROTO_TCP) { |
||||||
|
return -EPROTONOSUPPORT; |
||||||
|
} |
||||||
|
if (!typephp_network_ready()) { |
||||||
|
return -ENETDOWN; |
||||||
|
} |
||||||
|
for (int index = 0; index < SOCKET_COUNT; ++index) { |
||||||
|
if (!sockets[index].allocated) { |
||||||
|
struct tcp_pcb *pcb = tcp_new_ip_type(IPADDR_TYPE_V4); |
||||||
|
if (pcb == 0) { |
||||||
|
return -ENOBUFS; |
||||||
|
} |
||||||
|
kernel_socket *socket = &sockets[index]; |
||||||
|
memset(socket, 0, sizeof(*socket)); |
||||||
|
socket->allocated = 1; |
||||||
|
socket->domain = domain; |
||||||
|
socket->type = base_type; |
||||||
|
socket->protocol = LINUX_IPPROTO_TCP; |
||||||
|
socket->status_flags = LINUX_O_RDWR |
||||||
|
| ((flags & LINUX_SOCK_NONBLOCK) != 0 ? LINUX_O_NONBLOCK : 0); |
||||||
|
socket->descriptor_flags = |
||||||
|
(flags & LINUX_SOCK_CLOEXEC) != 0 ? LINUX_FD_CLOEXEC : 0; |
||||||
|
socket->owner = owner; |
||||||
|
socket->state = SOCKET_NEW; |
||||||
|
socket->pcb = pcb; |
||||||
|
install_callbacks(socket); |
||||||
|
return SOCKET_FD_BEGIN + index; |
||||||
|
} |
||||||
|
} |
||||||
|
return -EMFILE; |
||||||
|
} |
||||||
|
|
||||||
|
long typephp_socket_close(int fd) |
||||||
|
{ |
||||||
|
kernel_socket *socket = socket_for_fd(fd); |
||||||
|
if (socket == 0) { |
||||||
|
return -EBADF; |
||||||
|
} |
||||||
|
release_socket(socket); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
long typephp_socket_read(int fd, void *buffer, size_t length) |
||||||
|
{ |
||||||
|
return typephp_socket_recvfrom(fd, buffer, length, 0, 0, 0); |
||||||
|
} |
||||||
|
|
||||||
|
long typephp_socket_write(int fd, const void *buffer, size_t length) |
||||||
|
{ |
||||||
|
return typephp_socket_sendto(fd, buffer, length, 0, 0, 0); |
||||||
|
} |
||||||
|
|
||||||
|
long typephp_socket_connect(int fd, const void *address, typephp_socklen_t length) |
||||||
|
{ |
||||||
|
kernel_socket *socket = socket_for_fd(fd); |
||||||
|
const linux_sockaddr_in *remote = (const linux_sockaddr_in *) address; |
||||||
|
ip_addr_t remote_address; |
||||||
|
err_t error; |
||||||
|
if (socket == 0) { |
||||||
|
return -EBADF; |
||||||
|
} |
||||||
|
if (address == 0 || length < sizeof(*remote)) { |
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
if (remote->family != LINUX_AF_INET) { |
||||||
|
return -EAFNOSUPPORT; |
||||||
|
} |
||||||
|
if (socket->state == SOCKET_CONNECTING) { |
||||||
|
return -EALREADY; |
||||||
|
} |
||||||
|
if (socket->state == SOCKET_CONNECTED) { |
||||||
|
return -EISCONN; |
||||||
|
} |
||||||
|
if (socket->state != SOCKET_NEW || socket->pcb == 0) { |
||||||
|
return -ENOTCONN; |
||||||
|
} |
||||||
|
IP_ADDR4(&remote_address, |
||||||
|
(uint8_t) remote->address, |
||||||
|
(uint8_t) (remote->address >> 8u), |
||||||
|
(uint8_t) (remote->address >> 16u), |
||||||
|
(uint8_t) (remote->address >> 24u)); |
||||||
|
socket->state = SOCKET_CONNECTING; |
||||||
|
socket->pending_error = 0; |
||||||
|
error = tcp_connect(socket->pcb, &remote_address, |
||||||
|
lwip_ntohs(remote->port), connected_callback); |
||||||
|
if (error != ERR_OK) { |
||||||
|
socket->state = SOCKET_FAILED; |
||||||
|
socket->pending_error = network_errno(error); |
||||||
|
return -socket->pending_error; |
||||||
|
} |
||||||
|
if ((socket->status_flags & LINUX_O_NONBLOCK) != 0) { |
||||||
|
return -EINPROGRESS; |
||||||
|
} |
||||||
|
while (socket->state == SOCKET_CONNECTING) { |
||||||
|
wait_for_network_event(); |
||||||
|
} |
||||||
|
return socket->state == SOCKET_CONNECTED ? 0 |
||||||
|
: -(socket->pending_error != 0 ? socket->pending_error : ECONNREFUSED); |
||||||
|
} |
||||||
|
|
||||||
|
long typephp_socket_bind(int fd, const void *address, typephp_socklen_t length) |
||||||
|
{ |
||||||
|
kernel_socket *socket = socket_for_fd(fd); |
||||||
|
const linux_sockaddr_in *local = (const linux_sockaddr_in *) address; |
||||||
|
ip_addr_t local_address; |
||||||
|
err_t error; |
||||||
|
if (socket == 0) { |
||||||
|
return -EBADF; |
||||||
|
} |
||||||
|
if (address == 0 || length < sizeof(*local)) { |
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
if (local->family != LINUX_AF_INET) { |
||||||
|
return -EAFNOSUPPORT; |
||||||
|
} |
||||||
|
if (socket->state != SOCKET_NEW || socket->pcb == 0) { |
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
IP_ADDR4(&local_address, |
||||||
|
(uint8_t) local->address, |
||||||
|
(uint8_t) (local->address >> 8u), |
||||||
|
(uint8_t) (local->address >> 16u), |
||||||
|
(uint8_t) (local->address >> 24u)); |
||||||
|
error = tcp_bind(socket->pcb, |
||||||
|
local->address == 0 ? IP_ANY_TYPE : &local_address, |
||||||
|
lwip_ntohs(local->port)); |
||||||
|
return error == ERR_OK ? 0 : -network_errno(error); |
||||||
|
} |
||||||
|
|
||||||
|
long typephp_socket_sendto(int fd, const void *buffer, size_t length, int flags, |
||||||
|
const void *address, typephp_socklen_t address_length) |
||||||
|
{ |
||||||
|
kernel_socket *socket = socket_for_fd(fd); |
||||||
|
const uint8_t *input = (const uint8_t *) buffer; |
||||||
|
size_t written = 0; |
||||||
|
(void) address; |
||||||
|
(void) address_length; |
||||||
|
if (socket == 0) { |
||||||
|
return -EBADF; |
||||||
|
} |
||||||
|
if ((flags & ~(LINUX_MSG_DONTWAIT | LINUX_MSG_NOSIGNAL | LINUX_MSG_MORE)) != 0) { |
||||||
|
return -EOPNOTSUPP; |
||||||
|
} |
||||||
|
if (buffer == 0 && length != 0) { |
||||||
|
return -EFAULT; |
||||||
|
} |
||||||
|
if (socket->state != SOCKET_CONNECTED || socket->pcb == 0) { |
||||||
|
return -ENOTCONN; |
||||||
|
} |
||||||
|
while (written < length) { |
||||||
|
size_t chunk = length - written; |
||||||
|
const size_t available = tcp_sndbuf(socket->pcb); |
||||||
|
if (chunk > UINT16_MAX) { |
||||||
|
chunk = UINT16_MAX; |
||||||
|
} |
||||||
|
if (chunk > available) { |
||||||
|
chunk = available; |
||||||
|
} |
||||||
|
if (chunk == 0) { |
||||||
|
if ((socket->status_flags & LINUX_O_NONBLOCK) != 0 |
||||||
|
|| (flags & LINUX_MSG_DONTWAIT) != 0) { |
||||||
|
return written != 0 ? (long) written : -EAGAIN; |
||||||
|
} |
||||||
|
wait_for_network_event(); |
||||||
|
if (socket->state != SOCKET_CONNECTED || socket->pcb == 0) { |
||||||
|
return written != 0 ? (long) written : -ENOTCONN; |
||||||
|
} |
||||||
|
continue; |
||||||
|
} |
||||||
|
err_t error = tcp_write(socket->pcb, input + written, |
||||||
|
(u16_t) chunk, TCP_WRITE_FLAG_COPY); |
||||||
|
if (error == ERR_MEM) { |
||||||
|
if ((socket->status_flags & LINUX_O_NONBLOCK) != 0 |
||||||
|
|| (flags & LINUX_MSG_DONTWAIT) != 0) { |
||||||
|
return written != 0 ? (long) written : -EAGAIN; |
||||||
|
} |
||||||
|
wait_for_network_event(); |
||||||
|
continue; |
||||||
|
} |
||||||
|
if (error != ERR_OK) { |
||||||
|
return written != 0 ? (long) written : -network_errno(error); |
||||||
|
} |
||||||
|
written += chunk; |
||||||
|
(void) tcp_output(socket->pcb); |
||||||
|
typephp_network_poll(); |
||||||
|
} |
||||||
|
return (long) written; |
||||||
|
} |
||||||
|
|
||||||
|
long typephp_socket_recvfrom(int fd, void *buffer, size_t length, int flags, |
||||||
|
void *address, typephp_socklen_t *address_length) |
||||||
|
{ |
||||||
|
kernel_socket *socket = socket_for_fd(fd); |
||||||
|
uint8_t *output = (uint8_t *) buffer; |
||||||
|
size_t copied = 0; |
||||||
|
(void) address; |
||||||
|
(void) address_length; |
||||||
|
if (socket == 0) { |
||||||
|
return -EBADF; |
||||||
|
} |
||||||
|
if ((flags & ~(LINUX_MSG_PEEK | LINUX_MSG_DONTWAIT)) != 0) { |
||||||
|
return -EOPNOTSUPP; |
||||||
|
} |
||||||
|
if (buffer == 0 && length != 0) { |
||||||
|
return -EFAULT; |
||||||
|
} |
||||||
|
if (length == 0) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
while (socket->received == 0) { |
||||||
|
if (socket->state == SOCKET_PEER_CLOSED) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
if (socket->state == SOCKET_FAILED) { |
||||||
|
return -(socket->pending_error != 0 |
||||||
|
? socket->pending_error : ECONNRESET); |
||||||
|
} |
||||||
|
if (socket->state != SOCKET_CONNECTED || socket->pcb == 0) { |
||||||
|
return -ENOTCONN; |
||||||
|
} |
||||||
|
if ((socket->status_flags & LINUX_O_NONBLOCK) != 0 |
||||||
|
|| (flags & LINUX_MSG_DONTWAIT) != 0) { |
||||||
|
return -EAGAIN; |
||||||
|
} |
||||||
|
wait_for_network_event(); |
||||||
|
} |
||||||
|
copied = length < socket->received->tot_len |
||||||
|
? length : socket->received->tot_len; |
||||||
|
if (pbuf_copy_partial(socket->received, output, copied, 0) != copied) { |
||||||
|
return -EIO; |
||||||
|
} |
||||||
|
if ((flags & LINUX_MSG_PEEK) == 0) { |
||||||
|
size_t consumed = copied; |
||||||
|
while (consumed != 0) { |
||||||
|
const u16_t part = consumed > UINT16_MAX |
||||||
|
? UINT16_MAX : (u16_t) consumed; |
||||||
|
socket->received = pbuf_free_header(socket->received, part); |
||||||
|
tcp_recved(socket->pcb, part); |
||||||
|
consumed -= part; |
||||||
|
} |
||||||
|
} |
||||||
|
return (long) copied; |
||||||
|
} |
||||||
|
|
||||||
|
long typephp_socket_shutdown(int fd, int how) |
||||||
|
{ |
||||||
|
kernel_socket *socket = socket_for_fd(fd); |
||||||
|
err_t error; |
||||||
|
if (socket == 0) { |
||||||
|
return -EBADF; |
||||||
|
} |
||||||
|
if (how < 0 || how > 2) { |
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
if (socket->pcb == 0 |
||||||
|
|| (socket->state != SOCKET_CONNECTED |
||||||
|
&& socket->state != SOCKET_PEER_CLOSED)) { |
||||||
|
return -ENOTCONN; |
||||||
|
} |
||||||
|
error = tcp_shutdown(socket->pcb, how != 1, how != 0); |
||||||
|
return error == ERR_OK ? 0 : -network_errno(error); |
||||||
|
} |
||||||
|
|
||||||
|
long typephp_socket_getname(int fd, void *address, typephp_socklen_t *length, |
||||||
|
int peer) |
||||||
|
{ |
||||||
|
kernel_socket *socket = socket_for_fd(fd); |
||||||
|
linux_sockaddr_in result; |
||||||
|
const ip_addr_t *ip; |
||||||
|
uint16_t port; |
||||||
|
if (socket == 0) { |
||||||
|
return -EBADF; |
||||||
|
} |
||||||
|
if (socket->pcb == 0 || (peer && socket->state != SOCKET_CONNECTED)) { |
||||||
|
return -ENOTCONN; |
||||||
|
} |
||||||
|
if (address == 0 || length == 0 || *length < sizeof(result)) { |
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
memset(&result, 0, sizeof(result)); |
||||||
|
result.family = LINUX_AF_INET; |
||||||
|
ip = peer ? &socket->pcb->remote_ip : &socket->pcb->local_ip; |
||||||
|
port = peer ? socket->pcb->remote_port : socket->pcb->local_port; |
||||||
|
result.address = ip_2_ip4(ip)->addr; |
||||||
|
result.port = lwip_htons(port); |
||||||
|
memcpy(address, &result, sizeof(result)); |
||||||
|
*length = sizeof(result); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
long typephp_socket_setsockopt(int fd, int level, int option, |
||||||
|
const void *value, typephp_socklen_t length) |
||||||
|
{ |
||||||
|
kernel_socket *socket = socket_for_fd(fd); |
||||||
|
int enabled; |
||||||
|
if (socket == 0) { |
||||||
|
return -EBADF; |
||||||
|
} |
||||||
|
if (value == 0 || length < sizeof(int) || socket->pcb == 0) { |
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
enabled = *(const int *) value; |
||||||
|
if (level == LINUX_SOL_SOCKET && option == LINUX_SO_KEEPALIVE) { |
||||||
|
if (enabled) { |
||||||
|
ip_set_option(socket->pcb, SOF_KEEPALIVE); |
||||||
|
} else { |
||||||
|
ip_reset_option(socket->pcb, SOF_KEEPALIVE); |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
if (level == LINUX_IPPROTO_TCP && option == LINUX_TCP_NODELAY) { |
||||||
|
if (enabled) { |
||||||
|
tcp_nagle_disable(socket->pcb); |
||||||
|
} else { |
||||||
|
tcp_nagle_enable(socket->pcb); |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
return -ENOPROTOOPT; |
||||||
|
} |
||||||
|
|
||||||
|
long typephp_socket_getsockopt(int fd, int level, int option, |
||||||
|
void *value, typephp_socklen_t *length) |
||||||
|
{ |
||||||
|
kernel_socket *socket = socket_for_fd(fd); |
||||||
|
int result; |
||||||
|
if (socket == 0) { |
||||||
|
return -EBADF; |
||||||
|
} |
||||||
|
if (value == 0 || length == 0 || *length < sizeof(int)) { |
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
if (level == LINUX_SOL_SOCKET && option == LINUX_SO_ERROR) { |
||||||
|
result = socket->pending_error; |
||||||
|
socket->pending_error = 0; |
||||||
|
} else if (level == LINUX_SOL_SOCKET && option == LINUX_SO_KEEPALIVE) { |
||||||
|
result = socket->pcb != 0 && ip_get_option(socket->pcb, SOF_KEEPALIVE); |
||||||
|
} else if (level == LINUX_SOL_SOCKET && option == LINUX_SO_RCVBUF) { |
||||||
|
result = TCP_WND; |
||||||
|
} else if (level == LINUX_SOL_SOCKET && option == LINUX_SO_SNDBUF) { |
||||||
|
result = TCP_SND_BUF; |
||||||
|
} else if (level == LINUX_IPPROTO_TCP && option == LINUX_TCP_NODELAY) { |
||||||
|
result = socket->pcb != 0 && tcp_nagle_disabled(socket->pcb); |
||||||
|
} else { |
||||||
|
return -ENOPROTOOPT; |
||||||
|
} |
||||||
|
*(int *) value = result; |
||||||
|
*length = sizeof(int); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
long typephp_socket_fcntl(int fd, int command, long argument) |
||||||
|
{ |
||||||
|
kernel_socket *socket = socket_for_fd(fd); |
||||||
|
if (socket == 0) { |
||||||
|
return -EBADF; |
||||||
|
} |
||||||
|
switch (command) { |
||||||
|
case LINUX_F_GETFD: |
||||||
|
return socket->descriptor_flags; |
||||||
|
case LINUX_F_SETFD: |
||||||
|
if ((argument & ~LINUX_FD_CLOEXEC) != 0) { |
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
socket->descriptor_flags = (int) argument; |
||||||
|
return 0; |
||||||
|
case LINUX_F_GETFL: |
||||||
|
return socket->status_flags; |
||||||
|
case LINUX_F_SETFL: |
||||||
|
if ((argument & ~LINUX_O_NONBLOCK) != 0) { |
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
socket->status_flags = (socket->status_flags & ~LINUX_O_NONBLOCK) |
||||||
|
| (int) argument; |
||||||
|
return 0; |
||||||
|
default: |
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static long poll_once(typephp_pollfd *fds, size_t count) |
||||||
|
{ |
||||||
|
long ready = 0; |
||||||
|
for (size_t index = 0; index < count; ++index) { |
||||||
|
kernel_socket *socket = socket_for_fd(fds[index].fd); |
||||||
|
fds[index].revents = 0; |
||||||
|
if (socket == 0) { |
||||||
|
fds[index].revents = LINUX_POLLNVAL; |
||||||
|
} else { |
||||||
|
if (socket->pending_error != 0 || socket->state == SOCKET_FAILED) { |
||||||
|
fds[index].revents |= LINUX_POLLERR; |
||||||
|
} |
||||||
|
if ((fds[index].events & LINUX_POLLIN) != 0 |
||||||
|
&& (socket->received != 0 |
||||||
|
|| socket->state == SOCKET_PEER_CLOSED)) { |
||||||
|
fds[index].revents |= LINUX_POLLIN; |
||||||
|
} |
||||||
|
if ((fds[index].events & LINUX_POLLOUT) != 0 |
||||||
|
&& socket->state == SOCKET_CONNECTED && socket->pcb != 0 |
||||||
|
&& tcp_sndbuf(socket->pcb) != 0) { |
||||||
|
fds[index].revents |= LINUX_POLLOUT; |
||||||
|
} |
||||||
|
if (socket->state == SOCKET_PEER_CLOSED) { |
||||||
|
fds[index].revents |= LINUX_POLLHUP; |
||||||
|
} |
||||||
|
} |
||||||
|
if (fds[index].revents != 0) { |
||||||
|
++ready; |
||||||
|
} |
||||||
|
} |
||||||
|
return ready; |
||||||
|
} |
||||||
|
|
||||||
|
long typephp_socket_poll(typephp_pollfd *fds, size_t count, int timeout) |
||||||
|
{ |
||||||
|
const uint64_t start = typephp_network_milliseconds(); |
||||||
|
for (;;) { |
||||||
|
typephp_network_poll(); |
||||||
|
long ready = poll_once(fds, count); |
||||||
|
if (ready != 0 || timeout == 0) { |
||||||
|
return ready; |
||||||
|
} |
||||||
|
if (timeout > 0 |
||||||
|
&& typephp_network_milliseconds() - start >= (uint64_t) timeout) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
wait_for_network_event(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void typephp_socket_close_owner(unsigned long owner) |
||||||
|
{ |
||||||
|
for (int index = 0; index < SOCKET_COUNT; ++index) { |
||||||
|
if (sockets[index].allocated && sockets[index].owner == owner) { |
||||||
|
release_socket(&sockets[index]); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,37 @@ |
|||||||
|
#ifndef TYPEPHP_OS_KERNEL_SOCKET_H |
||||||
|
#define TYPEPHP_OS_KERNEL_SOCKET_H |
||||||
|
|
||||||
|
#include <stddef.h> |
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
typedef uint32_t typephp_socklen_t; |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
int fd; |
||||||
|
int16_t events; |
||||||
|
int16_t revents; |
||||||
|
} typephp_pollfd; |
||||||
|
|
||||||
|
int typephp_socket_is_fd(int fd); |
||||||
|
long typephp_socket_open(unsigned long owner, int domain, int type, int protocol); |
||||||
|
long typephp_socket_close(int fd); |
||||||
|
long typephp_socket_read(int fd, void *buffer, size_t length); |
||||||
|
long typephp_socket_write(int fd, const void *buffer, size_t length); |
||||||
|
long typephp_socket_connect(int fd, const void *address, typephp_socklen_t length); |
||||||
|
long typephp_socket_bind(int fd, const void *address, typephp_socklen_t length); |
||||||
|
long typephp_socket_sendto(int fd, const void *buffer, size_t length, int flags, |
||||||
|
const void *address, typephp_socklen_t address_length); |
||||||
|
long typephp_socket_recvfrom(int fd, void *buffer, size_t length, int flags, |
||||||
|
void *address, typephp_socklen_t *address_length); |
||||||
|
long typephp_socket_shutdown(int fd, int how); |
||||||
|
long typephp_socket_getname(int fd, void *address, typephp_socklen_t *length, |
||||||
|
int peer); |
||||||
|
long typephp_socket_setsockopt(int fd, int level, int option, |
||||||
|
const void *value, typephp_socklen_t length); |
||||||
|
long typephp_socket_getsockopt(int fd, int level, int option, |
||||||
|
void *value, typephp_socklen_t *length); |
||||||
|
long typephp_socket_fcntl(int fd, int command, long argument); |
||||||
|
long typephp_socket_poll(typephp_pollfd *fds, size_t count, int timeout); |
||||||
|
void typephp_socket_close_owner(unsigned long owner); |
||||||
|
|
||||||
|
#endif |
||||||
@ -0,0 +1,42 @@ |
|||||||
|
#!/usr/bin/env bash |
||||||
|
|
||||||
|
set -euo pipefail |
||||||
|
|
||||||
|
project_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) |
||||||
|
kernel="${project_dir}/build/typephp-os.elf" |
||||||
|
disk="${project_dir}/build/typephp-os.img" |
||||||
|
|
||||||
|
if ! command -v qemu-system-x86_64 >/dev/null 2>&1; then |
||||||
|
echo "run.sh: qemu-system-x86_64 is not installed" >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
if [[ ! -r /dev/kvm || ! -w /dev/kvm ]]; then |
||||||
|
echo "run.sh: /dev/kvm is unavailable or is not accessible by the current user" >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
if [[ ! -f "${kernel}" ]]; then |
||||||
|
echo "run.sh: kernel image not found: ${kernel}" >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
if [[ ! -f "${disk}" ]]; then |
||||||
|
echo "run.sh: disk image not found: ${disk}" >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
qemu-system-x86_64 \ |
||||||
|
-enable-kvm \ |
||||||
|
-cpu host \ |
||||||
|
-m 512M \ |
||||||
|
-kernel "${kernel}" \ |
||||||
|
-drive "file=${disk},format=raw,if=ide,index=0" \ |
||||||
|
-netdev user,id=net0 \ |
||||||
|
-device e1000,netdev=net0 \ |
||||||
|
-display none \ |
||||||
|
-serial stdio \ |
||||||
|
-monitor none \ |
||||||
|
-no-reboot \ |
||||||
|
-no-shutdown \ |
||||||
|
"$@" |
||||||
@ -0,0 +1,135 @@ |
|||||||
|
#!/usr/bin/env bash |
||||||
|
|
||||||
|
set -euo pipefail |
||||||
|
|
||||||
|
project_dir=$(cd -- "$(dirname -- "$0")/.." && pwd) |
||||||
|
build_root="${project_dir}/build/network-thirdparty" |
||||||
|
install_root="${build_root}/install" |
||||||
|
jobs=${JOBS:-4} |
||||||
|
build_revision=5 |
||||||
|
build_stamp="${build_root}/.typephp-os-network-v${build_revision}" |
||||||
|
common_cflags="-O2 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 -ffreestanding -fno-builtin -fno-stack-protector -fno-pic -fno-pie -ffunction-sections -fdata-sections" |
||||||
|
|
||||||
|
"${project_dir}/tools/fetch-openssl.sh" |
||||||
|
"${project_dir}/tools/fetch-nghttp2.sh" |
||||||
|
"${project_dir}/tools/fetch-curl.sh" |
||||||
|
|
||||||
|
mkdir -p "${build_root}/openssl" "${build_root}/nghttp2" \ |
||||||
|
"${build_root}/curl" "${install_root}" |
||||||
|
|
||||||
|
# Configure probes describe the build host, not TypePHP-OS. Reconfigure all |
||||||
|
# three libraries together whenever the freestanding profile changes so stale |
||||||
|
# hosted objects can never leak glibc fortify, pthread, or signal dependencies |
||||||
|
# into a user-mode ELF. |
||||||
|
if [[ ! -f "${build_stamp}" ]]; then |
||||||
|
if [[ -f "${build_root}/openssl/Makefile" ]]; then |
||||||
|
make -C "${build_root}/openssl" clean |
||||||
|
fi |
||||||
|
if [[ -f "${build_root}/nghttp2/Makefile" ]]; then |
||||||
|
make -C "${build_root}/nghttp2" clean |
||||||
|
fi |
||||||
|
if [[ -f "${build_root}/curl/Makefile" ]]; then |
||||||
|
make -C "${build_root}/curl" clean |
||||||
|
fi |
||||||
|
find "${install_root}" -type f -delete |
||||||
|
fi |
||||||
|
|
||||||
|
# TLS 1.2/1.3 and X.509 remain complete. Command-line tools, tests, dynamic |
||||||
|
# loading, historical protocol versions, datagram TLS, and unrelated protocol |
||||||
|
# facilities are excluded from the static TypePHP-OS network SDK. |
||||||
|
if [[ ! -f "${install_root}/lib64/libssl.a" \ |
||||||
|
&& ! -f "${install_root}/lib/libssl.a" ]]; then |
||||||
|
cd "${build_root}/openssl" |
||||||
|
"${project_dir}/thirdparty/openssl/Configure" linux-x86_64 \ |
||||||
|
--prefix="${install_root}" --libdir=lib \ |
||||||
|
no-shared no-apps no-tests no-docs no-dso no-module no-engine \ |
||||||
|
no-legacy no-quic no-dtls no-dtls1 no-dtls1_2 \ |
||||||
|
no-ssl3 no-tls1 no-tls1_1 no-cmp no-ocsp no-srp no-ts \ |
||||||
|
no-threads no-ui-console \ |
||||||
|
-DOPENSSL_RAND_SEED_DEVRANDOM_SHM_ID=-1 \ |
||||||
|
${common_cflags} |
||||||
|
make -j"${jobs}" build_sw |
||||||
|
make install_sw |
||||||
|
fi |
||||||
|
|
||||||
|
if [[ ! -f "${install_root}/lib/libnghttp2.a" ]]; then |
||||||
|
cd "${build_root}/nghttp2" |
||||||
|
"${project_dir}/thirdparty/nghttp2/configure" \ |
||||||
|
--prefix="${install_root}" --libdir="${install_root}/lib" \ |
||||||
|
--disable-shared --enable-static --enable-lib-only \ |
||||||
|
--disable-threads --disable-failmalloc \ |
||||||
|
CFLAGS="${common_cflags}" LDFLAGS="-no-pie" |
||||||
|
make -j"${jobs}" |
||||||
|
make install |
||||||
|
fi |
||||||
|
|
||||||
|
if [[ ! -f "${install_root}/lib/libcurl.a" ]]; then |
||||||
|
cd "${build_root}/curl" |
||||||
|
PKG_CONFIG_PATH="${install_root}/lib/pkgconfig" \ |
||||||
|
"${project_dir}/thirdparty/curl/configure" \ |
||||||
|
--prefix="${install_root}" --libdir="${install_root}/lib" \ |
||||||
|
--disable-shared --enable-static --enable-symbol-hiding \ |
||||||
|
--enable-http \ |
||||||
|
--disable-ftp --disable-file --disable-ipfs \ |
||||||
|
--disable-ldap --disable-ldaps --disable-rtsp \ |
||||||
|
--disable-dict --disable-telnet --disable-tftp \ |
||||||
|
--disable-pop3 --disable-imap --disable-smb --disable-smtp \ |
||||||
|
--disable-gopher --disable-mqtt \ |
||||||
|
--disable-proxy --disable-doh --disable-websockets \ |
||||||
|
--disable-unix-sockets --disable-socketpair \ |
||||||
|
--disable-httpsrr --disable-ech --disable-proxy-http3 \ |
||||||
|
--disable-alt-svc --disable-hsts --disable-netrc \ |
||||||
|
--disable-manual --disable-docs --disable-libcurl-option \ |
||||||
|
--disable-cookies --disable-dateparse --disable-progress-meter \ |
||||||
|
--disable-get-easy-options --disable-headers-api --disable-verbose \ |
||||||
|
--disable-threaded-resolver --disable-ipv6 \ |
||||||
|
--with-openssl="${install_root}" \ |
||||||
|
--with-nghttp2="${install_root}" \ |
||||||
|
--without-zlib --without-brotli --without-zstd \ |
||||||
|
--without-libpsl --without-libidn2 --without-libssh2 \ |
||||||
|
--without-librtmp --without-gssapi \ |
||||||
|
ac_cv_header_pthread_h=no ac_cv_func_pthread_create=no \ |
||||||
|
ac_cv_func_alarm=no ac_cv_func_sigaction=no \ |
||||||
|
curl_cv_func_alarm=no curl_cv_func_sigaction=no \ |
||||||
|
curl_cv_func_signal=no \ |
||||||
|
CFLAGS="${common_cflags}" \ |
||||||
|
LDFLAGS="-no-pie -Wl,--gc-sections" |
||||||
|
# Autoconf executed on Linux reports signal APIs that TypePHP-OS |
||||||
|
# deliberately does not expose. CURLOPT_NOSIGNAL is always used by the |
||||||
|
# user runtime, and removing these generated feature defines also removes |
||||||
|
# unreachable alarm/sigsetjmp ABI dependencies from libcurl. |
||||||
|
sed -i \ |
||||||
|
-e 's/^#define HAVE_ALARM 1$/\/\* #undef HAVE_ALARM \*\//' \ |
||||||
|
-e 's/^#define HAVE_SIGACTION 1$/\/\* #undef HAVE_SIGACTION \*\//' \ |
||||||
|
-e 's/^#define HAVE_SIGNAL 1$/\/\* #undef HAVE_SIGNAL \*\//' \ |
||||||
|
"${build_root}/curl/lib/curl_config.h" |
||||||
|
# Build libcurl only. The curl command-line executable is intentionally |
||||||
|
# outside the TypePHP-OS SDK. |
||||||
|
make -C lib -j"${jobs}" |
||||||
|
make -C lib install |
||||||
|
make -C include install |
||||||
|
mkdir -p "${install_root}/bin" "${install_root}/lib/pkgconfig" |
||||||
|
install -m 755 curl-config "${install_root}/bin/curl-config" |
||||||
|
install -m 644 libcurl.pc "${install_root}/lib/pkgconfig/libcurl.pc" |
||||||
|
fi |
||||||
|
|
||||||
|
touch "${build_stamp}" |
||||||
|
|
||||||
|
protocols=$("${build_root}/curl/curl-config" --protocols \ |
||||||
|
| tr '[:upper:]' '[:lower:]' | sort -u | tr '\n' ' ') |
||||||
|
if [[ "${protocols}" != "http https " ]]; then |
||||||
|
echo "unexpected libcurl protocol set: ${protocols}" >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
features=$("${build_root}/curl/curl-config" --features | tr '\n' ' ') |
||||||
|
if [[ " ${features} " != *" HTTP2 "* ]]; then |
||||||
|
echo "minimal libcurl was built without HTTP/2: ${features}" >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
echo "OpenSSL static TLS library: OK" |
||||||
|
echo "nghttp2 static library: OK" |
||||||
|
echo "libcurl protocols: HTTP HTTPS" |
||||||
|
echo "libcurl HTTP/2: OK" |
||||||
|
echo "Network SDK prefix: ${install_root}" |
||||||
@ -0,0 +1,30 @@ |
|||||||
|
#!/usr/bin/env sh |
||||||
|
|
||||||
|
set -eu |
||||||
|
|
||||||
|
project_dir=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) |
||||||
|
version=8.22.0 |
||||||
|
archive_sha256=f7ef3ae8a22e521f289803fe93543eb64c329b58aa73a9e224dfd915a2a5f4f7 |
||||||
|
target="${project_dir}/thirdparty/curl" |
||||||
|
marker="${target}/.typephp-source-${version}" |
||||||
|
|
||||||
|
if [ -f "${marker}" ] && [ -f "${target}/lib/http.c" ]; then |
||||||
|
exit 0 |
||||||
|
fi |
||||||
|
|
||||||
|
temporary=$(mktemp -d "${TMPDIR:-/tmp}/typephp-os-curl.XXXXXX") |
||||||
|
trap 'rm -rf "${temporary}"' EXIT HUP INT TERM |
||||||
|
archive="${temporary}/curl.tar.xz" |
||||||
|
source_dir="${temporary}/curl-${version}" |
||||||
|
|
||||||
|
echo "Downloading curl ${version}" |
||||||
|
curl -L --fail --retry 3 --silent --show-error \ |
||||||
|
-o "${archive}" "https://curl.se/download/curl-${version}.tar.xz" |
||||||
|
echo "${archive_sha256} ${archive}" | sha256sum -c - |
||||||
|
tar -xJf "${archive}" -C "${temporary}" |
||||||
|
|
||||||
|
mkdir -p "${project_dir}/thirdparty" |
||||||
|
rm -rf "${target}" |
||||||
|
mv "${source_dir}" "${target}" |
||||||
|
printf '%s\n' "${archive_sha256}" > "${marker}" |
||||||
|
echo "Installed curl ${version} in ${target}" |
||||||
@ -0,0 +1,32 @@ |
|||||||
|
#!/usr/bin/env sh |
||||||
|
|
||||||
|
set -eu |
||||||
|
|
||||||
|
project_dir=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) |
||||||
|
version=2.2.1 |
||||||
|
tag=STABLE-2_2_1_RELEASE |
||||||
|
archive_sha256=ce0b7461c0ad9602c376f0bf07c5eb7253b48c7bf66f011c6bf3e2a96731c539 |
||||||
|
target="${project_dir}/thirdparty/lwip" |
||||||
|
marker="${target}/.typephp-source-${version}" |
||||||
|
|
||||||
|
if [ -f "${marker}" ] && [ -f "${target}/src/core/tcp.c" ]; then |
||||||
|
exit 0 |
||||||
|
fi |
||||||
|
|
||||||
|
temporary=$(mktemp -d "${TMPDIR:-/tmp}/typephp-os-lwip.XXXXXX") |
||||||
|
trap 'rm -rf "${temporary}"' EXIT HUP INT TERM |
||||||
|
archive="${temporary}/lwip.tar.gz" |
||||||
|
source_dir="${temporary}/lwip-${tag}" |
||||||
|
|
||||||
|
echo "Downloading lwIP ${version}" |
||||||
|
curl -L --fail --retry 3 --silent --show-error \ |
||||||
|
-o "${archive}" \ |
||||||
|
"https://github.com/lwip-tcpip/lwip/archive/refs/tags/${tag}.tar.gz" |
||||||
|
echo "${archive_sha256} ${archive}" | sha256sum -c - |
||||||
|
tar -xzf "${archive}" -C "${temporary}" |
||||||
|
|
||||||
|
mkdir -p "${project_dir}/thirdparty" |
||||||
|
rm -rf "${target}" |
||||||
|
mv "${source_dir}" "${target}" |
||||||
|
printf '%s\n' "${archive_sha256}" > "${marker}" |
||||||
|
echo "Installed lwIP ${version} in ${target}" |
||||||
@ -0,0 +1,31 @@ |
|||||||
|
#!/usr/bin/env sh |
||||||
|
|
||||||
|
set -eu |
||||||
|
|
||||||
|
project_dir=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) |
||||||
|
version=1.70.0 |
||||||
|
archive_sha256=e05cb1388eaca3830aded4ccf20044b6e1ac1a61411dcca11b0437c4285c8bc2 |
||||||
|
target="${project_dir}/thirdparty/nghttp2" |
||||||
|
marker="${target}/.typephp-source-${version}" |
||||||
|
|
||||||
|
if [ -f "${marker}" ] && [ -f "${target}/lib/nghttp2_session.c" ]; then |
||||||
|
exit 0 |
||||||
|
fi |
||||||
|
|
||||||
|
temporary=$(mktemp -d "${TMPDIR:-/tmp}/typephp-os-nghttp2.XXXXXX") |
||||||
|
trap 'rm -rf "${temporary}"' EXIT HUP INT TERM |
||||||
|
archive="${temporary}/nghttp2.tar.xz" |
||||||
|
source_dir="${temporary}/nghttp2-${version}" |
||||||
|
|
||||||
|
echo "Downloading nghttp2 ${version}" |
||||||
|
curl -L --fail --retry 3 --silent --show-error \ |
||||||
|
-o "${archive}" \ |
||||||
|
"https://github.com/nghttp2/nghttp2/releases/download/v${version}/nghttp2-${version}.tar.xz" |
||||||
|
echo "${archive_sha256} ${archive}" | sha256sum -c - |
||||||
|
tar -xJf "${archive}" -C "${temporary}" |
||||||
|
|
||||||
|
mkdir -p "${project_dir}/thirdparty" |
||||||
|
rm -rf "${target}" |
||||||
|
mv "${source_dir}" "${target}" |
||||||
|
printf '%s\n' "${archive_sha256}" > "${marker}" |
||||||
|
echo "Installed nghttp2 ${version} in ${target}" |
||||||
@ -0,0 +1,31 @@ |
|||||||
|
#!/usr/bin/env sh |
||||||
|
|
||||||
|
set -eu |
||||||
|
|
||||||
|
project_dir=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) |
||||||
|
version=3.5.8 |
||||||
|
archive_sha256=a8f84a39918ec6415ce765d9b429d313ba97b8143169c172e734b9514464f5b2 |
||||||
|
target="${project_dir}/thirdparty/openssl" |
||||||
|
marker="${target}/.typephp-source-${version}" |
||||||
|
|
||||||
|
if [ -f "${marker}" ] && [ -f "${target}/Configure" ]; then |
||||||
|
exit 0 |
||||||
|
fi |
||||||
|
|
||||||
|
temporary=$(mktemp -d "${TMPDIR:-/tmp}/typephp-os-openssl.XXXXXX") |
||||||
|
trap 'rm -rf "${temporary}"' EXIT HUP INT TERM |
||||||
|
archive="${temporary}/openssl.tar.gz" |
||||||
|
source_dir="${temporary}/openssl-${version}" |
||||||
|
|
||||||
|
echo "Downloading OpenSSL ${version} LTS" |
||||||
|
curl -L --fail --retry 3 --silent --show-error \ |
||||||
|
-o "${archive}" \ |
||||||
|
"https://github.com/openssl/openssl/releases/download/openssl-${version}/openssl-${version}.tar.gz" |
||||||
|
echo "${archive_sha256} ${archive}" | sha256sum -c - |
||||||
|
tar -xzf "${archive}" -C "${temporary}" |
||||||
|
|
||||||
|
mkdir -p "${project_dir}/thirdparty" |
||||||
|
rm -rf "${target}" |
||||||
|
mv "${source_dir}" "${target}" |
||||||
|
printf '%s\n' "${archive_sha256}" > "${marker}" |
||||||
|
echo "Installed OpenSSL ${version} in ${target}" |
||||||
@ -0,0 +1,93 @@ |
|||||||
|
#include <errno.h> |
||||||
|
#include <netdb.h> |
||||||
|
#include <netinet/in.h> |
||||||
|
#include <string.h> |
||||||
|
#include <sys/random.h> |
||||||
|
#include <sys/socket.h> |
||||||
|
#include <unistd.h> |
||||||
|
|
||||||
|
static int fail(const char *operation) |
||||||
|
{ |
||||||
|
(void) write(STDERR_FILENO, "nettest: ", sizeof("nettest: ") - 1); |
||||||
|
(void) write(STDERR_FILENO, operation, strlen(operation)); |
||||||
|
(void) write(STDERR_FILENO, ": ", 2); |
||||||
|
(void) write(STDERR_FILENO, strerror(errno), strlen(strerror(errno))); |
||||||
|
(void) write(STDERR_FILENO, "\n", 1); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
int main(void) |
||||||
|
{ |
||||||
|
static const char request[] = |
||||||
|
"GET /invalid-elf.txt HTTP/1.0\r\n" |
||||||
|
"Host: 10.0.2.2\r\n" |
||||||
|
"Connection: close\r\n\r\n"; |
||||||
|
struct sockaddr_in server = { |
||||||
|
AF_INET, |
||||||
|
htons(18080), |
||||||
|
{ htonl(0x0a000202u) }, |
||||||
|
{ 0, 0, 0, 0, 0, 0, 0, 0 }, |
||||||
|
}; |
||||||
|
char response[64]; |
||||||
|
unsigned char random[32]; |
||||||
|
struct addrinfo hints = {0}; |
||||||
|
struct addrinfo *resolved = 0; |
||||||
|
unsigned char random_bits = 0; |
||||||
|
int index; |
||||||
|
|
||||||
|
hints.ai_family = AF_INET; |
||||||
|
hints.ai_socktype = SOCK_STREAM; |
||||||
|
if (getaddrinfo("httpcan.org", "443", &hints, &resolved) != 0 |
||||||
|
|| resolved == 0 || resolved->ai_addr == 0) { |
||||||
|
(void) write(STDERR_FILENO, "nettest: DNS resolution failed\n", |
||||||
|
sizeof("nettest: DNS resolution failed\n") - 1); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
freeaddrinfo(resolved); |
||||||
|
(void) write(STDOUT_FILENO, "lwIP DNS: OK\n", |
||||||
|
sizeof("lwIP DNS: OK\n") - 1); |
||||||
|
|
||||||
|
if (getrandom(random, sizeof(random), 0) != sizeof(random)) { |
||||||
|
return fail("getrandom"); |
||||||
|
} |
||||||
|
for (index = 0; index < (int) sizeof(random); ++index) { |
||||||
|
random_bits |= random[index]; |
||||||
|
} |
||||||
|
if (random_bits == 0) { |
||||||
|
(void) write(STDERR_FILENO, "nettest: empty random output\n", |
||||||
|
sizeof("nettest: empty random output\n") - 1); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
(void) write(STDOUT_FILENO, "CSPRNG: OK\n", |
||||||
|
sizeof("CSPRNG: OK\n") - 1); |
||||||
|
|
||||||
|
int fd = socket(AF_INET, SOCK_STREAM, 0); |
||||||
|
if (fd < 0) { |
||||||
|
return fail("socket"); |
||||||
|
} |
||||||
|
if (connect(fd, (const struct sockaddr *) &server, sizeof(server)) < 0) { |
||||||
|
(void) close(fd); |
||||||
|
return fail("connect"); |
||||||
|
} |
||||||
|
if (send(fd, request, sizeof(request) - 1, MSG_NOSIGNAL) |
||||||
|
!= (ssize_t) (sizeof(request) - 1)) { |
||||||
|
(void) close(fd); |
||||||
|
return fail("send"); |
||||||
|
} |
||||||
|
ssize_t received = recv(fd, response, sizeof(response), 0); |
||||||
|
if (received < 12) { |
||||||
|
(void) close(fd); |
||||||
|
return fail("recv"); |
||||||
|
} |
||||||
|
(void) close(fd); |
||||||
|
if (response[0] != 'H' || response[1] != 'T' || response[2] != 'T' |
||||||
|
|| response[3] != 'P' || response[4] != '/' |
||||||
|
|| response[5] != '1' || response[6] != '.') { |
||||||
|
(void) write(STDERR_FILENO, "nettest: invalid HTTP response\n", |
||||||
|
sizeof("nettest: invalid HTTP response\n") - 1); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
(void) write(STDOUT_FILENO, "TCP HTTP socket: OK\n", |
||||||
|
sizeof("TCP HTTP socket: OK\n") - 1); |
||||||
|
return 0; |
||||||
|
} |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
#ifndef TYPEPHP_OS_USER_ARPA_INET_H |
||||||
|
#define TYPEPHP_OS_USER_ARPA_INET_H |
||||||
|
|
||||||
|
#include <netinet/in.h> |
||||||
|
#include <sys/socket.h> |
||||||
|
|
||||||
|
#define INET_ADDRSTRLEN 16 |
||||||
|
#define INET6_ADDRSTRLEN 46 |
||||||
|
|
||||||
|
int inet_aton(const char *text, struct in_addr *address); |
||||||
|
in_addr_t inet_addr(const char *text); |
||||||
|
int inet_pton(int family, const char *text, void *address); |
||||||
|
const char *inet_ntop(int family, const void *address, char *text, |
||||||
|
socklen_t length); |
||||||
|
|
||||||
|
#endif |
||||||
@ -0,0 +1,40 @@ |
|||||||
|
#ifndef TYPEPHP_OS_USER_NETDB_H |
||||||
|
#define TYPEPHP_OS_USER_NETDB_H |
||||||
|
|
||||||
|
#include <sys/socket.h> |
||||||
|
|
||||||
|
struct addrinfo { |
||||||
|
int ai_flags; |
||||||
|
int ai_family; |
||||||
|
int ai_socktype; |
||||||
|
int ai_protocol; |
||||||
|
socklen_t ai_addrlen; |
||||||
|
struct sockaddr *ai_addr; |
||||||
|
char *ai_canonname; |
||||||
|
struct addrinfo *ai_next; |
||||||
|
}; |
||||||
|
|
||||||
|
#define AI_PASSIVE 0x0001 |
||||||
|
#define AI_CANONNAME 0x0002 |
||||||
|
#define AI_NUMERICHOST 0x0004 |
||||||
|
#define AI_V4MAPPED 0x0008 |
||||||
|
#define AI_ALL 0x0010 |
||||||
|
#define AI_ADDRCONFIG 0x0020 |
||||||
|
#define AI_NUMERICSERV 0x0400 |
||||||
|
|
||||||
|
#define EAI_BADFLAGS -1 |
||||||
|
#define EAI_NONAME -2 |
||||||
|
#define EAI_AGAIN -3 |
||||||
|
#define EAI_FAIL -4 |
||||||
|
#define EAI_FAMILY -6 |
||||||
|
#define EAI_SOCKTYPE -7 |
||||||
|
#define EAI_SERVICE -8 |
||||||
|
#define EAI_MEMORY -10 |
||||||
|
#define EAI_SYSTEM -11 |
||||||
|
|
||||||
|
int getaddrinfo(const char *node, const char *service, |
||||||
|
const struct addrinfo *hints, struct addrinfo **result); |
||||||
|
void freeaddrinfo(struct addrinfo *result); |
||||||
|
const char *gai_strerror(int error); |
||||||
|
|
||||||
|
#endif |
||||||
@ -0,0 +1,50 @@ |
|||||||
|
#ifndef TYPEPHP_OS_USER_NETINET_IN_H |
||||||
|
#define TYPEPHP_OS_USER_NETINET_IN_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#include <sys/socket.h> |
||||||
|
|
||||||
|
typedef uint16_t in_port_t; |
||||||
|
typedef uint32_t in_addr_t; |
||||||
|
|
||||||
|
struct in_addr { |
||||||
|
in_addr_t s_addr; |
||||||
|
}; |
||||||
|
|
||||||
|
struct sockaddr_in { |
||||||
|
sa_family_t sin_family; |
||||||
|
in_port_t sin_port; |
||||||
|
struct in_addr sin_addr; |
||||||
|
unsigned char sin_zero[8]; |
||||||
|
}; |
||||||
|
|
||||||
|
#define IPPROTO_IP 0 |
||||||
|
#define IPPROTO_TCP 6 |
||||||
|
|
||||||
|
#define INADDR_ANY ((in_addr_t) 0x00000000) |
||||||
|
#define INADDR_LOOPBACK ((in_addr_t) 0x0100007f) |
||||||
|
#define INADDR_NONE ((in_addr_t) 0xffffffff) |
||||||
|
|
||||||
|
#define TCP_NODELAY 1 |
||||||
|
|
||||||
|
static inline uint16_t htons(uint16_t value) |
||||||
|
{ |
||||||
|
return __builtin_bswap16(value); |
||||||
|
} |
||||||
|
|
||||||
|
static inline uint16_t ntohs(uint16_t value) |
||||||
|
{ |
||||||
|
return __builtin_bswap16(value); |
||||||
|
} |
||||||
|
|
||||||
|
static inline uint32_t htonl(uint32_t value) |
||||||
|
{ |
||||||
|
return __builtin_bswap32(value); |
||||||
|
} |
||||||
|
|
||||||
|
static inline uint32_t ntohl(uint32_t value) |
||||||
|
{ |
||||||
|
return __builtin_bswap32(value); |
||||||
|
} |
||||||
|
|
||||||
|
#endif |
||||||
@ -0,0 +1,23 @@ |
|||||||
|
#ifndef TYPEPHP_OS_USER_POLL_H |
||||||
|
#define TYPEPHP_OS_USER_POLL_H |
||||||
|
|
||||||
|
#include <sys/types.h> |
||||||
|
|
||||||
|
typedef unsigned long nfds_t; |
||||||
|
|
||||||
|
struct pollfd { |
||||||
|
int fd; |
||||||
|
short events; |
||||||
|
short revents; |
||||||
|
}; |
||||||
|
|
||||||
|
#define POLLIN 0x001 |
||||||
|
#define POLLPRI 0x002 |
||||||
|
#define POLLOUT 0x004 |
||||||
|
#define POLLERR 0x008 |
||||||
|
#define POLLHUP 0x010 |
||||||
|
#define POLLNVAL 0x020 |
||||||
|
|
||||||
|
int poll(struct pollfd *fds, nfds_t count, int timeout); |
||||||
|
|
||||||
|
#endif |
||||||
@ -0,0 +1,14 @@ |
|||||||
|
#ifndef TYPEPHP_OS_USER_SYS_RANDOM_H |
||||||
|
#define TYPEPHP_OS_USER_SYS_RANDOM_H |
||||||
|
|
||||||
|
#include <stddef.h> |
||||||
|
#include <sys/types.h> |
||||||
|
|
||||||
|
#define GRND_NONBLOCK 0x0001 |
||||||
|
#define GRND_RANDOM 0x0002 |
||||||
|
#define GRND_INSECURE 0x0004 |
||||||
|
|
||||||
|
ssize_t getrandom(void *buffer, size_t length, unsigned int flags); |
||||||
|
int getentropy(void *buffer, size_t length); |
||||||
|
|
||||||
|
#endif |
||||||
@ -0,0 +1,66 @@ |
|||||||
|
#ifndef TYPEPHP_OS_USER_SYS_SOCKET_H |
||||||
|
#define TYPEPHP_OS_USER_SYS_SOCKET_H |
||||||
|
|
||||||
|
#include <stddef.h> |
||||||
|
#include <stdint.h> |
||||||
|
#include <sys/types.h> |
||||||
|
|
||||||
|
typedef uint32_t socklen_t; |
||||||
|
typedef uint16_t sa_family_t; |
||||||
|
|
||||||
|
struct sockaddr { |
||||||
|
sa_family_t sa_family; |
||||||
|
char sa_data[14]; |
||||||
|
}; |
||||||
|
|
||||||
|
struct iovec { |
||||||
|
void *iov_base; |
||||||
|
size_t iov_len; |
||||||
|
}; |
||||||
|
|
||||||
|
#define AF_UNSPEC 0 |
||||||
|
#define AF_UNIX 1 |
||||||
|
#define AF_INET 2 |
||||||
|
#define AF_INET6 10 |
||||||
|
|
||||||
|
#define PF_UNSPEC AF_UNSPEC |
||||||
|
#define PF_UNIX AF_UNIX |
||||||
|
#define PF_INET AF_INET |
||||||
|
#define PF_INET6 AF_INET6 |
||||||
|
|
||||||
|
#define SOCK_STREAM 1 |
||||||
|
#define SOCK_DGRAM 2 |
||||||
|
#define SOCK_RAW 3 |
||||||
|
#define SOCK_NONBLOCK 04000 |
||||||
|
#define SOCK_CLOEXEC 02000000 |
||||||
|
|
||||||
|
#define SOL_SOCKET 1 |
||||||
|
#define SO_ERROR 4 |
||||||
|
#define SO_KEEPALIVE 9 |
||||||
|
#define SO_RCVBUF 8 |
||||||
|
#define SO_SNDBUF 7 |
||||||
|
|
||||||
|
#define MSG_PEEK 2 |
||||||
|
#define MSG_DONTWAIT 0x40 |
||||||
|
#define MSG_NOSIGNAL 0x4000 |
||||||
|
|
||||||
|
#define SHUT_RD 0 |
||||||
|
#define SHUT_WR 1 |
||||||
|
#define SHUT_RDWR 2 |
||||||
|
|
||||||
|
int socket(int domain, int type, int protocol); |
||||||
|
int connect(int fd, const struct sockaddr *address, socklen_t length); |
||||||
|
int bind(int fd, const struct sockaddr *address, socklen_t length); |
||||||
|
ssize_t send(int fd, const void *buffer, size_t length, int flags); |
||||||
|
ssize_t recv(int fd, void *buffer, size_t length, int flags); |
||||||
|
ssize_t sendto(int fd, const void *buffer, size_t length, int flags, |
||||||
|
const struct sockaddr *address, socklen_t address_length); |
||||||
|
ssize_t recvfrom(int fd, void *buffer, size_t length, int flags, |
||||||
|
struct sockaddr *address, socklen_t *address_length); |
||||||
|
int shutdown(int fd, int how); |
||||||
|
int getsockname(int fd, struct sockaddr *address, socklen_t *length); |
||||||
|
int getpeername(int fd, struct sockaddr *address, socklen_t *length); |
||||||
|
int setsockopt(int fd, int level, int option, const void *value, socklen_t length); |
||||||
|
int getsockopt(int fd, int level, int option, void *value, socklen_t *length); |
||||||
|
|
||||||
|
#endif |
||||||
@ -0,0 +1,36 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
function main(int $argc, array $argv): void |
||||||
|
{ |
||||||
|
$url = 'https://httpcan.org/get'; |
||||||
|
$handle = curl_init($url); |
||||||
|
if ($handle === false) { |
||||||
|
echo "curl_init failed\n"; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
curl_setopt($handle, CURLOPT_RETURNTRANSFER, true); |
||||||
|
curl_setopt($handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS); |
||||||
|
curl_setopt($handle, CURLOPT_NOSIGNAL, true); |
||||||
|
curl_setopt($handle, CURLOPT_CAINFO, '/CACERT.PEM'); |
||||||
|
curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, 10); |
||||||
|
curl_setopt($handle, CURLOPT_TIMEOUT, 20); |
||||||
|
curl_setopt($handle, CURLOPT_USERAGENT, 'TypePHP-OS/0.1'); |
||||||
|
|
||||||
|
$body = curl_exec($handle); |
||||||
|
if ($body === false) { |
||||||
|
echo 'HTTPS request failed: ', curl_error($handle), "\n"; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
$status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); |
||||||
|
$version = curl_getinfo($handle, CURLINFO_HTTP_VERSION); |
||||||
|
$curlVersion = curl_version(); |
||||||
|
$digest = openssl_digest('TypePHP-OS', 'sha256'); |
||||||
|
echo 'GET ', $url, "\n"; |
||||||
|
echo 'HTTP status: ', (string) $status, "\n"; |
||||||
|
echo 'HTTP version: ', $version === CURL_HTTP_VERSION_2_0 ? '2' : (string) $version, "\n"; |
||||||
|
echo 'TLS backend: ', (string) $curlVersion['ssl_version'], "\n"; |
||||||
|
echo 'OpenSSL SHA-256: ', (string) $digest, "\n"; |
||||||
|
echo $body, "\n"; |
||||||
|
} |
||||||
@ -0,0 +1,92 @@ |
|||||||
|
name: typephp_os_user_https |
||||||
|
mode: bin |
||||||
|
sources: |
||||||
|
- https.php |
||||||
|
build-dir: ../../build/generated-user-https |
||||||
|
output: ../../build/https.elf |
||||||
|
cxx-std: c++17 |
||||||
|
include-paths: |
||||||
|
- ../../kernel/core/abi |
||||||
|
- ../../thirdparty/openlibm/include |
||||||
|
- ../../thirdparty/openlibm/src |
||||||
|
- ../../thirdparty/openlibm/amd64 |
||||||
|
- ../../build/network-thirdparty/install/include |
||||||
|
defines: |
||||||
|
- _DEFAULT_SOURCE=1 |
||||||
|
- HAVE_REALLOCARRAY=1 |
||||||
|
- HAVE_SYS_SOCKET_H=1 |
||||||
|
- HAVE_NETINET_IN_H=1 |
||||||
|
- HAVE_POLL=1 |
||||||
|
- HAVE_POLL_H=1 |
||||||
|
- HAVE_GETADDRINFO=1 |
||||||
|
- HAVE_GAI_STRERROR=1 |
||||||
|
- HAVE_INET_PTON=1 |
||||||
|
- HAVE_SHUTDOWN=1 |
||||||
|
- HAVE_STRUCT_TM_TM_GMTOFF=1 |
||||||
|
- TYPEPHP_NO_MAIN=1 |
||||||
|
- PHPX_NO_EXCEPTION=1 |
||||||
|
- PHPX_NO_RTTI=1 |
||||||
|
- PHP_NANO_NO_LIBC=1 |
||||||
|
- HAVE_SYS_UTSNAME_H=1 |
||||||
|
- HAVE_SYS_TYPES_H=1 |
||||||
|
- HAVE_SYS_TIME_H=1 |
||||||
|
- HAVE_UNISTD_H=1 |
||||||
|
- HAVE_TLS12=1 |
||||||
|
- HAVE_TLS13=1 |
||||||
|
- HAVE_TLS_ALPN=1 |
||||||
|
- HAVE_TLS_SNI=1 |
||||||
|
- CURL_STATICLIB=1 |
||||||
|
- __NO_CTYPE=1 |
||||||
|
- ZEND_MM_ERROR=0 |
||||||
|
- ZEND_MM_CUSTOM=0 |
||||||
|
- ASSEMBLER=1 |
||||||
|
- __BSD_VISIBLE=1 |
||||||
|
cxx-flags: |
||||||
|
- -O2 |
||||||
|
- -U_FORTIFY_SOURCE |
||||||
|
- -D_FORTIFY_SOURCE=0 |
||||||
|
- -ffreestanding |
||||||
|
- -fno-builtin |
||||||
|
- -fno-stack-protector |
||||||
|
- -fno-pic |
||||||
|
- -fno-pie |
||||||
|
- -m64 |
||||||
|
- -mno-red-zone |
||||||
|
- -mcmodel=small |
||||||
|
- -fno-rtti |
||||||
|
- -fno-threadsafe-statics |
||||||
|
c-flags: |
||||||
|
- -O2 |
||||||
|
- -U_FORTIFY_SOURCE |
||||||
|
- -D_FORTIFY_SOURCE=0 |
||||||
|
- -ffreestanding |
||||||
|
- -fno-builtin |
||||||
|
- -fno-stack-protector |
||||||
|
- -fno-pic |
||||||
|
- -fno-pie |
||||||
|
- -m64 |
||||||
|
- -mno-red-zone |
||||||
|
- -mcmodel=small |
||||||
|
asm-flags: |
||||||
|
- -m64 |
||||||
|
- -mno-red-zone |
||||||
|
- -mcmodel=small |
||||||
|
ld-flags: |
||||||
|
- -nostdlib |
||||||
|
- -no-pie |
||||||
|
- -Wl,-m,elf_x86_64 |
||||||
|
- -Wl,--build-id=none |
||||||
|
- -Wl,-T,user/runtime/linker.ld |
||||||
|
- -Wl,--defsym,USER_BASE=0x41000000 |
||||||
|
link-paths: |
||||||
|
- ../../build/network-thirdparty/install/lib |
||||||
|
- ../../build |
||||||
|
link-libs: |
||||||
|
- curl |
||||||
|
- nghttp2 |
||||||
|
- ssl |
||||||
|
- crypto |
||||||
|
- ssl |
||||||
|
- crypto |
||||||
|
- typephp-os |
||||||
|
- compiler-rt-builtins |
||||||
@ -0,0 +1,351 @@ |
|||||||
|
/*
|
||||||
|
+----------------------------------------------------------------------+ |
||||||
|
| TypePHP OS | |
||||||
|
+----------------------------------------------------------------------+ |
||||||
|
| Small glibc ABI surface required by hosted-built static libraries. | |
||||||
|
| SPDX-License-Identifier: BSD-3-Clause | |
||||||
|
+----------------------------------------------------------------------+ |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <errno.h> |
||||||
|
#include <math.h> |
||||||
|
#include <stdarg.h> |
||||||
|
#include <stddef.h> |
||||||
|
#include <stdint.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
#include <time.h> |
||||||
|
#include <unistd.h> |
||||||
|
|
||||||
|
static unsigned short ctype_flags[384]; |
||||||
|
static int ctype_tolower[384]; |
||||||
|
static int ctype_ready; |
||||||
|
|
||||||
|
static void init_ctype_tables(void) |
||||||
|
{ |
||||||
|
int value; |
||||||
|
if (ctype_ready) { |
||||||
|
return; |
||||||
|
} |
||||||
|
for (value = -128; value < 256; ++value) { |
||||||
|
const unsigned char byte = (unsigned char) value; |
||||||
|
unsigned short flags = 0; |
||||||
|
ctype_tolower[value + 128] = value; |
||||||
|
if (byte >= 'A' && byte <= 'Z') { |
||||||
|
flags |= 0x0100 | 0x0400 | 0x0008; |
||||||
|
ctype_tolower[value + 128] = byte + ('a' - 'A'); |
||||||
|
} else if (byte >= 'a' && byte <= 'z') { |
||||||
|
flags |= 0x0200 | 0x0400 | 0x0008; |
||||||
|
} |
||||||
|
if (byte >= '0' && byte <= '9') { |
||||||
|
flags |= 0x0800 | 0x1000 | 0x0008; |
||||||
|
} else if ((byte >= 'a' && byte <= 'f') |
||||||
|
|| (byte >= 'A' && byte <= 'F')) { |
||||||
|
flags |= 0x1000; |
||||||
|
} |
||||||
|
if (byte == ' ' || (byte >= '\t' && byte <= '\r')) { |
||||||
|
flags |= 0x2000; |
||||||
|
} |
||||||
|
if (byte == ' ' || byte == '\t') { |
||||||
|
flags |= 0x0001; |
||||||
|
} |
||||||
|
if (byte < 0x20 || byte == 0x7f) { |
||||||
|
flags |= 0x0002; |
||||||
|
} |
||||||
|
if (byte >= 0x20 && byte < 0x7f) { |
||||||
|
flags |= 0x4000; |
||||||
|
} |
||||||
|
if (byte > 0x20 && byte < 0x7f) { |
||||||
|
flags |= 0x8000; |
||||||
|
} |
||||||
|
if (byte > 0x20 && byte < 0x7f |
||||||
|
&& !(byte >= '0' && byte <= '9') |
||||||
|
&& !(byte >= 'A' && byte <= 'Z') |
||||||
|
&& !(byte >= 'a' && byte <= 'z')) { |
||||||
|
flags |= 0x0004; |
||||||
|
} |
||||||
|
ctype_flags[value + 128] = flags; |
||||||
|
} |
||||||
|
ctype_ready = 1; |
||||||
|
} |
||||||
|
|
||||||
|
const unsigned short **__ctype_b_loc(void) |
||||||
|
{ |
||||||
|
static const unsigned short *table; |
||||||
|
init_ctype_tables(); |
||||||
|
table = ctype_flags + 128; |
||||||
|
return &table; |
||||||
|
} |
||||||
|
|
||||||
|
const int **__ctype_tolower_loc(void) |
||||||
|
{ |
||||||
|
static const int *table; |
||||||
|
init_ctype_tables(); |
||||||
|
table = ctype_tolower + 128; |
||||||
|
return &table; |
||||||
|
} |
||||||
|
|
||||||
|
int __popcountdi2(uint64_t value) |
||||||
|
{ |
||||||
|
int count = 0; |
||||||
|
while (value != 0) { |
||||||
|
value &= value - 1; |
||||||
|
++count; |
||||||
|
} |
||||||
|
return count; |
||||||
|
} |
||||||
|
|
||||||
|
char *secure_getenv(const char *name) |
||||||
|
{ |
||||||
|
return getenv(name); |
||||||
|
} |
||||||
|
|
||||||
|
int atexit(void (*function)(void)) |
||||||
|
{ |
||||||
|
/* A user image is discarded as one unit. OpenSSL cleanup registration is
|
||||||
|
* therefore successful without retaining process-exit callbacks. */ |
||||||
|
(void) function; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
void explicit_bzero(void *memory, size_t size) |
||||||
|
{ |
||||||
|
volatile unsigned char *cursor = (volatile unsigned char *) memory; |
||||||
|
while (size-- != 0) { |
||||||
|
*cursor++ = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void *shmat(int id, const void *address, int flags) |
||||||
|
{ |
||||||
|
(void) id; |
||||||
|
(void) address; |
||||||
|
(void) flags; |
||||||
|
errno = ENOSYS; |
||||||
|
return (void *) -1; |
||||||
|
} |
||||||
|
|
||||||
|
int shmdt(const void *address) |
||||||
|
{ |
||||||
|
(void) address; |
||||||
|
errno = ENOSYS; |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
int shmget(int key, size_t size, int flags) |
||||||
|
{ |
||||||
|
(void) key; |
||||||
|
(void) size; |
||||||
|
(void) flags; |
||||||
|
errno = ENOSYS; |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
static int leap_year(int year) |
||||||
|
{ |
||||||
|
return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; |
||||||
|
} |
||||||
|
|
||||||
|
struct tm *gmtime_r(const time_t *timer, struct tm *result) |
||||||
|
{ |
||||||
|
static const int month_days[] = { |
||||||
|
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, |
||||||
|
}; |
||||||
|
int64_t seconds; |
||||||
|
int64_t days; |
||||||
|
int year = 1970; |
||||||
|
int month = 0; |
||||||
|
int days_in_month; |
||||||
|
if (timer == 0 || result == 0 || *timer < 0) { |
||||||
|
errno = EINVAL; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
seconds = (int64_t) *timer; |
||||||
|
days = seconds / 86400; |
||||||
|
result->tm_sec = (int) (seconds % 60); |
||||||
|
result->tm_min = (int) ((seconds / 60) % 60); |
||||||
|
result->tm_hour = (int) ((seconds / 3600) % 24); |
||||||
|
result->tm_wday = (int) ((days + 4) % 7); |
||||||
|
while (days >= (leap_year(year) ? 366 : 365)) { |
||||||
|
days -= leap_year(year) ? 366 : 365; |
||||||
|
++year; |
||||||
|
} |
||||||
|
result->tm_yday = (int) days; |
||||||
|
while (month < 12) { |
||||||
|
days_in_month = month_days[month] |
||||||
|
+ (month == 1 && leap_year(year) ? 1 : 0); |
||||||
|
if (days < days_in_month) { |
||||||
|
break; |
||||||
|
} |
||||||
|
days -= days_in_month; |
||||||
|
++month; |
||||||
|
} |
||||||
|
result->tm_year = year - 1900; |
||||||
|
result->tm_mon = month; |
||||||
|
result->tm_mday = (int) days + 1; |
||||||
|
result->tm_isdst = 0; |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
struct tm *gmtime(const time_t *timer) |
||||||
|
{ |
||||||
|
static struct tm result; |
||||||
|
return gmtime_r(timer, &result); |
||||||
|
} |
||||||
|
|
||||||
|
/* TypePHP-OS currently uses UTC as its only timezone. This civil-date
|
||||||
|
* conversion follows the proleptic Gregorian calendar and normalizes the |
||||||
|
* caller's struct tm through gmtime_r(). */ |
||||||
|
time_t mktime(struct tm *value) |
||||||
|
{ |
||||||
|
int year; |
||||||
|
unsigned int month; |
||||||
|
int64_t era; |
||||||
|
unsigned int year_of_era; |
||||||
|
unsigned int day_of_year; |
||||||
|
unsigned int day_of_era; |
||||||
|
int64_t days; |
||||||
|
int64_t seconds; |
||||||
|
time_t result; |
||||||
|
if (value == 0 || value->tm_mon < 0 || value->tm_mon > 11 |
||||||
|
|| value->tm_mday < 1 || value->tm_mday > 31 |
||||||
|
|| value->tm_hour < 0 || value->tm_hour > 23 |
||||||
|
|| value->tm_min < 0 || value->tm_min > 59 |
||||||
|
|| value->tm_sec < 0 || value->tm_sec > 60) { |
||||||
|
errno = EINVAL; |
||||||
|
return (time_t) -1; |
||||||
|
} |
||||||
|
year = value->tm_year + 1900; |
||||||
|
month = (unsigned int) value->tm_mon + 1u; |
||||||
|
year -= month <= 2u; |
||||||
|
era = (year >= 0 ? year : year - 399) / 400; |
||||||
|
year_of_era = (unsigned int) (year - era * 400); |
||||||
|
day_of_year = (153u * (month > 2u ? month - 3u : month + 9u) |
||||||
|
+ 2u) / 5u + (unsigned int) value->tm_mday - 1u; |
||||||
|
day_of_era = year_of_era * 365u + year_of_era / 4u |
||||||
|
- year_of_era / 100u + day_of_year; |
||||||
|
days = era * 146097 + (int64_t) day_of_era - 719468; |
||||||
|
seconds = days * 86400 + value->tm_hour * 3600 |
||||||
|
+ value->tm_min * 60 + value->tm_sec; |
||||||
|
result = (time_t) seconds; |
||||||
|
if (seconds >= 0) { |
||||||
|
(void) gmtime_r(&result, value); |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
float floorf(float value) |
||||||
|
{ |
||||||
|
return (float) floor((double) value); |
||||||
|
} |
||||||
|
|
||||||
|
char *realpath(const char *path, char *resolved) |
||||||
|
{ |
||||||
|
char cwd[256]; |
||||||
|
size_t path_size; |
||||||
|
size_t cwd_size = 0; |
||||||
|
if (path == 0 || *path == '\0') { |
||||||
|
errno = ENOENT; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
path_size = strlen(path); |
||||||
|
if (path[0] != '/') { |
||||||
|
if (getcwd(cwd, sizeof(cwd)) == 0) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
cwd_size = strlen(cwd); |
||||||
|
} |
||||||
|
if (resolved == 0) { |
||||||
|
resolved = (char *) malloc(cwd_size + (cwd_size > 1 ? 1 : 0) |
||||||
|
+ path_size + 1); |
||||||
|
if (resolved == 0) { |
||||||
|
errno = ENOMEM; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
} |
||||||
|
if (cwd_size != 0) { |
||||||
|
memcpy(resolved, cwd, cwd_size); |
||||||
|
if (cwd_size > 1) { |
||||||
|
resolved[cwd_size++] = '/'; |
||||||
|
} |
||||||
|
} |
||||||
|
memcpy(resolved + cwd_size, path, path_size + 1); |
||||||
|
return resolved; |
||||||
|
} |
||||||
|
|
||||||
|
int typephp_os_sscanf(const char *input, const char *format, ...) |
||||||
|
__asm__("__isoc99_sscanf"); |
||||||
|
|
||||||
|
int typephp_os_sscanf(const char *input, const char *format, ...) |
||||||
|
{ |
||||||
|
va_list arguments; |
||||||
|
int assigned = 0; |
||||||
|
va_start(arguments, format); |
||||||
|
while (*format != '\0') { |
||||||
|
if (*format != '%') { |
||||||
|
if (*input++ != *format++) { |
||||||
|
break; |
||||||
|
} |
||||||
|
continue; |
||||||
|
} |
||||||
|
++format; |
||||||
|
if (*format == 'd' || *format == 'u') { |
||||||
|
char *end = 0; |
||||||
|
const int signed_value = *format == 'd'; |
||||||
|
unsigned long value = strtoul(input, &end, 10); |
||||||
|
if (end == input) { |
||||||
|
break; |
||||||
|
} |
||||||
|
if (signed_value) { |
||||||
|
*va_arg(arguments, int *) = (int) value; |
||||||
|
} else { |
||||||
|
*va_arg(arguments, unsigned int *) = (unsigned int) value; |
||||||
|
} |
||||||
|
input = end; |
||||||
|
++format; |
||||||
|
++assigned; |
||||||
|
continue; |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
va_end(arguments); |
||||||
|
return assigned; |
||||||
|
} |
||||||
|
|
||||||
|
int typephp_os_xpg_strerror_r(int error, char *buffer, size_t size) |
||||||
|
__asm__("__xpg_strerror_r"); |
||||||
|
|
||||||
|
int typephp_os_xpg_strerror_r(int error, char *buffer, size_t size) |
||||||
|
{ |
||||||
|
const char *message = strerror(error); |
||||||
|
const size_t length = strlen(message); |
||||||
|
if (size == 0 || length >= size) { |
||||||
|
return ERANGE; |
||||||
|
} |
||||||
|
memcpy(buffer, message, length + 1); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
char *typephp_os_gnu_strerror_r(int error, char *buffer, size_t size) |
||||||
|
__asm__("strerror_r"); |
||||||
|
|
||||||
|
char *typephp_os_gnu_strerror_r(int error, char *buffer, size_t size) |
||||||
|
{ |
||||||
|
return typephp_os_xpg_strerror_r(error, buffer, size) == 0 |
||||||
|
? buffer : strerror(error); |
||||||
|
} |
||||||
|
|
||||||
|
char *__xpg_basename(char *path) |
||||||
|
{ |
||||||
|
char *last = path; |
||||||
|
if (path == 0 || *path == '\0') { |
||||||
|
return "."; |
||||||
|
} |
||||||
|
while (*path != '\0') { |
||||||
|
if (*path == '/' && path[1] != '\0') { |
||||||
|
last = path + 1; |
||||||
|
} |
||||||
|
++path; |
||||||
|
} |
||||||
|
return last; |
||||||
|
} |
||||||
@ -0,0 +1,379 @@ |
|||||||
|
#include <arpa/inet.h> |
||||||
|
#include <errno.h> |
||||||
|
#include <netdb.h> |
||||||
|
#include <netinet/in.h> |
||||||
|
#include <poll.h> |
||||||
|
#include <stdint.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
#include <sys/socket.h> |
||||||
|
#include <sys/syscall.h> |
||||||
|
#include <sys/time.h> |
||||||
|
|
||||||
|
long syscall(long number, ...); |
||||||
|
|
||||||
|
int socket(int domain, int type, int protocol) |
||||||
|
{ |
||||||
|
return (int) syscall(SYS_socket, domain, type, protocol); |
||||||
|
} |
||||||
|
|
||||||
|
int connect(int fd, const struct sockaddr *address, socklen_t length) |
||||||
|
{ |
||||||
|
return (int) syscall(SYS_connect, fd, address, length); |
||||||
|
} |
||||||
|
|
||||||
|
int bind(int fd, const struct sockaddr *address, socklen_t length) |
||||||
|
{ |
||||||
|
return (int) syscall(SYS_bind, fd, address, length); |
||||||
|
} |
||||||
|
|
||||||
|
ssize_t sendto(int fd, const void *buffer, size_t length, int flags, |
||||||
|
const struct sockaddr *address, socklen_t address_length) |
||||||
|
{ |
||||||
|
return (ssize_t) syscall(SYS_sendto, fd, buffer, length, flags, |
||||||
|
address, address_length); |
||||||
|
} |
||||||
|
|
||||||
|
ssize_t recvfrom(int fd, void *buffer, size_t length, int flags, |
||||||
|
struct sockaddr *address, socklen_t *address_length) |
||||||
|
{ |
||||||
|
return (ssize_t) syscall(SYS_recvfrom, fd, buffer, length, flags, |
||||||
|
address, address_length); |
||||||
|
} |
||||||
|
|
||||||
|
ssize_t send(int fd, const void *buffer, size_t length, int flags) |
||||||
|
{ |
||||||
|
return sendto(fd, buffer, length, flags, 0, 0); |
||||||
|
} |
||||||
|
|
||||||
|
ssize_t recv(int fd, void *buffer, size_t length, int flags) |
||||||
|
{ |
||||||
|
return recvfrom(fd, buffer, length, flags, 0, 0); |
||||||
|
} |
||||||
|
|
||||||
|
int shutdown(int fd, int how) |
||||||
|
{ |
||||||
|
return (int) syscall(SYS_shutdown, fd, how); |
||||||
|
} |
||||||
|
|
||||||
|
int getsockname(int fd, struct sockaddr *address, socklen_t *length) |
||||||
|
{ |
||||||
|
return (int) syscall(SYS_getsockname, fd, address, length); |
||||||
|
} |
||||||
|
|
||||||
|
int getpeername(int fd, struct sockaddr *address, socklen_t *length) |
||||||
|
{ |
||||||
|
return (int) syscall(SYS_getpeername, fd, address, length); |
||||||
|
} |
||||||
|
|
||||||
|
int setsockopt(int fd, int level, int option, const void *value, socklen_t length) |
||||||
|
{ |
||||||
|
return (int) syscall(SYS_setsockopt, fd, level, option, value, length); |
||||||
|
} |
||||||
|
|
||||||
|
int getsockopt(int fd, int level, int option, void *value, socklen_t *length) |
||||||
|
{ |
||||||
|
return (int) syscall(SYS_getsockopt, fd, level, option, value, length); |
||||||
|
} |
||||||
|
|
||||||
|
int poll(struct pollfd *fds, nfds_t count, int timeout) |
||||||
|
{ |
||||||
|
return (int) syscall(SYS_poll, fds, count, timeout); |
||||||
|
} |
||||||
|
|
||||||
|
int typephp_os_select(int count, void *read_set_pointer, void *write_set_pointer, |
||||||
|
void *except_set_pointer, struct timeval *timeout) __asm__("select"); |
||||||
|
|
||||||
|
int typephp_os_select(int count, void *read_set_pointer, void *write_set_pointer, |
||||||
|
void *except_set_pointer, struct timeval *timeout) |
||||||
|
{ |
||||||
|
enum { BITS_PER_WORD = (int) (sizeof(unsigned long) * 8), MAX_FDS = 1024 }; |
||||||
|
unsigned long *read_set = (unsigned long *) read_set_pointer; |
||||||
|
unsigned long *write_set = (unsigned long *) write_set_pointer; |
||||||
|
unsigned long *except_set = (unsigned long *) except_set_pointer; |
||||||
|
struct pollfd fds[MAX_FDS]; |
||||||
|
int fd_map[MAX_FDS]; |
||||||
|
int poll_count = 0; |
||||||
|
int milliseconds = -1; |
||||||
|
int ready; |
||||||
|
int fd; |
||||||
|
if (count < 0 || count > MAX_FDS) { |
||||||
|
errno = EINVAL; |
||||||
|
return -1; |
||||||
|
} |
||||||
|
if (timeout != 0) { |
||||||
|
int64_t value = (int64_t) timeout->tv_sec * 1000 |
||||||
|
+ (timeout->tv_usec + 999) / 1000; |
||||||
|
milliseconds = value > INT32_MAX ? INT32_MAX : (int) value; |
||||||
|
} |
||||||
|
for (fd = 0; fd < count; ++fd) { |
||||||
|
const unsigned long mask = 1ul << (fd % BITS_PER_WORD); |
||||||
|
short events = 0; |
||||||
|
if (read_set != 0 && (read_set[fd / BITS_PER_WORD] & mask) != 0) { |
||||||
|
events |= POLLIN; |
||||||
|
} |
||||||
|
if (write_set != 0 && (write_set[fd / BITS_PER_WORD] & mask) != 0) { |
||||||
|
events |= POLLOUT; |
||||||
|
} |
||||||
|
if (except_set != 0 && (except_set[fd / BITS_PER_WORD] & mask) != 0) { |
||||||
|
events |= POLLERR; |
||||||
|
} |
||||||
|
if (events != 0) { |
||||||
|
fds[poll_count].fd = fd; |
||||||
|
fds[poll_count].events = events; |
||||||
|
fds[poll_count].revents = 0; |
||||||
|
fd_map[poll_count++] = fd; |
||||||
|
} |
||||||
|
} |
||||||
|
if (read_set != 0) { |
||||||
|
memset(read_set, 0, ((size_t) count + BITS_PER_WORD - 1) / BITS_PER_WORD |
||||||
|
* sizeof(unsigned long)); |
||||||
|
} |
||||||
|
if (write_set != 0) { |
||||||
|
memset(write_set, 0, ((size_t) count + BITS_PER_WORD - 1) / BITS_PER_WORD |
||||||
|
* sizeof(unsigned long)); |
||||||
|
} |
||||||
|
if (except_set != 0) { |
||||||
|
memset(except_set, 0, ((size_t) count + BITS_PER_WORD - 1) / BITS_PER_WORD |
||||||
|
* sizeof(unsigned long)); |
||||||
|
} |
||||||
|
ready = poll(fds, (nfds_t) poll_count, milliseconds); |
||||||
|
if (ready <= 0) { |
||||||
|
return ready; |
||||||
|
} |
||||||
|
ready = 0; |
||||||
|
for (fd = 0; fd < poll_count; ++fd) { |
||||||
|
const int selected = fd_map[fd]; |
||||||
|
const unsigned long mask = 1ul << (selected % BITS_PER_WORD); |
||||||
|
int marked = 0; |
||||||
|
if (read_set != 0 && (fds[fd].revents & (POLLIN | POLLHUP)) != 0) { |
||||||
|
read_set[selected / BITS_PER_WORD] |= mask; |
||||||
|
marked = 1; |
||||||
|
} |
||||||
|
if (write_set != 0 && (fds[fd].revents & POLLOUT) != 0) { |
||||||
|
write_set[selected / BITS_PER_WORD] |= mask; |
||||||
|
marked = 1; |
||||||
|
} |
||||||
|
if (except_set != 0 && (fds[fd].revents & (POLLERR | POLLNVAL)) != 0) { |
||||||
|
except_set[selected / BITS_PER_WORD] |= mask; |
||||||
|
marked = 1; |
||||||
|
} |
||||||
|
ready += marked; |
||||||
|
} |
||||||
|
return ready; |
||||||
|
} |
||||||
|
|
||||||
|
static int parse_ipv4(const char *text, uint32_t *address) |
||||||
|
{ |
||||||
|
uint32_t octets[4] = {0, 0, 0, 0}; |
||||||
|
unsigned int part = 0; |
||||||
|
int digits = 0; |
||||||
|
if (text == 0 || *text == '\0') { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
while (*text != '\0') { |
||||||
|
if (*text >= '0' && *text <= '9') { |
||||||
|
octets[part] = octets[part] * 10u + (unsigned int) (*text - '0'); |
||||||
|
if (++digits > 3 || octets[part] > 255u) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
} else if (*text == '.' && digits != 0 && part < 3) { |
||||||
|
++part; |
||||||
|
digits = 0; |
||||||
|
} else { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
++text; |
||||||
|
} |
||||||
|
if (part != 3 || digits == 0) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
*address = octets[0] | (octets[1] << 8u) |
||||||
|
| (octets[2] << 16u) | (octets[3] << 24u); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
int inet_pton(int family, const char *text, void *address) |
||||||
|
{ |
||||||
|
uint32_t value; |
||||||
|
if (family != AF_INET) { |
||||||
|
errno = EAFNOSUPPORT; |
||||||
|
return -1; |
||||||
|
} |
||||||
|
if (address == 0 || !parse_ipv4(text, &value)) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
*(uint32_t *) address = value; |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
int inet_aton(const char *text, struct in_addr *address) |
||||||
|
{ |
||||||
|
return address != 0 && inet_pton(AF_INET, text, address) == 1; |
||||||
|
} |
||||||
|
|
||||||
|
in_addr_t inet_addr(const char *text) |
||||||
|
{ |
||||||
|
in_addr_t address; |
||||||
|
return parse_ipv4(text, &address) ? address : INADDR_NONE; |
||||||
|
} |
||||||
|
|
||||||
|
static char *append_octet(char *output, unsigned int value) |
||||||
|
{ |
||||||
|
if (value >= 100) { |
||||||
|
*output++ = (char) ('0' + value / 100u); |
||||||
|
value %= 100u; |
||||||
|
*output++ = (char) ('0' + value / 10u); |
||||||
|
} else if (value >= 10) { |
||||||
|
*output++ = (char) ('0' + value / 10u); |
||||||
|
} |
||||||
|
*output++ = (char) ('0' + value % 10u); |
||||||
|
return output; |
||||||
|
} |
||||||
|
|
||||||
|
const char *inet_ntop(int family, const void *address, char *text, |
||||||
|
socklen_t length) |
||||||
|
{ |
||||||
|
char buffer[INET_ADDRSTRLEN]; |
||||||
|
char *output = buffer; |
||||||
|
const unsigned char *bytes = (const unsigned char *) address; |
||||||
|
unsigned int index; |
||||||
|
if (family != AF_INET) { |
||||||
|
errno = EAFNOSUPPORT; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
if (address == 0 || text == 0) { |
||||||
|
errno = EFAULT; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
for (index = 0; index < 4; ++index) { |
||||||
|
output = append_octet(output, bytes[index]); |
||||||
|
if (index != 3) { |
||||||
|
*output++ = '.'; |
||||||
|
} |
||||||
|
} |
||||||
|
*output = '\0'; |
||||||
|
if ((size_t) (output - buffer + 1) > length) { |
||||||
|
errno = ENOSPC; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
memcpy(text, buffer, (size_t) (output - buffer + 1)); |
||||||
|
return text; |
||||||
|
} |
||||||
|
|
||||||
|
static int service_port(const char *service, uint16_t *port) |
||||||
|
{ |
||||||
|
unsigned int value = 0; |
||||||
|
if (service == 0) { |
||||||
|
*port = 0; |
||||||
|
return 1; |
||||||
|
} |
||||||
|
if (strcmp(service, "http") == 0) { |
||||||
|
*port = 80; |
||||||
|
return 1; |
||||||
|
} |
||||||
|
if (strcmp(service, "https") == 0) { |
||||||
|
*port = 443; |
||||||
|
return 1; |
||||||
|
} |
||||||
|
if (*service == '\0') { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
while (*service != '\0') { |
||||||
|
if (*service < '0' || *service > '9') { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
value = value * 10u + (unsigned int) (*service++ - '0'); |
||||||
|
if (value > 65535u) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
} |
||||||
|
*port = (uint16_t) value; |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
int getaddrinfo(const char *node, const char *service, |
||||||
|
const struct addrinfo *hints, struct addrinfo **result) |
||||||
|
{ |
||||||
|
struct addrinfo *entry; |
||||||
|
struct sockaddr_in *address; |
||||||
|
uint16_t port; |
||||||
|
uint32_t ipv4; |
||||||
|
int flags = hints != 0 ? hints->ai_flags : 0; |
||||||
|
int family = hints != 0 ? hints->ai_family : AF_UNSPEC; |
||||||
|
int socktype = hints != 0 ? hints->ai_socktype : 0; |
||||||
|
int protocol = hints != 0 ? hints->ai_protocol : 0; |
||||||
|
const int supported_flags = AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST |
||||||
|
| AI_ADDRCONFIG | AI_NUMERICSERV; |
||||||
|
if (result == 0) { |
||||||
|
return EAI_FAIL; |
||||||
|
} |
||||||
|
*result = 0; |
||||||
|
if ((flags & ~supported_flags) != 0) { |
||||||
|
return EAI_BADFLAGS; |
||||||
|
} |
||||||
|
if (family != AF_UNSPEC && family != AF_INET) { |
||||||
|
return EAI_FAMILY; |
||||||
|
} |
||||||
|
if (socktype != 0 && socktype != SOCK_STREAM) { |
||||||
|
return EAI_SOCKTYPE; |
||||||
|
} |
||||||
|
if (protocol != 0 && protocol != IPPROTO_TCP) { |
||||||
|
return EAI_SERVICE; |
||||||
|
} |
||||||
|
if (!service_port(service, &port)) { |
||||||
|
return EAI_SERVICE; |
||||||
|
} |
||||||
|
if (node == 0) { |
||||||
|
ipv4 = (flags & AI_PASSIVE) != 0 ? INADDR_ANY : INADDR_LOOPBACK; |
||||||
|
} else if (!parse_ipv4(node, &ipv4)) { |
||||||
|
long status; |
||||||
|
if ((flags & AI_NUMERICHOST) != 0) { |
||||||
|
return EAI_NONAME; |
||||||
|
} |
||||||
|
status = syscall(SYS_typephp_dns_resolve_ipv4, node, &ipv4); |
||||||
|
if (status != 0) { |
||||||
|
return errno == ETIMEDOUT || errno == EAGAIN ? EAI_AGAIN : EAI_NONAME; |
||||||
|
} |
||||||
|
} |
||||||
|
entry = (struct addrinfo *) calloc(1, |
||||||
|
sizeof(*entry) + sizeof(*address)); |
||||||
|
if (entry == 0) { |
||||||
|
return EAI_MEMORY; |
||||||
|
} |
||||||
|
address = (struct sockaddr_in *) (entry + 1); |
||||||
|
address->sin_family = AF_INET; |
||||||
|
address->sin_port = htons(port); |
||||||
|
address->sin_addr.s_addr = ipv4; |
||||||
|
entry->ai_flags = flags; |
||||||
|
entry->ai_family = AF_INET; |
||||||
|
entry->ai_socktype = socktype != 0 ? socktype : SOCK_STREAM; |
||||||
|
entry->ai_protocol = protocol != 0 ? protocol : IPPROTO_TCP; |
||||||
|
entry->ai_addrlen = sizeof(*address); |
||||||
|
entry->ai_addr = (struct sockaddr *) address; |
||||||
|
entry->ai_next = 0; |
||||||
|
*result = entry; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
void freeaddrinfo(struct addrinfo *result) |
||||||
|
{ |
||||||
|
free(result); |
||||||
|
} |
||||||
|
|
||||||
|
const char *gai_strerror(int error) |
||||||
|
{ |
||||||
|
switch (error) { |
||||||
|
case 0: return "Success"; |
||||||
|
case EAI_BADFLAGS: return "Invalid flags"; |
||||||
|
case EAI_NONAME: return "Name or service not known"; |
||||||
|
case EAI_AGAIN: return "Temporary failure in name resolution"; |
||||||
|
case EAI_FAIL: return "Non-recoverable name resolution failure"; |
||||||
|
case EAI_FAMILY: return "Address family not supported"; |
||||||
|
case EAI_SOCKTYPE: return "Socket type not supported"; |
||||||
|
case EAI_SERVICE: return "Service not supported"; |
||||||
|
case EAI_MEMORY: return "Memory allocation failure"; |
||||||
|
case EAI_SYSTEM: return "System error"; |
||||||
|
default: return "Unknown address resolution error"; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,29 @@ |
|||||||
|
#include <stdint.h> |
||||||
|
#include <stddef.h> |
||||||
|
#include <sys/syscall.h> |
||||||
|
|
||||||
|
#include <typephp_os_abi.h> |
||||||
|
|
||||||
|
long syscall(long number, ...); |
||||||
|
int main(int argc, char **argv); |
||||||
|
|
||||||
|
/* Small C utilities only need a modest libc arena. A heavier runtime can
|
||||||
|
* override this weak definition without giving C and TypePHP programs |
||||||
|
* separate crt0 implementations. */ |
||||||
|
__attribute__((weak, noinline)) size_t typephp_os_runtime_arena_size(void) |
||||||
|
{ |
||||||
|
return 2u * 1024u * 1024u; |
||||||
|
} |
||||||
|
|
||||||
|
int typephp_os_runtime_start(int argc, char **argv) |
||||||
|
{ |
||||||
|
const size_t arena_size = typephp_os_runtime_arena_size(); |
||||||
|
const uintptr_t begin = (uintptr_t) syscall(SYS_brk, 0); |
||||||
|
const uintptr_t end = begin + arena_size; |
||||||
|
if (begin == UINTPTR_MAX || end < begin |
||||||
|
|| (uintptr_t) syscall(SYS_brk, end) != end) { |
||||||
|
typephp_os_panic("unable to allocate the userspace runtime arena"); |
||||||
|
} |
||||||
|
typephp_os_memory_init((void *) begin, arena_size); |
||||||
|
return main(argc, argv); |
||||||
|
} |
||||||
@ -0,0 +1,207 @@ |
|||||||
|
/* Minimal descriptor-backed stdio used by OpenSSL certificate loading. */ |
||||||
|
|
||||||
|
#include <errno.h> |
||||||
|
#include <fcntl.h> |
||||||
|
#include <stdint.h> |
||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <unistd.h> |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
uint32_t magic; |
||||||
|
int fd; |
||||||
|
int error; |
||||||
|
int eof; |
||||||
|
} typephp_file; |
||||||
|
|
||||||
|
enum { TYPEPHP_FILE_MAGIC = 0x54504649u }; |
||||||
|
|
||||||
|
static typephp_file *private_file(FILE *stream) |
||||||
|
{ |
||||||
|
typephp_file *file = (typephp_file *) stream; |
||||||
|
return file != 0 && file->magic == TYPEPHP_FILE_MAGIC ? file : 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int mode_flags(const char *mode) |
||||||
|
{ |
||||||
|
int flags; |
||||||
|
if (mode == 0 || *mode == '\0') { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
flags = *mode == 'r' ? O_RDONLY |
||||||
|
: (*mode == 'w' ? O_WRONLY | O_CREAT | O_TRUNC |
||||||
|
: (*mode == 'a' ? O_WRONLY | O_CREAT | O_APPEND : -1)); |
||||||
|
if (flags >= 0 && mode[1] == '+') { |
||||||
|
flags = (flags & ~(O_RDONLY | O_WRONLY)) | O_RDWR; |
||||||
|
} |
||||||
|
return flags; |
||||||
|
} |
||||||
|
|
||||||
|
FILE *fopen(const char *path, const char *mode) |
||||||
|
{ |
||||||
|
const int flags = mode_flags(mode); |
||||||
|
typephp_file *file; |
||||||
|
int fd; |
||||||
|
if (flags < 0) { |
||||||
|
errno = EINVAL; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
fd = open(path, flags, 0666); |
||||||
|
if (fd < 0) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
file = (typephp_file *) malloc(sizeof(*file)); |
||||||
|
if (file == 0) { |
||||||
|
close(fd); |
||||||
|
errno = ENOMEM; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
file->magic = TYPEPHP_FILE_MAGIC; |
||||||
|
file->fd = fd; |
||||||
|
file->error = 0; |
||||||
|
file->eof = 0; |
||||||
|
return (FILE *) file; |
||||||
|
} |
||||||
|
|
||||||
|
FILE *fopen64(const char *path, const char *mode) |
||||||
|
{ |
||||||
|
return fopen(path, mode); |
||||||
|
} |
||||||
|
|
||||||
|
int fclose(FILE *stream) |
||||||
|
{ |
||||||
|
typephp_file *file = private_file(stream); |
||||||
|
int status; |
||||||
|
if (file == 0) { |
||||||
|
errno = EBADF; |
||||||
|
return EOF; |
||||||
|
} |
||||||
|
status = close(file->fd); |
||||||
|
file->magic = 0; |
||||||
|
free(file); |
||||||
|
return status; |
||||||
|
} |
||||||
|
|
||||||
|
size_t fread(void *buffer, size_t size, size_t count, FILE *stream) |
||||||
|
{ |
||||||
|
typephp_file *file = private_file(stream); |
||||||
|
size_t bytes; |
||||||
|
ssize_t received; |
||||||
|
if (file == 0 || (size != 0 && count > SIZE_MAX / size)) { |
||||||
|
errno = EINVAL; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
bytes = size * count; |
||||||
|
received = read(file->fd, buffer, bytes); |
||||||
|
if (received < 0) { |
||||||
|
file->error = 1; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
if ((size_t) received < bytes) { |
||||||
|
file->eof = 1; |
||||||
|
} |
||||||
|
return size == 0 ? 0 : (size_t) received / size; |
||||||
|
} |
||||||
|
|
||||||
|
char *fgets(char *buffer, int size, FILE *stream) |
||||||
|
{ |
||||||
|
typephp_file *file = private_file(stream); |
||||||
|
int offset = 0; |
||||||
|
if (file == 0 || buffer == 0 || size <= 0) { |
||||||
|
errno = EINVAL; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
while (offset + 1 < size) { |
||||||
|
ssize_t received = read(file->fd, buffer + offset, 1); |
||||||
|
if (received < 0) { |
||||||
|
file->error = 1; |
||||||
|
return offset == 0 ? 0 : buffer; |
||||||
|
} |
||||||
|
if (received == 0) { |
||||||
|
file->eof = 1; |
||||||
|
break; |
||||||
|
} |
||||||
|
if (buffer[offset++] == '\n') { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
if (offset == 0) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
buffer[offset] = '\0'; |
||||||
|
return buffer; |
||||||
|
} |
||||||
|
|
||||||
|
int ferror(FILE *stream) |
||||||
|
{ |
||||||
|
typephp_file *file = private_file(stream); |
||||||
|
return file != 0 ? file->error : 1; |
||||||
|
} |
||||||
|
|
||||||
|
int feof(FILE *stream) |
||||||
|
{ |
||||||
|
typephp_file *file = private_file(stream); |
||||||
|
return file != 0 ? file->eof : 0; |
||||||
|
} |
||||||
|
|
||||||
|
void clearerr(FILE *stream) |
||||||
|
{ |
||||||
|
typephp_file *file = private_file(stream); |
||||||
|
if (file != 0) { |
||||||
|
file->error = 0; |
||||||
|
file->eof = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
int fseeko(FILE *stream, off_t offset, int whence) |
||||||
|
{ |
||||||
|
typephp_file *file = private_file(stream); |
||||||
|
if (file == 0) { |
||||||
|
errno = EBADF; |
||||||
|
return -1; |
||||||
|
} |
||||||
|
file->eof = 0; |
||||||
|
return lseek(file->fd, offset, whence) < 0 ? -1 : 0; |
||||||
|
} |
||||||
|
|
||||||
|
off_t ftello(FILE *stream) |
||||||
|
{ |
||||||
|
typephp_file *file = private_file(stream); |
||||||
|
if (file == 0) { |
||||||
|
errno = EBADF; |
||||||
|
return -1; |
||||||
|
} |
||||||
|
return lseek(file->fd, 0, SEEK_CUR); |
||||||
|
} |
||||||
|
|
||||||
|
int fseek(FILE *stream, long offset, int whence) |
||||||
|
{ |
||||||
|
return fseeko(stream, (off_t) offset, whence); |
||||||
|
} |
||||||
|
|
||||||
|
long ftell(FILE *stream) |
||||||
|
{ |
||||||
|
return (long) ftello(stream); |
||||||
|
} |
||||||
|
|
||||||
|
void rewind(FILE *stream) |
||||||
|
{ |
||||||
|
(void) fseeko(stream, 0, SEEK_SET); |
||||||
|
clearerr(stream); |
||||||
|
} |
||||||
|
|
||||||
|
void setbuf(FILE *stream, char *buffer) |
||||||
|
{ |
||||||
|
(void) stream; |
||||||
|
(void) buffer; |
||||||
|
} |
||||||
|
|
||||||
|
size_t __fread_chk(void *buffer, size_t buffer_size, size_t size, |
||||||
|
size_t count, FILE *stream) |
||||||
|
{ |
||||||
|
if (size != 0 && count > buffer_size / size) { |
||||||
|
errno = ERANGE; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
return fread(buffer, size, count, stream); |
||||||
|
} |
||||||
Loading…
Reference in new issue