mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-24 03:20:30 +00:00
Add finger demo to redbean and fix regression
This change fixes a regression in unix.connect() caused by the recent addition of UNIX domain sockets. The BSD finger command has been added to third_party for fun and profit. A new demo has been added to redbean showing how a protocol as simple as finger can be implemented.
This commit is contained in:
parent
2415afab0e
commit
17cbe73411
34 changed files with 2454 additions and 29 deletions
70
tool/net/demo/unix-finger.lua
Normal file
70
tool/net/demo/unix-finger.lua
Normal file
|
@ -0,0 +1,70 @@
|
|||
-- UNIX Finger Example
|
||||
|
||||
local function WriteForm(host, user)
|
||||
Write([[<!doctype html>
|
||||
<title>redbean unix finger demo</title>
|
||||
<style>
|
||||
body { padding: 1em; }
|
||||
h1 a { color: inherit; text-decoration: none; }
|
||||
h1 img { border: none; vertical-align: middle; }
|
||||
input { margin: .5em; padding: .25em; }
|
||||
pre { margin-left: 2em; }
|
||||
p { word-break: break-word; max-width: 650px; }
|
||||
dt { font-weight: bold; }
|
||||
dd { margin-top: 1em; margin-bottom: 1em; }
|
||||
.hdr { text-indent: -1em; padding-left: 1em; }
|
||||
</style>
|
||||
<h1>
|
||||
<a href="/"><img src="/redbean.png"></a>
|
||||
<a href="unix-finger.lua">redbean unix finger demo</a>
|
||||
</h1>
|
||||
<p>
|
||||
Your redbean is able to function as an finger client. Lua server
|
||||
pages can use the <code>unix</code> module to implement protocols
|
||||
that your redbean wasn't originally intended to support. All it
|
||||
takes is few lines of code!
|
||||
</p>
|
||||
<form action="unix-finger.lua" method="post">
|
||||
<input type="text" id="host" name="host" size="40"
|
||||
value="%s" placeholder="host" autofocus>
|
||||
<label for="host">host</label>
|
||||
<br>
|
||||
<input type="text" id="user" name="user" size="40"
|
||||
value="%s" placeholder="user">
|
||||
<label for="user">user</label>
|
||||
<br>
|
||||
<input type="submit" value="finger">
|
||||
</form>
|
||||
]] % {EscapeHtml(host), EscapeHtml(user)})
|
||||
end
|
||||
|
||||
local function main()
|
||||
if IsPublicIp(GetClientAddr()) then
|
||||
ServeError(403)
|
||||
elseif GetMethod() == 'GET' or GetMethod() == 'HEAD' then
|
||||
WriteForm('graph.no', 'new_york')
|
||||
elseif GetMethod() == 'POST' then
|
||||
ip = assert(ResolveIp(GetParam('host')))
|
||||
fd = assert(unix.socket())
|
||||
assert(unix.connect(fd, ip, 79))
|
||||
assert(unix.write(fd, GetParam('user') .. '\r\n'))
|
||||
response = ''
|
||||
while true do
|
||||
data = assert(unix.read(fd))
|
||||
if data == '' then
|
||||
break
|
||||
end
|
||||
response = response .. data
|
||||
end
|
||||
assert(unix.close(fd))
|
||||
WriteForm(GetParam('host'), GetParam('user'))
|
||||
Write('<pre>\r\n')
|
||||
Write(EscapeHtml(VisualizeControlCodes(response)))
|
||||
Write('</pre>\r\n')
|
||||
else
|
||||
ServeError(405)
|
||||
SetHeader('Allow', 'GET, HEAD, POST')
|
||||
end
|
||||
end
|
||||
|
||||
main()
|
|
@ -1449,6 +1449,28 @@ FUNCTIONS
|
|||
value will be the `"0b"`-prefixed binary str. The result is
|
||||
currently modulo 2^64. Negative numbers are converted to unsigned.
|
||||
|
||||
ResolveIp(hostname:str)
|
||||
├─→ ip:uint32
|
||||
└─→ nil, error:str
|
||||
|
||||
Gets IP address associated with hostname.
|
||||
|
||||
This function first checks if hostname is already an IP address, in
|
||||
which case it returns the result of `ParseIp`. Otherwise, it checks
|
||||
HOSTS.TXT on the local system and returns the first IPv4 address
|
||||
associated with hostname. If no such entry is found, a DNS lookup is
|
||||
performed using the system configured (e.g. /etc/resolv.conf) DNS
|
||||
resolution service. If the service returns multiple IN A records
|
||||
then only the first one is reutrned.
|
||||
|
||||
The returned address is word-encoded in host endian order. For
|
||||
example, 1.2.3.4 is encoded as 0x01020304. The `FormatIp` function
|
||||
may be used to turn this value back into a string.
|
||||
|
||||
If no IP address could be found, then nil is returned alongside a
|
||||
string of unspecified format describing the error. Calls to this
|
||||
function may be wrapped in assert() if an exception is desired.
|
||||
|
||||
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "libc/bits/popcnt.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/rusage.h"
|
||||
#include "libc/dns/dns.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/fmt/leb128.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
|
@ -41,7 +42,9 @@
|
|||
#include "libc/runtime/sysconf.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/sysv/consts/af.h"
|
||||
#include "libc/sysv/consts/ipproto.h"
|
||||
#include "libc/sysv/consts/rusage.h"
|
||||
#include "libc/sysv/consts/sock.h"
|
||||
#include "libc/time/time.h"
|
||||
#include "libc/x/x.h"
|
||||
#include "net/http/escape.h"
|
||||
|
@ -379,6 +382,27 @@ int LuaSlurp(lua_State *L) {
|
|||
}
|
||||
}
|
||||
|
||||
int LuaResolveIp(lua_State *L) {
|
||||
ssize_t rc;
|
||||
int64_t ip;
|
||||
const char *host;
|
||||
struct addrinfo *ai = NULL;
|
||||
struct addrinfo hint = {AI_NUMERICSERV, AF_INET, SOCK_STREAM, IPPROTO_TCP};
|
||||
host = luaL_checkstring(L, 1);
|
||||
if ((ip = ParseIp(host, -1)) != -1) {
|
||||
lua_pushinteger(L, ntohl(ai->ai_addr4->sin_addr.s_addr));
|
||||
return 1;
|
||||
} else if ((rc = getaddrinfo(host, "0", &hint, &ai)) == EAI_SUCCESS) {
|
||||
lua_pushinteger(L, ntohl(ai->ai_addr4->sin_addr.s_addr));
|
||||
freeaddrinfo(ai);
|
||||
return 1;
|
||||
} else {
|
||||
lua_pushnil(L);
|
||||
lua_pushfstring(L, "%s: DNS lookup failed: EAI_%s", host, gai_strerror(rc));
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
static int LuaCheckControlFlags(lua_State *L, int idx) {
|
||||
int f = luaL_checkinteger(L, idx);
|
||||
if (f & ~(kControlWs | kControlC0 | kControlC1)) {
|
||||
|
|
|
@ -70,6 +70,7 @@ int LuaRand64(lua_State *);
|
|||
int LuaRdrand(lua_State *);
|
||||
int LuaRdseed(lua_State *);
|
||||
int LuaRdtsc(lua_State *);
|
||||
int LuaResolveIp(lua_State *);
|
||||
int LuaSetLogLevel(lua_State *);
|
||||
int LuaSha1(lua_State *);
|
||||
int LuaSha224(lua_State *);
|
||||
|
|
|
@ -173,6 +173,7 @@ o/$(MODE)/tool/net/demo/unix-subprocess.lua.zip.o \
|
|||
o/$(MODE)/tool/net/demo/unix-webserver.lua.zip.o \
|
||||
o/$(MODE)/tool/net/demo/unix-dir.lua.zip.o \
|
||||
o/$(MODE)/tool/net/demo/unix-info.lua.zip.o \
|
||||
o/$(MODE)/tool/net/demo/unix-finger.lua.zip.o \
|
||||
o/$(MODE)/tool/net/demo/fetch.lua.zip.o \
|
||||
o/$(MODE)/tool/net/demo/call-lua-module.lua.zip.o \
|
||||
o/$(MODE)/tool/net/demo/store-asset.lua.zip.o \
|
||||
|
@ -222,6 +223,7 @@ o/$(MODE)/tool/net/redbean-demo.com.dbg: \
|
|||
o/$(MODE)/tool/net/demo/unix-webserver.lua.zip.o \
|
||||
o/$(MODE)/tool/net/demo/unix-dir.lua.zip.o \
|
||||
o/$(MODE)/tool/net/demo/unix-info.lua.zip.o \
|
||||
o/$(MODE)/tool/net/demo/unix-finger.lua.zip.o \
|
||||
o/$(MODE)/tool/net/demo/fetch.lua.zip.o \
|
||||
o/$(MODE)/tool/net/demo/store-asset.lua.zip.o \
|
||||
o/$(MODE)/tool/net/demo/call-lua-module.lua.zip.o \
|
||||
|
|
|
@ -5131,6 +5131,7 @@ static const luaL_Reg kLuaFuncs[] = {
|
|||
{"Rdrand", LuaRdrand}, //
|
||||
{"Rdseed", LuaRdseed}, //
|
||||
{"Rdtsc", LuaRdtsc}, //
|
||||
{"ResolveIp", LuaResolveIp}, //
|
||||
{"Route", LuaRoute}, //
|
||||
{"RouteHost", LuaRouteHost}, //
|
||||
{"RoutePath", LuaRoutePath}, //
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue