Initial import

This commit is contained in:
Justine Tunney 2020-06-15 07:18:57 -07:00
commit c91b3c5006
14915 changed files with 590219 additions and 0 deletions

78
tool/net/dig.c Normal file
View file

@ -0,0 +1,78 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/dns/dns.h"
#include "libc/log/log.h"
#include "libc/runtime/runtime.h"
#include "libc/sock/sock.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/ai.h"
#include "libc/sysv/consts/ipproto.h"
#include "libc/sysv/consts/sock.h"
#include "tool/decode/lib/flagger.h"
#include "tool/decode/lib/idname.h"
#include "tool/decode/lib/socknames.h"
void lookup(const char *name) {
struct addrinfo hints = (struct addrinfo){.ai_family = AF_INET,
.ai_socktype = SOCK_STREAM,
.ai_protocol = IPPROTO_TCP,
.ai_flags = AI_NUMERICSERV};
struct addrinfo *addrs = NULL;
if (getaddrinfo(name, "80", &hints, &addrs) == -1) {
perror("getaddrinfo");
exit(1);
}
if (addrs) {
for (struct addrinfo *addr = addrs; addr; addr = addr->ai_next) {
const unsigned char *ip =
addr->ai_family == AF_INET
? (const unsigned char *)&((struct sockaddr_in *)addr->ai_addr)
->sin_addr
: (const unsigned char *)"\0\0\0\0";
printf("%s = %s\n", "ai_flags",
recreateflags(kAddrInfoFlagNames, addr->ai_flags), addr->ai_flags);
printf("%s = %s (%d)\n", "ai_family",
findnamebyid(kAddressFamilyNames, addr->ai_family),
addr->ai_family);
printf("%s = %s (%d)\n", "ai_socktype",
findnamebyid(kSockTypeNames, addr->ai_socktype),
addr->ai_socktype);
printf("%s = %s (%d)\n", "ai_protocol",
findnamebyid(kProtocolNames, addr->ai_protocol),
addr->ai_protocol);
printf("%s = %d\n", "ai_addrlen", addr->ai_addrlen);
printf("%s = %hhu.%hhu.%hhu.%hhu\n", "ai_addr", ip[0], ip[1], ip[2],
ip[3]);
printf("%s = %s\n", "ai_canonname", addr->ai_canonname);
}
freeaddrinfo(addrs);
} else {
fprintf(stderr, "%s: %s\n", name, "no results");
}
}
int main(int argc, char *argv[]) {
for (int i = 1; i < argc; ++i) {
lookup(argv[i]);
}
return 0;
}

300
tool/net/echoserver.c Normal file
View file

