Files
nt-lite/code/user/apps/udp/udp.c
T

316 lines
8.6 KiB
C

// User application entry points for udp.
#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]);
}
// Prints ipv4.
static void print_ipv4(const uint8_t ip[4]) {
print_u64(ip[0]); print("."); print_u64(ip[1]); print(".");
print_u64(ip[2]); print("."); print_u64(ip[3]);
}
// Prints ipv6.
static void print_ipv6(const uint8_t ip[16]) {
static const char hex[] = "0123456789abcdef";
for (int i = 0; i < 8; ++i) {
uint16_t word = ((uint16_t)ip[i * 2] << 8) | ip[i * 2 + 1];
int started = 0;
for (int shift = 12; shift >= 0; shift -= 4) {
unsigned nibble = (word >> shift) & 0xfu;
if (nibble || started || shift == 0) {
char c = hex[nibble];
print_char(c);
started = 1;
}
}
if (i != 7) print(":");
}
}
// Prints socket address.
static void print_socket_address(const SYS_SOCKET_ADDRESS* address) {
if (!address) return;
if (address->Family == SYS_AF_INET) {
print_ipv4(address->Address);
print(":");
print_u64(address->Port);
} else if (address->Family == SYS_AF_INET6) {
print("[");
print_ipv6(address->Address);
print("]:");
print_u64(address->Port);
} else {
print("unknown");
}
}
// 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;
}
// 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;
}
// Parses port.
static bool parse_port(const char* s, uint16_t* out) {
if (!s || !*s || !out) return false;
uint32_t v = 0;
while (*s >= '0' && *s <= '9') {
v = v * 10u + (uint32_t)(*s - '0');
if (v > 65535u) return false;
s++;
}
if (*s != 0 || v == 0) return false;
*out = (uint16_t)v;
return true;
}
// Implements the usage operation.
static void usage(void) {
print("usage:\n");
print(" udp.exe send <host|address> <port> [message]\n");
print(" udp.exe listen [-4|-6] <port>\n");
print("examples:\n");
print(" udp.exe send 10.0.2.2 9999 hello\n");
print(" udp.exe send ::1 9999 hello\n");
print(" udp.exe listen -6 9999\n");
}
// Resolves an IPv4 or IPv6 destination.
static uint32_t resolve_address(const char* text, SYS_SOCKET_ADDRESS* out) {
uint8_t ip[4];
if (parse_ipv4(text, ip)) {
memset(out, 0, sizeof(*out));
out->Size = sizeof(*out);
out->Family = SYS_AF_INET;
memcpy(out->Address, ip, 4);
return NTLSTATUS_SUCCESS;
}
SYS_RESOLVE_HOST_REQUEST req;
size_t addressSize = sizeof(*out);
memset(out, 0, sizeof(*out));
memset(&req, 0, sizeof(req));
req.Name = SysSvFromCstr(text);
req.Family = SYS_AF_INET6;
req.AddressOut = out;
req.AddressSizeInOut = &addressSize;
uint32_t status = SysResolveHost(&req);
if (status == NTLSTATUS_SUCCESS)
return NTLSTATUS_SUCCESS;
addressSize = sizeof(*out);
memset(out, 0, sizeof(*out));
req.Family = SYS_AF_INET;
return SysResolveHost(&req);
}
// Runs send.
static int run_send(const char* cmd) {
char tok[256];
SYS_SOCKET_ADDRESS remote;
uint16_t port = 0;
cmd = next_token(cmd, tok, sizeof(tok));
if (!tok[0]) { usage(); return 1; }
uint32_t status = resolve_address(tok, &remote);
if (status != NTLSTATUS_SUCCESS) { print("udp: resolve failed "); print_u64(status); print("\n"); return 1; }
cmd = next_token(cmd, tok, sizeof(tok));
if (!tok[0] || !parse_port(tok, &port)) { usage(); return 1; }
// Skip leading whitespace, then take the entire remaining command line.
if (cmd) {
while (*cmd == ' ' || *cmd == '\t') {
cmd++;
}
}
const char* msg = "hello from nt-lite udp\n";
if (cmd && *cmd != '\0') {
msg = cmd;
}
uint32_t sock = 0;
uint32_t st = SysCreateSocket(remote.Family, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &sock);
if (st != NTLSTATUS_SUCCESS) {
print("udp: SysCreateSocket failed "); print_u64(st); print("\n");
return 1;
}
remote.Port = port;
size_t sent = 0;
SYS_SOCKET_SEND_TO_REQUEST req;
memset(&req, 0, sizeof(req));
req.Buffer = msg;
req.Length = strlen(msg);
req.RemoteAddress = &remote;
req.RemoteAddressSize = sizeof(remote);
req.SentOut = &sent;
st = SysSendToSocket(sock, &req);
SysCloseHandle(sock);
if (st != NTLSTATUS_SUCCESS) {
print("udp: SysSendToSocket failed "); print_u64(st); print("\n");
return 1;
}
print("udp: sent "); print_u64(sent); print(" bytes to "); print_socket_address(&remote); print("\n");
return 0;
}
// Runs listen.
static int run_listen(const char* cmd) {
char tok[64];
uint16_t port = 0;
uint32_t family = SYS_AF_INET;
const char* rest = next_token(cmd, tok, sizeof(tok));
if (strcmp(tok, "-4") == 0 || strcmp(tok, "-6") == 0) {
family = strcmp(tok, "-6") == 0 ? SYS_AF_INET6 : SYS_AF_INET;
if (rest)
(void)next_token(rest, tok, sizeof(tok));
else
tok[0] = 0;
}
if (!tok[0] || !parse_port(tok, &port)) { usage(); return 1; }
uint32_t sock = 0;
uint32_t st = SysCreateSocket(family, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &sock);
if (st != NTLSTATUS_SUCCESS) {
print("udp: SysCreateSocket failed "); print_u64(st); print("\n");
return 1;
}
SYS_SOCKET_ADDRESS bindAddr;
memset(&bindAddr, 0, sizeof(bindAddr));
bindAddr.Size = sizeof(bindAddr);
bindAddr.Family = family;
bindAddr.Port = port;
st = SysBindSocket(sock, &bindAddr, sizeof(bindAddr));
if (st != NTLSTATUS_SUCCESS) {
print("udp: SysBindSocket failed "); print_u64(st); print("\n");
SysCloseHandle(sock);
return 1;
}
print("udp: listening on ");
if (family == SYS_AF_INET6) print("[::]:"); else print("0.0.0.0:");
print_u64(port); print(" for one datagram\n");
char buf[1025];
SYS_SOCKET_ADDRESS remote;
size_t remoteSize = sizeof(remote);
size_t got = 0;
SYS_SOCKET_RECV_FROM_REQUEST req;
memset(&req, 0, sizeof(req));
req.Buffer = buf;
req.Length = sizeof(buf) - 1;
req.RemoteAddressOut = &remote;
req.RemoteAddressSizeInOut = &remoteSize;
req.ReceivedOut = &got;
st = SysRecvFromSocket(sock, &req);
SysCloseHandle(sock);
if (st != NTLSTATUS_SUCCESS) {
print("udp: SysRecvFromSocket failed "); print_u64(st); print("\n");
return 1;
}
buf[got] = 0;
print("udp: received "); print_u64(got); print(" bytes from ");
print_socket_address(&remote);
print("\n");
print(buf);
if (got && buf[got - 1] != '\n') print("\n");
return 0;
}
// Runs the requested operation.
int main(void) {
const char* cmd = SysGetPeb()->CommandLine.Data;
char tok[64];
cmd = next_token(cmd, tok, sizeof(tok)); // program path
cmd = next_token(cmd, tok, sizeof(tok)); // verb
if (!tok[0]) { usage(); SysExitProcess(1); }
int rc;
if (strcmp(tok, "send") == 0) {
rc = run_send(cmd);
} else if (strcmp(tok, "listen") == 0) {
rc = run_listen(cmd);
} else {
usage();
rc = 1;
}
SysExitProcess(rc);
return rc;
}