302 lines
8.8 KiB
C
302 lines
8.8 KiB
C
// User application entry points for httpget.
|
|
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <ntlite/syscall.h>
|
|
|
|
// Prints the requested operation.
|
|
static void print(const char* s) {
|
|
if (s) { SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) }; SysWriteConsole(&req); }
|
|
}
|
|
|
|
// Prints char.
|
|
static void print_char(char c) {
|
|
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
|
|
SysWriteConsole(&req);
|
|
}
|
|
|
|
// Prints u64.
|
|
static void print_u64(uint64_t v) {
|
|
char buf[32];
|
|
int n = 0;
|
|
if (v == 0) { print("0"); return; }
|
|
while (v && n < (int)sizeof(buf)) {
|
|
buf[n++] = (char)('0' + (v % 10));
|
|
v /= 10;
|
|
}
|
|
while (n > 0) print_char(buf[--n]);
|
|
}
|
|
|
|
// Appends char.
|
|
static void append_char(char* dst, size_t cap, size_t* off, char c) {
|
|
if (!dst || !off || cap == 0 || *off + 1 >= cap) return;
|
|
dst[*off] = c;
|
|
*off += 1;
|
|
dst[*off] = 0;
|
|
}
|
|
|
|
// Appends str.
|
|
static void append_str(char* dst, size_t cap, size_t* off, const char* s) {
|
|
if (!s) return;
|
|
while (*s) append_char(dst, cap, off, *s++);
|
|
}
|
|
|
|
// Appends u8.
|
|
static void append_u8(char* dst, size_t cap, size_t* off, uint8_t v) {
|
|
if (v >= 100) {
|
|
append_char(dst, cap, off, (char)('0' + v / 100));
|
|
v %= 100;
|
|
append_char(dst, cap, off, (char)('0' + v / 10));
|
|
append_char(dst, cap, off, (char)('0' + v % 10));
|
|
} else if (v >= 10) {
|
|
append_char(dst, cap, off, (char)('0' + v / 10));
|
|
append_char(dst, cap, off, (char)('0' + v % 10));
|
|
} else {
|
|
append_char(dst, cap, off, (char)('0' + v));
|
|
}
|
|
}
|
|
|
|
// Implements the format ipv4 operation.
|
|
static void format_ipv4(char* dst, size_t cap, const uint8_t ip[4]) {
|
|
size_t off = 0;
|
|
if (!dst || cap == 0) return;
|
|
dst[0] = 0;
|
|
append_u8(dst, cap, &off, ip[0]); append_char(dst, cap, &off, '.');
|
|
append_u8(dst, cap, &off, ip[1]); append_char(dst, cap, &off, '.');
|
|
append_u8(dst, cap, &off, ip[2]); append_char(dst, cap, &off, '.');
|
|
append_u8(dst, cap, &off, ip[3]);
|
|
}
|
|
|
|
static void append_hex16(char* dst, size_t cap, size_t* off, uint16_t value) {
|
|
static const char hex[] = "0123456789abcdef";
|
|
int started = 0;
|
|
for (int shift = 12; shift >= 0; shift -= 4) {
|
|
unsigned nibble = (value >> shift) & 0xfu;
|
|
if (nibble || started || shift == 0) {
|
|
append_char(dst, cap, off, hex[nibble]);
|
|
started = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Implements the format ipv6 operation.
|
|
static void format_ipv6(char* dst, size_t cap, const uint8_t ip[16]) {
|
|
size_t off = 0;
|
|
if (!dst || cap == 0) return;
|
|
dst[0] = 0;
|
|
for (int i = 0; i < 8; ++i) {
|
|
uint16_t word = ((uint16_t)ip[i * 2] << 8) | ip[i * 2 + 1];
|
|
append_hex16(dst, cap, &off, word);
|
|
if (i != 7) append_char(dst, cap, &off, ':');
|
|
}
|
|
}
|
|
|
|
// Implements the format socket address operation.
|
|
static void format_socket_address(char* dst, size_t cap, const SYS_SOCKET_ADDRESS* address) {
|
|
if (!address || address->Family == SYS_AF_INET)
|
|
format_ipv4(dst, cap, address ? address->Address : (const uint8_t*)"\0\0\0\0");
|
|
else
|
|
format_ipv6(dst, cap, address->Address);
|
|
}
|
|
|
|
// Parses u8.
|
|
static bool parse_u8(const char** p, uint8_t* out) {
|
|
const char* s = *p;
|
|
uint32_t v = 0;
|
|
int digits = 0;
|
|
while (*s >= '0' && *s <= '9') {
|
|
v = v * 10u + (uint32_t)(*s - '0');
|
|
if (v > 255u) return false;
|
|
s++;
|
|
digits++;
|
|
}
|
|
if (digits == 0) return false;
|
|
*out = (uint8_t)v;
|
|
*p = s;
|
|
return true;
|
|
}
|
|
|
|
// Parses ipv4.
|
|
static bool parse_ipv4(const char* s, uint8_t ip[4]) {
|
|
if (!s || !ip) return false;
|
|
for (int i = 0; i < 4; i++) {
|
|
if (!parse_u8(&s, &ip[i])) return false;
|
|
if (i != 3) {
|
|
if (*s != '.') return false;
|
|
s++;
|
|
}
|
|
}
|
|
return *s == 0;
|
|
}
|
|
|
|
// Implements the next token operation.
|
|
static const char* next_token(const char* s, char* out, size_t outSize) {
|
|
if (!s || !out || outSize == 0) return NULL;
|
|
while (*s == ' ' || *s == '\t') s++;
|
|
if (*s == 0) { out[0] = 0; return NULL; }
|
|
|
|
size_t n = 0;
|
|
while (*s && *s != ' ' && *s != '\t') {
|
|
if (n + 1 < outSize) out[n++] = *s;
|
|
s++;
|
|
}
|
|
out[n] = 0;
|
|
return s;
|
|
}
|
|
|
|
// Implements the usage operation.
|
|
static void usage(void) {
|
|
print("usage: httpget.exe [host-or-ipv4] [path]\n");
|
|
print("default: httpget.exe 10.0.2.2 /\n");
|
|
}
|
|
|
|
// Implements the resolve host operation.
|
|
static uint32_t resolve_host(const char* host, SYS_SOCKET_ADDRESS* address) {
|
|
uint8_t ip[4];
|
|
if (parse_ipv4(host, ip)) {
|
|
memset(address, 0, sizeof(*address));
|
|
address->Size = sizeof(*address);
|
|
address->Family = SYS_AF_INET;
|
|
memcpy(address->Address, ip, 4);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
size_t resolvedSize = sizeof(*address);
|
|
SYS_RESOLVE_HOST_REQUEST req;
|
|
memset(address, 0, sizeof(*address));
|
|
memset(&req, 0, sizeof(req));
|
|
req.Name = SysSvFromCstr(host);
|
|
req.Family = SYS_AF_INET6;
|
|
req.AddressOut = address;
|
|
req.AddressSizeInOut = &resolvedSize;
|
|
|
|
uint32_t st = SysResolveHost(&req);
|
|
if (st == NTLSTATUS_SUCCESS)
|
|
return NTLSTATUS_SUCCESS;
|
|
|
|
resolvedSize = sizeof(*address);
|
|
memset(address, 0, sizeof(*address));
|
|
req.Family = SYS_AF_INET;
|
|
st = SysResolveHost(&req);
|
|
return st;
|
|
}
|
|
|
|
// Runs the requested operation.
|
|
int main(void) {
|
|
SYS_SOCKET_ADDRESS addr;
|
|
char host[256];
|
|
char path[128];
|
|
strcpy(host, "10.0.2.2");
|
|
path[0] = '/';
|
|
path[1] = 0;
|
|
|
|
const char* cmd = SysGetPeb()->CommandLine.Data;
|
|
char tok[256];
|
|
cmd = next_token(cmd, tok, sizeof(tok)); // program path
|
|
if (cmd) {
|
|
const char* rest = next_token(cmd, tok, sizeof(tok));
|
|
if (tok[0]) {
|
|
if (strlen(tok) >= sizeof(host)) {
|
|
print("httpget: host too long\n");
|
|
SysExitProcess(1);
|
|
}
|
|
strcpy(host, tok);
|
|
cmd = rest;
|
|
}
|
|
if (cmd) {
|
|
(void)next_token(cmd, tok, sizeof(tok));
|
|
if (tok[0]) {
|
|
size_t len = strlen(tok);
|
|
if (len + 2 > sizeof(path)) {
|
|
print("httpget: path too long\n");
|
|
SysExitProcess(1);
|
|
}
|
|
if (tok[0] != '/') {
|
|
path[0] = '/';
|
|
memcpy(path + 1, tok, len + 1);
|
|
} else {
|
|
memcpy(path, tok, len + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t st = resolve_host(host, &addr);
|
|
if (st != NTLSTATUS_SUCCESS) {
|
|
print("httpget: resolve failed "); print_u64(st); print(" for "); print(host); print("\n");
|
|
usage();
|
|
SysExitProcess(1);
|
|
}
|
|
|
|
char ipText[64];
|
|
format_socket_address(ipText, sizeof(ipText), &addr);
|
|
|
|
print("httpget: GET http://");
|
|
print(host);
|
|
print(path);
|
|
print(" (");
|
|
print(ipText);
|
|
print(")\n");
|
|
|
|
uint32_t sock = 0;
|
|
st = SysCreateSocket(addr.Family, SYS_SOCK_STREAM, SYS_IPPROTO_TCP, &sock);
|
|
if (st != NTLSTATUS_SUCCESS) {
|
|
print("httpget: SysCreateSocket failed "); print_u64(st); print("\n");
|
|
SysExitProcess(1);
|
|
}
|
|
|
|
addr.Port = 80;
|
|
|
|
st = SysConnectSocket(sock, &addr, sizeof(addr));
|
|
if (st != NTLSTATUS_SUCCESS) {
|
|
print("httpget: SysConnectSocket failed "); print_u64(st); print("\n");
|
|
SysCloseHandle(sock);
|
|
SysExitProcess(1);
|
|
}
|
|
|
|
char request[512];
|
|
size_t off = 0;
|
|
request[0] = 0;
|
|
append_str(request, sizeof(request), &off, "GET ");
|
|
append_str(request, sizeof(request), &off, path);
|
|
append_str(request, sizeof(request), &off, " HTTP/1.0\r\nHost: ");
|
|
append_str(request, sizeof(request), &off, host);
|
|
append_str(request, sizeof(request), &off, "\r\nUser-Agent: nt-lite-sys-socket\r\nConnection: close\r\n\r\n");
|
|
|
|
size_t totalSent = 0;
|
|
while (totalSent < off) {
|
|
size_t sent = 0;
|
|
st = SysSendSocket(sock, request + totalSent, off - totalSent, 0, &sent);
|
|
if (st != NTLSTATUS_SUCCESS || sent == 0) {
|
|
print("httpget: SysSendSocket failed "); print_u64(st); print(" sent="); print_u64(sent); print("\n");
|
|
SysCloseHandle(sock);
|
|
SysExitProcess(1);
|
|
}
|
|
totalSent += sent;
|
|
}
|
|
|
|
char buf[513];
|
|
size_t total = 0;
|
|
while (true) {
|
|
size_t got = 0;
|
|
st = SysRecvSocket(sock, buf, sizeof(buf) - 1, 0, &got);
|
|
if (st != NTLSTATUS_SUCCESS) {
|
|
print("\nhttpget: SysRecvSocket failed "); print_u64(st); print(" after "); print_u64(total); print(" bytes\n");
|
|
SysCloseHandle(sock);
|
|
SysExitProcess(1);
|
|
}
|
|
if (got == 0) break;
|
|
buf[got] = 0;
|
|
print(buf);
|
|
total += got;
|
|
}
|
|
|
|
print("\nhttpget: done, "); print_u64(total); print(" bytes\n");
|
|
SysShutdownSocket(sock, SYS_SHUTDOWN_BOTH);
|
|
SysCloseHandle(sock);
|
|
SysExitProcess(0);
|
|
return 0;
|
|
}
|