@ -0,0 +1,300 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/alg/arraylist.h"
#include "libc/bits/safemacros.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/iovec.h"
#include "libc/errno.h"
#include "libc/fmt/fmt.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/runtime/gc.h"
#include "libc/runtime/interruptiblecall.h"
#include "libc/runtime/runtime.h"
#include "libc/sock/sock.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/exit.h"
#include "libc/sysv/consts/ipproto.h"
#include "libc/sysv/consts/msg.h"
#include "libc/sysv/consts/poll.h"
#include "libc/sysv/consts/so.h"
#include "libc/sysv/consts/sock.h"
#include "libc/sysv/consts/sol.h"
#include "libc/x/x.h"
#include "third_party/getopt/getopt.h"
/**
* @fileoverview Asynchronous TCP/UDP Echo Server.
*
* make -j8 o/default/tool/net/echoserver.com
* o/default/tool/net/echoserver.com udp:0.0.0.0:7 tcp:0.0.0.0:7
*/
enum SocketKind {
kSocketServer,
kSocketClient,
};
struct Message {
struct iovec data;
struct sockaddr_in dest;
uint32_t destsize;
};
struct Messages {
size_t i, n;
struct Message *p;
};
struct Socket {
int64_t fd;
enum SocketKind kind;
int type;
int protocol;
struct sockaddr_in addr;
struct Messages egress; /* LIFO */
};
struct Sockets {
size_t i, n;
struct Socket *p;
};
struct Polls {
size_t i, n;
struct pollfd *p;
};
struct Sockets g_sockets;
struct Polls g_polls;
nodiscard char *DescribeAddress(struct sockaddr_in *addr) {
char ip4buf[16];
return xasprintf("%s:%hu",
inet_ntop(addr->sin_family, &addr->sin_addr.s_addr, ip4buf,
sizeof(ip4buf)),
ntohs(addr->sin_port));
}
nodiscard char *DescribeSocket(struct Socket *s) {
return xasprintf("%s:%s", s->protocol == IPPROTO_UDP ? "udp" : "tcp",
gc(DescribeAddress(&s->addr)));
}
noreturn void ShowUsageAndExit(bool iserror) {
FILE *f = iserror ? stderr : stdout;
int rc = iserror ? EXIT_FAILURE : EXIT_SUCCESS;
fprintf(f, "%s: %s %s\n", "Usage", g_argv[0], "PROTOCOL:ADDR:PORT...");
exit(rc);
}
void GetFlags(int argc, char *argv[]) {
int opt;
while ((opt = getopt(argc, argv, "h")) != -1) {
switch (opt) {
case 'h':
ShowUsageAndExit(false);
default:
ShowUsageAndExit(true);
}
}
if (optind == argc) ShowUsageAndExit(true);
}
void AddSocket(const struct Socket *s) {
struct pollfd pfd;
pfd.fd = s->fd;
pfd.events = POLLIN;
pfd.revents = 0;
CHECK_NE(-1L, append(&g_sockets, s));
CHECK_NE(-1L, append(&g_polls, &pfd));
}
void RemoveSocket(size_t i) {
DCHECK_LT(i, g_sockets.i);
LOGF("removing: %s", gc(DescribeSocket(&g_sockets.p[i])));
CHECK_NE(-1, close(g_sockets.p[i].fd));
while (g_sockets.p[i].egress.i) {
free(g_sockets.p[i].egress.p[g_sockets.p[i].egress.i - 1].data.iov_base);
}
memcpy(&g_sockets.p[i], &g_sockets.p[i + 1],
(intptr_t)&g_sockets.p[g_sockets.i] - (intptr_t)&g_sockets.p[i + 1]);
memcpy(&g_polls.p[i], &g_polls.p[i + 1],
(intptr_t)&g_polls.p[g_polls.i] - (intptr_t)&g_polls.p[i + 1]);
g_sockets.i--;
g_polls.i--;
}
void GetListeningAddressesFromCommandLine(int argc, char *argv[]) {
int i;
for (i = optind; i < argc; ++i) {
struct Socket server;
memset(&server, 0, sizeof(server));
char scheme[4];
unsigned char *ip4 = (unsigned char *)&server.addr.sin_addr.s_addr;
uint16_t port;
if (sscanf(argv[i], "%3s:%hhu.%hhu.%hhu.%hhu:%hu", scheme, &ip4[0], &ip4[1],
&ip4[2], &ip4[3], &port) != 6) {
fprintf(stderr, "error: bad ip4 uri\n");
ShowUsageAndExit(true);
}
server.fd = -1;
server.kind = kSocketServer;
server.addr.sin_family = AF_INET;
server.addr.sin_port = htons(port);
if (strcasecmp(scheme, "tcp") == 0) {
server.type = SOCK_STREAM;
server.protocol = IPPROTO_TCP;
} else if (strcasecmp(scheme, "udp") == 0) {
server.type = SOCK_DGRAM;
server.protocol = IPPROTO_UDP;
} else {
fprintf(stderr, "%s: %s\n", "error", "bad scheme (should be tcp or udp)");
ShowUsageAndExit(true);
}
AddSocket(&server);
}
}
void BeginListeningForIncomingTraffic(void) {
size_t i;
for (i = 0; i < g_sockets.i; ++i) {
int yes = 1;
struct Socket *s = &g_sockets.p[i];
CHECK_NE(-1L,
(g_polls.p[i].fd = s->fd = socket(
s->addr.sin_family, s->type | SOCK_NONBLOCK, s->protocol)));
CHECK_NE(-1L,
setsockopt(s->fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)));
CHECK_NE(-1, bind(s->fd, &s->addr, sizeof(s->addr)));
if (s->protocol == IPPROTO_TCP) {
CHECK_NE(-1, listen(s->fd, 1));
}
uint32_t addrsize = sizeof(s->addr);
CHECK_NE(-1, getsockname(s->fd, &s->addr, &addrsize));
LOGF("listening on %s", gc(DescribeSocket(s)));
}
}
void AcceptConnection(size_t i) {
struct Socket *server = &g_sockets.p[i];
struct Socket client;
memset(&client, 0, sizeof(client));
client.kind = kSocketClient;
client.type = server->type;
client.protocol = server->protocol;
uint32_t addrsize = sizeof(client.addr);
CHECK_NE(-1L, (client.fd = accept4(server->fd, &client.addr, &addrsize,
SOCK_NONBLOCK)));
LOGF("%s accepted %s", gc(DescribeSocket(server)),
gc(DescribeSocket(&client)));
AddSocket(&client);
}
bool ReceiveData(size_t i) {
ssize_t got;
struct Message msg;
bool isudp = g_sockets.p[i].protocol == IPPROTO_UDP;
memset(&msg, 0, sizeof(msg));
msg.destsize = sizeof(msg.dest);
msg.data.iov_len = PAGESIZE;
msg.data.iov_base = xmalloc(msg.data.iov_len);
CHECK_NE(-1L, (got = recvfrom(g_sockets.p[i].fd, msg.data.iov_base,
msg.data.iov_len, 0, isudp ? &msg.dest : NULL,
isudp ? &msg.destsize : NULL)));
if (0 < got && got <= msg.data.iov_len) {
LOGF("%s received %lu bytes from %s", gc(DescribeSocket(&g_sockets.p[i])),
got, gc(DescribeAddress(&msg.dest)));
msg.data.iov_base = xrealloc(msg.data.iov_base, (msg.data.iov_len = got));
append(&g_sockets.p[i].egress, &msg);
g_polls.p[i].events |= POLLOUT;
return true;
} else {
RemoveSocket(i);
free_s(&msg.data.iov_base);
return false;
}
}
void SendData(size_t i) {
ssize_t sent;
struct Socket *s = &g_sockets.p[i];
struct Message *msg = &s->egress.p[s->egress.i - 1];
bool isudp = s->protocol == IPPROTO_UDP;
DCHECK(s->egress.i);
CHECK_NE(-1L, (sent = sendto(s->fd, msg->data.iov_base, msg->data.iov_len, 0,
isudp ? &msg->dest : NULL,
isudp ? msg->destsize : 0)));
LOGF("%s sent %lu bytes to %s", gc(DescribeSocket(s)), msg->data.iov_len,
gc(DescribeAddress(&msg->dest)));
if (!(msg->data.iov_len -= min((size_t)sent, (size_t)msg->data.iov_len))) {
free_s(&msg->data.iov_base);
if (!--s->egress.i) {
g_polls.p[i].events &= ~POLLOUT;
}
}
}
void HandleSomeNetworkTraffic(void) {
size_t i;
int eventcount;
CHECK_GE((eventcount = poll(g_polls.p, g_polls.i, -1)), 0);
for (i = 0; eventcount && i < g_sockets.i; ++i) {
if (!g_polls.p[i].revents) continue;
--eventcount;
if (g_polls.p[i].revents & (POLLERR | POLLHUP | POLLNVAL)) {
CHECK_EQ(kSocketClient, g_sockets.p[i].kind);
RemoveSocket(i);
} else {
if (g_polls.p[i].revents & POLLIN) {
if (g_sockets.p[i].kind == kSocketServer &&
g_sockets.p[i].protocol == IPPROTO_TCP) {
AcceptConnection(i);
} else {
if (!ReceiveData(i)) continue;
}
}
if (g_polls.p[i].revents & POLLOUT) {
SendData(i);
}
}
}
}
void EchoServer(void) {
for (;;) HandleSomeNetworkTraffic();
}
int main(int argc, char *argv[]) {
STATIC_YOINK("isatty");
GetFlags(argc, argv);
GetListeningAddressesFromCommandLine(argc, argv);
BeginListeningForIncomingTraffic();
struct InterruptibleCall icall;
memset(&icall, 0, sizeof(icall));
interruptiblecall(&icall, (void *)EchoServer, 0, 0, 0, 0);
fputc('\r', stderr);
LOGF("%s", "shutting down...");
size_t i;
for (i = g_sockets.i; i; --i) RemoveSocket(i - 1);
return 0;
}

56
tool/net/net.mk Normal file
View file

@ -0,0 +1,56 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
PKGS += TOOL_NET
TOOL_NET_FILES := $(wildcard tool/net/*)
TOOL_NET_SRCS = $(filter %.c,$(TOOL_NET_FILES))
TOOL_NET_HDRS = $(filter %.h,$(TOOL_NET_FILES))
TOOL_NET_COMS = $(TOOL_NET_OBJS:%.o=%.com)
TOOL_NET_OBJS = \
$(TOOL_NET_SRCS:%=o/$(MODE)/%.zip.o) \
$(TOOL_NET_SRCS:%.c=o/$(MODE)/%.o)
TOOL_NET_BINS = \
$(TOOL_NET_COMS) \
$(TOOL_NET_COMS:%=%.dbg)
TOOL_NET_DIRECTDEPS = \
LIBC_CALLS \
LIBC_DNS \
LIBC_FMT \
LIBC_LOG \
LIBC_NEXGEN32E \
LIBC_RUNTIME \
LIBC_SOCK \
LIBC_STDIO \
LIBC_STR \
LIBC_STUBS \
LIBC_SYSV \
LIBC_UNICODE \
LIBC_X \
THIRD_PARTY_GETOPT \
TOOL_DECODE_LIB
TOOL_NET_DEPS := \
$(call uniq,$(foreach x,$(TOOL_NET_DIRECTDEPS),$($(x))))
o/$(MODE)/tool/net/net.pkg: \
$(TOOL_NET_OBJS) \
$(foreach x,$(TOOL_NET_DIRECTDEPS),$($(x)_A).pkg)
o/$(MODE)/tool/net/%.com.dbg: \
$(TOOL_NET_DEPS) \
o/$(MODE)/tool/net/%.o \
o/$(MODE)/tool/net/net.pkg \
$(CRT) \
$(APE)
@$(APELINK)
$(TOOL_NET_OBJS): \
$(BUILD_FILES) \
tool/net/net.mk
.PHONY: o/$(MODE)/tool/net
o/$(MODE)/tool/net: $(TOOL_NET_BINS) $(TOOL_NET_CHECKS)