mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-03 09:48:29 +00:00
Add tcp syn packet fingerprinting to redbean
This change also fixes bugs in enoprotoopt reporting with setsockopt and getsockopt error returns.
This commit is contained in:
parent
866b21a151
commit
4d25f8c3c9
75 changed files with 1551 additions and 115 deletions
|
@ -20,8 +20,20 @@ db:exec[[
|
|||
INSERT INTO test (content) VALUES ('Hello Sqlite3');
|
||||
]]
|
||||
|
||||
function OnServerListen(fd, ip, port)
|
||||
unix.setsockopt(fd, unix.SOL_TCP, unix.TCP_SAVE_SYN, true)
|
||||
return false
|
||||
end
|
||||
|
||||
function OnClientConnection(ip, port, serverip, serverport)
|
||||
syn, synerr = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_SAVED_SYN)
|
||||
end
|
||||
|
||||
-- this intercepts all requests if it's defined
|
||||
function OnHttpRequest()
|
||||
Log(kLogInfo, "client is running %s and reports %s" % {
|
||||
finger.GetSynFingerOs(finger.FingerSyn(syn)),
|
||||
GetHeader('User-Agent')})
|
||||
if HasParam('magic') then
|
||||
Write('<p>\r\n')
|
||||
Write('OnHttpRequest() has intercepted your request<br>\r\n')
|
||||
|
|
65
tool/net/demo/finger.lua
Normal file
65
tool/net/demo/finger.lua
Normal file
|
@ -0,0 +1,65 @@
|
|||
-- fingerprinting example
|
||||
|
||||
Write[[<!doctype html>
|
||||
|
||||
<title>redbean binary trees</title>
|
||||
|
||||
<style>
|
||||
body { padding: 1em; }
|
||||
h1 a { color: inherit; text-decoration: none; }
|
||||
h1 img { border: none; vertical-align: middle; }
|
||||
input { margin: 1em; padding: .5em; }
|
||||
p { word-break: break-word; max-width: 650px; }
|
||||
dt { font-family: monospace; }
|
||||
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="finger.lua">redbean finger demo</a>
|
||||
</h1>
|
||||
|
||||
]]
|
||||
|
||||
Write[[
|
||||
<h2>Your TCP SYN Packet</h2>
|
||||
]]
|
||||
-- See .init.lua hooks which set SYN and SYNERR globals.
|
||||
if syn then
|
||||
if syn ~= '' then
|
||||
Write('<dl>\r\n')
|
||||
Write('<dt>syn = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_SAVED_SYN)\r\n')
|
||||
Write('<dd>')
|
||||
Write(EscapeHtml(EncodeLua(syn)))
|
||||
Write('\r\n')
|
||||
Write('<dt>finger.FingerSyn(syn)\r\n')
|
||||
Write('<dd>')
|
||||
Write(EscapeHtml(EncodeLua(finger.FingerSyn(syn))))
|
||||
Write('\r\n')
|
||||
Write('<dt>finger.DescribeSyn(syn)\r\n')
|
||||
Write('<dd>')
|
||||
Write(EscapeHtml(EncodeLua(finger.DescribeSyn(syn))))
|
||||
Write('\r\n')
|
||||
Write('<dt>finger.GetSynFingerOs(finger.FingerSyn(syn))\r\n')
|
||||
Write('<dd>')
|
||||
Write(EscapeHtml(EncodeLua(finger.GetSynFingerOs(finger.FingerSyn(syn)))))
|
||||
Write('\r\n')
|
||||
Write('</dl>\r\n')
|
||||
else
|
||||
Write([[
|
||||
<p>
|
||||
your operating system returned an empty string as the syn
|
||||
packet! did you remember to use setsockopt(TCP_SAVE_SYN)?
|
||||
did you call getsockopt(TCP_SAVED_SYN) more than once?
|
||||
</p>
|
||||
]])
|
||||
end
|
||||
else
|
||||
Write([[
|
||||
<p>
|
||||
your operating system most likely doesn't support TCP_SAVED_SYN
|
||||
because getsockopt() returned %s.
|
||||
</p>
|
||||
]] % {EscapeHtml(tostring(synerr))})
|
||||
end
|
|
@ -1974,6 +1974,100 @@ MAXMIND MODULE
|
|||
For further details, please see maxmind.lua in redbean-demo.com.
|
||||
|
||||
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
FINGER MODULE
|
||||
|
||||
This is an experimental module that, like the maxmind module, gives
|
||||
you insight into what kind of device is connecting to your redbean.
|
||||
This module can help you protect your redbean because it provides
|
||||
tools for identifying clients that misrepresent themselves. For
|
||||
example the User-Agent header might report itself as a Windows
|
||||
computer when the SYN packet says it's a Linux computer.
|
||||
|
||||
function OnServerListen(fd, ip, port)
|
||||
unix.setsockopt(fd, unix.SOL_TCP, unix.TCP_SAVE_SYN, true)
|
||||
return false
|
||||
end
|
||||
|
||||
function OnClientConnection(ip, port, serverip, serverport)
|
||||
fd = GetClientFd()
|
||||
syn = unix.getsockopt(fd, unix.SOL_TCP, unix.TCP_SAVED_SYN)
|
||||
end
|
||||
|
||||
function OnHttpRequest()
|
||||
Log(kLogInfo, "client is running %s and reports %s" % {
|
||||
finger.GetSynFingerOs(finger.FingerSyn(syn)),
|
||||
GetHeader('User-Agent')})
|
||||
Route()
|
||||
end
|
||||
|
||||
The following functions are provided.
|
||||
|
||||
finger.FingerSyn(syn_packet_bytes:str)
|
||||
├─→ synfinger:uint32
|
||||
└─→ nil, error:str
|
||||
|
||||
Fingerprints IP+TCP SYN packet.
|
||||
|
||||
This returns a hash-like magic number that reflects the SYN packet
|
||||
structure, e.g. ordering of options, maximum segment size, etc. We
|
||||
make no guarantees this hashing algorithm won't change as we learn
|
||||
more about the optimal way to fingerprint, so be sure to save your
|
||||
syn packets too if you're using this feature, in case they need to
|
||||
be rehashed in the future.
|
||||
|
||||
This function is nil/error propagating.
|
||||
|
||||
finger.GetSynFingerOs(synfinger:uint32)
|
||||
├─→ osname:str
|
||||
└─→ nil, error:str
|
||||
|
||||
Fingerprints IP+TCP SYN packet.
|
||||
|
||||
If `synfinger` is a known hard-coded magic number, then one of the
|
||||
following strings may be returned:
|
||||
|
||||
- `"LINUX"`
|
||||
- `"WINDOWS"`
|
||||
- `"XNU"`
|
||||
- `"NETBSD"`
|
||||
- `"FREEBSD"`
|
||||
- `"OPENBSD"`
|
||||
|
||||
If this function returns nil, then one thing you can do to help is
|
||||
file an issue and share with us your SYN packet specimens. The way
|
||||
we prefer to receive them is in EncodeLua(syn_packet_bytes) format
|
||||
along with details on the operating system which you must know.
|
||||
|
||||
finger.DescribeSyn(syn_packet_bytes:str)
|
||||
├─→ description:str
|
||||
└─→ nil, error:str
|
||||
|
||||
Describes IP+TCP SYN packet.
|
||||
|
||||
The layout looks as follows:
|
||||
|
||||
TTL:OPTIONS:WSIZE:MSS
|
||||
|
||||
The `TTL`, `WSIZE`, and `MSS` fields are unsigned decimal fields.
|
||||
|
||||
The `OPTIONS` field communicates the ordering of the commonly used
|
||||
subset of tcp options. The following character mappings are defined.
|
||||
TCP options not on this list will be ignored.
|
||||
|
||||
- E: End of Option list
|
||||
- N: No-Operation
|
||||
- M: Maxmimum Segment Size
|
||||
- K: Window Scale
|
||||
- O: SACK Permitted
|
||||
- A: SACK
|
||||
- e: Echo (obsolete)
|
||||
- r: Echo reply (obsolete)
|
||||
- T: Timestamps
|
||||
|
||||
This function is nil/error propagating.
|
||||
|
||||
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
ARGON2 MODULE
|
||||
|
||||
|
@ -3071,6 +3165,17 @@ UNIX MODULE
|
|||
`level` and `optname` may be one of the following pairs. The ellipses
|
||||
type signature above changes depending on which options are used.
|
||||
|
||||
`optname` is the option feature magic number. The constants for
|
||||
these will be set to `0` if the option isn't supported on the host
|
||||
platform.
|
||||
|
||||
Raises `ENOPROTOOPT` if your `level` / `optname` combination isn't
|
||||
valid, recognized, or supported on the host platform.
|
||||
|
||||
Raises `ENOTSOCK` if `fd` is valid but isn't a socket.
|
||||
|
||||
Raises `EBADF` if `fd` isn't valid.
|
||||
|
||||
unix.getsockopt(fd:int, level:int, optname:int)
|
||||
├─→ value:int
|
||||
└─→ nil, unix.Errno
|
||||
|
@ -3145,9 +3250,19 @@ UNIX MODULE
|
|||
close(). Sometimes it's desirable to have extra assurance on errors
|
||||
happened, even if it comes at the cost of performance.
|
||||
|
||||
Returns `EINVAL` if settings other than the above are used.
|
||||
unix.setsockopt(serverfd:int, unix.SOL_TCP, unix.TCP_SAVE_SYN, enabled:int)
|
||||
├─→ true
|
||||
└─→ nil, unix.Errno
|
||||
unix.getsockopt(clientfd:int, unix.SOL_TCP, unix.TCP_SAVED_SYN)
|
||||
├─→ syn_packet_bytes:str
|
||||
└─→ nil, unix.Errno
|
||||
|
||||
Returns `ENOSYS` if setting isn't supported by the host OS.
|
||||
This `TCP_SAVED_SYN` option may be used to retrieve the bytes of the
|
||||
TCP SYN packet that the client sent when the connection for `fd` was
|
||||
opened. In order for this to work, `TCP_SAVE_SYN` must have been set
|
||||
earlier on the listening socket. This is Linux-only. You can use the
|
||||
`OnServerListen` hook to enable SYN saving in your Redbean. When the
|
||||
`TCP_SAVE_SYN` option isn't used, this may return empty string.
|
||||
|
||||
unix.poll({[fd:int]=events:int, ...}[, timeoutms:int])
|
||||
├─→ {[fd:int]=revents:int, ...}
|
||||
|
|
98
tool/net/lfinger.c
Normal file
98
tool/net/lfinger.c
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*-*- 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 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "net/finger/finger.h"
|
||||
#include "third_party/lua/lauxlib.h"
|
||||
|
||||
// finger.DescribeSyn(syn_packet_bytes:str)
|
||||
// ├─→ description:str
|
||||
// └─→ nil, error:str
|
||||
static int LuaDescribeSyn(lua_State *L) {
|
||||
char *q, *r;
|
||||
size_t n, m;
|
||||
const char *p;
|
||||
luaL_Buffer b;
|
||||
if (!lua_isnoneornil(L, 1)) {
|
||||
p = luaL_checklstring(L, 1, &n);
|
||||
m = 128;
|
||||
q = luaL_buffinitsize(L, &b, m);
|
||||
if ((r = DescribeSyn(q, m, p, n))) {
|
||||
luaL_pushresultsize(&b, r - q);
|
||||
return 1;
|
||||
} else {
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, "bad syn packet");
|
||||
return 2;
|
||||
}
|
||||
} else {
|
||||
return lua_gettop(L);
|
||||
}
|
||||
}
|
||||
|
||||
// finger.FingerSyn(syn_packet_bytes:str)
|
||||
// ├─→ synfinger:uint32
|
||||
// └─→ nil, error:str
|
||||
static int LuaFingerSyn(lua_State *L) {
|
||||
size_t n;
|
||||
uint32_t x;
|
||||
const char *p;
|
||||
if (!lua_isnoneornil(L, 1)) {
|
||||
p = luaL_checklstring(L, 1, &n);
|
||||
if ((x = FingerSyn(p, n))) {
|
||||
lua_pushinteger(L, x);
|
||||
return 1;
|
||||
} else {
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, "bad syn packet");
|
||||
return 2;
|
||||
}
|
||||
} else {
|
||||
return lua_gettop(L);
|
||||
}
|
||||
}
|
||||
|
||||
// finger.GetSynFingerOs(synfinger:uint32)
|
||||
// ├─→ osname:str
|
||||
// └─→ nil, error:str
|
||||
static int LuaGetSynFingerOs(lua_State *L) {
|
||||
int os;
|
||||
if (!lua_isnoneornil(L, 1)) {
|
||||
if ((os = GetSynFingerOs(luaL_checkinteger(L, 1)))) {
|
||||
lua_pushstring(L, GetOsName(os));
|
||||
return 1;
|
||||
} else {
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, "unknown syn os fingerprint");
|
||||
return 2;
|
||||
}
|
||||
} else {
|
||||
return lua_gettop(L);
|
||||
}
|
||||
}
|
||||
|
||||
static const luaL_Reg kLuaFinger[] = {
|
||||
{"DescribeSyn", LuaDescribeSyn}, //
|
||||
{"FingerSyn", LuaFingerSyn}, //
|
||||
{"GetSynFingerOs", LuaGetSynFingerOs}, //
|
||||
{0}, //
|
||||
};
|
||||
|
||||
int LuaFinger(lua_State *L) {
|
||||
luaL_newlib(L, kLuaFinger);
|
||||
return 1;
|
||||
}
|
11
tool/net/lfinger.h
Normal file
11
tool/net/lfinger.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#ifndef COSMOPOLITAN_TOOL_NET_LFINGER_H_
|
||||
#define COSMOPOLITAN_TOOL_NET_LFINGER_H_
|
||||
#include "third_party/lua/lauxlib.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
int LuaFinger(lua_State *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_TOOL_NET_LFINGER_H_ */
|
|
@ -54,6 +54,7 @@ TOOL_NET_DIRECTDEPS = \
|
|||
LIBC_UNICODE \
|
||||
LIBC_X \
|
||||
LIBC_ZIPOS \
|
||||
NET_FINGER \
|
||||
NET_HTTP \
|
||||
NET_HTTPS \
|
||||
THIRD_PARTY_ARGON2 \
|
||||
|
@ -95,6 +96,7 @@ o/$(MODE)/tool/net/redbean.com.dbg: \
|
|||
$(TOOL_NET_DEPS) \
|
||||
o/$(MODE)/tool/net/redbean.o \
|
||||
o/$(MODE)/tool/net/lfuncs.o \
|
||||
o/$(MODE)/tool/net/lfinger.o \
|
||||
o/$(MODE)/tool/net/lre.o \
|
||||
o/$(MODE)/tool/net/ljson.o \
|
||||
o/$(MODE)/tool/net/lmaxmind.o \
|
||||
|
@ -178,6 +180,7 @@ 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/finger.lua.zip.o \
|
||||
o/$(MODE)/tool/net/demo/call-lua-module.lua.zip.o \
|
||||
o/$(MODE)/tool/net/demo/store-asset.lua.zip.o \
|
||||
o/$(MODE)/tool/net/demo/maxmind.lua.zip.o \
|
||||
|
@ -214,6 +217,7 @@ o/$(MODE)/tool/net/redbean-demo.com.dbg: \
|
|||
$(TOOL_NET_DEPS) \
|
||||
o/$(MODE)/tool/net/redbean.o \
|
||||
o/$(MODE)/tool/net/lfuncs.o \
|
||||
o/$(MODE)/tool/net/lfinger.o \
|
||||
o/$(MODE)/tool/net/lre.o \
|
||||
o/$(MODE)/tool/net/ljson.o \
|
||||
o/$(MODE)/tool/net/lmaxmind.o \
|
||||
|
@ -229,6 +233,7 @@ o/$(MODE)/tool/net/redbean-demo.com.dbg: \
|
|||
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/finger.lua.zip.o \
|
||||
o/$(MODE)/tool/net/demo/store-asset.lua.zip.o \
|
||||
o/$(MODE)/tool/net/demo/call-lua-module.lua.zip.o \
|
||||
o/$(MODE)/tool/net/demo/redbean.lua.zip.o \
|
||||
|
@ -331,6 +336,7 @@ o/$(MODE)/tool/net/redbean-unsecure.com.dbg: \
|
|||
$(TOOL_NET_DEPS) \
|
||||
o/$(MODE)/tool/net/redbean-unsecure.o \
|
||||
o/$(MODE)/tool/net/lfuncs.o \
|
||||
o/$(MODE)/tool/net/lfinger.o \
|
||||
o/$(MODE)/tool/net/lre.o \
|
||||
o/$(MODE)/tool/net/ljson.o \
|
||||
o/$(MODE)/tool/net/lmaxmind.o \
|
||||
|
|
|
@ -112,6 +112,7 @@
|
|||
#include "third_party/zlib/zlib.h"
|
||||
#include "tool/args/args.h"
|
||||
#include "tool/build/lib/case.h"
|
||||
#include "tool/net/lfinger.h"
|
||||
#include "tool/net/lfuncs.h"
|
||||
#include "tool/net/ljson.h"
|
||||
#include "tool/net/luacheck.h"
|
||||
|
@ -5253,6 +5254,7 @@ static const luaL_Reg kLuaLibs[] = {
|
|||
{"argon2", luaopen_argon2}, //
|
||||
{"lsqlite3", luaopen_lsqlite3}, //
|
||||
{"maxmind", LuaMaxmind}, //
|
||||
{"finger", LuaFinger}, //
|
||||
{"re", LuaRe}, //
|
||||
{"unix", LuaUnix}, //
|
||||
};
|
||||
|
|
137
tool/viz/maxmind.c
Normal file
137
tool/viz/maxmind.c
Normal file
|
@ -0,0 +1,137 @@
|
|||
/*-*- 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 2021 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/bits.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/gc.internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "net/http/http.h"
|
||||
#include "net/http/ip.h"
|
||||
#include "third_party/maxmind/maxminddb.h"
|
||||
|
||||
#define PATH(...) \
|
||||
(const char *const[]) { \
|
||||
__VA_ARGS__, 0 \
|
||||
}
|
||||
|
||||
MMDB_s *ipdb, *asdb;
|
||||
|
||||
int PrintIpInfo(const char *ipstr) {
|
||||
int error;
|
||||
bool uhoh;
|
||||
int64_t ip;
|
||||
uint16_t cc;
|
||||
MMDB_entry_data_s e, f;
|
||||
MMDB_lookup_result_s r;
|
||||
if ((ip = ParseIp(ipstr, -1)) == -1) {
|
||||
fprintf(stderr, "BAD IP: %s\n", ipstr);
|
||||
return -1;
|
||||
}
|
||||
r = MMDB_lookup(ipdb, ip, &error);
|
||||
CHECK_EQ(MMDB_SUCCESS, error);
|
||||
if (!r.found_entry) {
|
||||
fprintf(stderr, "NOT FOUND: %s\n", ipstr);
|
||||
return -1;
|
||||
}
|
||||
printf("%hhu.%hhu.%hhu.%hhu\n", ip >> 24, ip >> 16, ip >> 8, ip);
|
||||
if (!MMDB_aget_value(&r.entry, &e, PATH("location", "metro_code"))) {
|
||||
printf("%s: %s\n", "metro", GetMetroName(e.uint16));
|
||||
}
|
||||
if (!MMDB_aget_value(&r.entry, &e, PATH("postal", "code"))) {
|
||||
printf("%s: %.*s\n", "postal", e.data_size, e.utf8_string);
|
||||
}
|
||||
if (!MMDB_aget_value(&r.entry, &e, PATH("city", "names", "en"))) {
|
||||
printf("%s: %.*s\n", "city", e.data_size, e.utf8_string);
|
||||
}
|
||||
if (!MMDB_aget_value(&r.entry, &e, PATH("subdivisions", "0", "iso_code")) &&
|
||||
!MMDB_aget_value(&r.entry, &f,
|
||||
PATH("subdivisions", "0", "names", "en"))) {
|
||||
printf("%s: %.*s (%.*s)\n", "region", e.data_size, e.utf8_string,
|
||||
f.data_size, f.utf8_string);
|
||||
}
|
||||
if (!MMDB_aget_value(&r.entry, &e, PATH("country", "iso_code")) &&
|
||||
!MMDB_aget_value(&r.entry, &f, PATH("country", "names", "en"))) {
|
||||
printf("%s: %.*s (%.*s)\n", "country", e.data_size, e.utf8_string,
|
||||
f.data_size, f.utf8_string);
|
||||
cc = READ16LE(e.utf8_string);
|
||||
} else {
|
||||
cc = 0;
|
||||
}
|
||||
if (!MMDB_aget_value(&r.entry, &e, PATH("registered_country", "iso_code")) &&
|
||||
!MMDB_aget_value(&r.entry, &f,
|
||||
PATH("registered_country", "names", "en"))) {
|
||||
uhoh = cc && cc != READ16LE(e.utf8_string);
|
||||
printf("%s%s: %.*s (%.*s)%s\n", uhoh ? "\e[1m" : "", "registered",
|
||||
e.data_size, e.utf8_string, f.data_size, f.utf8_string,
|
||||
uhoh ? "\e[0m" : "");
|
||||
}
|
||||
if ((!MMDB_aget_value(&r.entry, &e,
|
||||
PATH("country", "is_in_european_union")) &&
|
||||
e.boolean) ||
|
||||
(!MMDB_aget_value(&r.entry, &e,
|
||||
PATH("registered_country", "is_in_european_union")) &&
|
||||
e.boolean)) {
|
||||
printf("%s: %s\n", "is_in_european_union", "true");
|
||||
}
|
||||
if (!MMDB_aget_value(&r.entry, &e, PATH("continent", "code")) &&
|
||||
!MMDB_aget_value(&r.entry, &f, PATH("continent", "names", "en"))) {
|
||||
printf("%s: %.*s (%.*s)\n", "continent", e.data_size, e.utf8_string,
|
||||
f.data_size, f.utf8_string);
|
||||
}
|
||||
if (!MMDB_aget_value(&r.entry, &e, PATH("location", "time_zone"))) {
|
||||
printf("%s: %.*s\n", "timezone", e.data_size, e.utf8_string);
|
||||
}
|
||||
if (!MMDB_aget_value(&r.entry, &e, PATH("location", "latitude")) &&
|
||||
!MMDB_aget_value(&r.entry, &f, PATH("location", "longitude"))) {
|
||||
printf("%s: %g %g\n", "location", e.double_value, f.double_value);
|
||||
}
|
||||
if (!MMDB_aget_value(&r.entry, &e, PATH("location", "accuracy_radius"))) {
|
||||
printf("%s: %dkm\n", "accuracy", e.uint16);
|
||||
}
|
||||
printf("%s: %s\n", "category", GetIpCategoryName(CategorizeIp(ip)));
|
||||
if ((r = MMDB_lookup(asdb, ip, &error)).found_entry &&
|
||||
!MMDB_aget_value(&r.entry, &e, PATH("autonomous_system_number")) &&
|
||||
!MMDB_aget_value(&r.entry, &f, PATH("autonomous_system_organization"))) {
|
||||
printf("%s: AS%u (%.*s)\n", "asn", e.uint32, f.data_size, f.utf8_string);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int i, rc;
|
||||
ipdb = gc(calloc(1, sizeof(MMDB_s)));
|
||||
CHECK_EQ(MMDB_SUCCESS,
|
||||
MMDB_open("/usr/local/share/maxmind/GeoLite2-City.mmdb", 0, ipdb));
|
||||
asdb = gc(calloc(1, sizeof(MMDB_s)));
|
||||
CHECK_EQ(MMDB_SUCCESS,
|
||||
MMDB_open("/usr/local/share/maxmind/GeoLite2-ASN.mmdb", 0, asdb));
|
||||
for (rc = 0, i = 1; i < argc; ++i) {
|
||||
if (PrintIpInfo(argv[i]) != -1) {
|
||||
if (i + 1 < argc) printf("\n");
|
||||
} else {
|
||||
fprintf(stderr, "NOT FOUND: %s\n", argv[i]);
|
||||
rc = 1;
|
||||
}
|
||||
}
|
||||
MMDB_close(asdb);
|
||||
MMDB_close(ipdb);
|
||||
return rc;
|
||||
}
|
156
tool/viz/tcp.c
Normal file
156
tool/viz/tcp.c
Normal file
|
@ -0,0 +1,156 @@
|
|||
#if 0
|
||||
/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/bits/bits.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
// clang-format off
|
||||
|
||||
static char ip[] = "\
|
||||
\x45\x00\x00\x3c\xc8\xbc\x40\x00\x40\x06\x48\xf6\x7f\x0a\x0a\x7a\
|
||||
\x7f\x0a\x0a\x7c\xe2\x24\x1b\x58\xf6\xe9\xf2\x85\x00\x00\x00\x00\
|
||||
\xa0\x02\xfa\xf0\x5f\xac\x00\x00\x02\x04\x05\xb4\x04\x02\x08\x0a\
|
||||
\x4a\x43\x93\x29\x00\x00\x00\x00\x01\x03\x03\x07";
|
||||
|
||||
static const char *const kTcpOptionNames[] = {
|
||||
[0] = "End of Option List",
|
||||
[1] = "No-Operation",
|
||||
[2] = "Maximum Segment Size",
|
||||
[3] = "Window Scale",
|
||||
[4] = "SACK Permitted",
|
||||
[5] = "SACK",
|
||||
[6] = "Echo (obsoleted by option 8)",
|
||||
[7] = "Echo Reply (obsoleted by option 8)",
|
||||
[8] = "Timestamps",
|
||||
[9] = "Partial Order Connection Permitted (obsolete)",
|
||||
[10] = "Partial Order Service Profile (obsolete)",
|
||||
[11] = "CC (obsolete)",
|
||||
[12] = "CC.NEW (obsolete)",
|
||||
[13] = "CC.ECHO (obsolete)",
|
||||
[14] = "TCP Alternate Checksum Request (obsolete)",
|
||||
[15] = "TCP Alternate Checksum Data (obsolete)",
|
||||
[16] = "Skeeter",
|
||||
[17] = "Bubba",
|
||||
[18] = "Trailer Checksum Option",
|
||||
[19] = "MD5 Signature Option (obsoleted by option 29)",
|
||||
[20] = "SCPS Capabilities",
|
||||
[21] = "Selective Negative Acknowledgements",
|
||||
[22] = "Record Boundaries",
|
||||
[23] = "Corruption experienced",
|
||||
[24] = "SNAP",
|
||||
[25] = "Unassigned (released 2000-12-18)",
|
||||
[26] = "TCP Compression Filter",
|
||||
[27] = "Quick-Start Response",
|
||||
[28] = "User Timeout Option",
|
||||
[29] = "TCP Authentication Option (TCP-AO)",
|
||||
[30] = "Multipath TCP (MPTCP)",
|
||||
[31] = "Reserved (known unauthorized use without proper IANA assignm",
|
||||
[32] = "Reserved (known unauthorized use without proper IANA assignm",
|
||||
[33] = "Reserved (known unauthorized use without proper IANA assignm",
|
||||
[34] = "variable TCP Fast Open Cookie",
|
||||
[35] = "Reserved",
|
||||
[69] = "Encryption Negotiation",
|
||||
[253] = "RFC3692-1",
|
||||
[254] = "RFC3692-2",
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int version = (ip[0] & 0b11110000) >> 4;
|
||||
int ihl = (ip[0] & 0b00001111) >> 0;
|
||||
int dscp = (ip[1] & 0b11111100) >> 2;
|
||||
int ecn = (ip[1] & 0b00000011) >> 0;
|
||||
int lengthtotal = READ16BE(ip + 2);
|
||||
int identification = READ16BE(ip + 4);
|
||||
int flags = (ip[6] & 0b11100000) >> 5;
|
||||
int fragmentoffset = (ip[6] & 0b00011111) << 8 | (ip[7] & 255);
|
||||
int ttl = ip[8] & 255;
|
||||
int protocol = ip[9] & 255;
|
||||
int ipchecksum = (ip[10] & 255) << 8 | (ip[11] & 255);
|
||||
int srcip = READ32BE(ip + 12);
|
||||
int dstip = READ32BE(ip + 16);
|
||||
|
||||
printf("\n");
|
||||
printf("// version = %u\n", version);
|
||||
printf("// ihl = %u\n", ihl * 4);
|
||||
printf("// dscp = %u\n", dscp);
|
||||
printf("// ecn = %u\n", ecn);
|
||||
printf("// lengthtotal = %u\n", lengthtotal);
|
||||
printf("// identification = %u\n", identification);
|
||||
printf("// flags = %u\n", flags);
|
||||
printf("// fragmentoffset = %u\n", fragmentoffset);
|
||||
printf("// ttl = %u\n", ttl);
|
||||
printf("// protocol = %u\n", protocol);
|
||||
printf("// ipchecksum = %u\n", ipchecksum);
|
||||
printf("// srcip = %hhu.%hhu.%hhu.%hhu\n", srcip >> 24, srcip >> 16, srcip >> 8, srcip);
|
||||
printf("// dstip = %hhu.%hhu.%hhu.%hhu\n", dstip >> 24, dstip >> 16, dstip >> 8, dstip);
|
||||
printf("// \n");
|
||||
|
||||
char *tcp = ip + ihl * 4;
|
||||
int srcport = READ16BE(tcp + 0);
|
||||
int dstport = READ16BE(tcp + 2);
|
||||
int sequence = READ32BE(tcp + 4);
|
||||
int acknumber = READ32BE(tcp + 8);
|
||||
int dataoffset = (tcp[12] & 0b11110000) >> 4;
|
||||
bool ns = !!(tcp[12] & 0b00000001);
|
||||
bool cwr = !!(tcp[13] & 0b10000000);
|
||||
bool ece = !!(tcp[13] & 0b01000000);
|
||||
bool urg = !!(tcp[13] & 0b00100000);
|
||||
bool ack = !!(tcp[13] & 0b00010000);
|
||||
bool psh = !!(tcp[13] & 0b00001000);
|
||||
bool rst = !!(tcp[13] & 0b00000100);
|
||||
bool syn = !!(tcp[13] & 0b00000010);
|
||||
bool fin = !!(tcp[13] & 0b00000001);
|
||||
int wsize = READ16BE(tcp + 14);
|
||||
int tcpchecksum = READ16BE(tcp + 16);
|
||||
int urgpointer = READ16BE(tcp + 18);
|
||||
|
||||
printf("// srcport = %u\n", srcport);
|
||||
printf("// dstport = %u\n", dstport);
|
||||
printf("// sequence = %u\n", sequence);
|
||||
printf("// acknumber = %u\n", acknumber);
|
||||
printf("// dataoffset = %u\n", dataoffset);
|
||||
printf("// ns = %u\n", ns);
|
||||
printf("// cwr = %u\n", cwr);
|
||||
printf("// ece = %u\n", ece);
|
||||
printf("// urg = %u\n", urg);
|
||||
printf("// ack = %u\n", ack);
|
||||
printf("// psh = %u\n", psh);
|
||||
printf("// rst = %u\n", rst);
|
||||
printf("// syn = %u\n", syn);
|
||||
printf("// fin = %u\n", fin);
|
||||
printf("// wsize = %u\n", wsize);
|
||||
printf("// tcpchecksum = %u\n", tcpchecksum);
|
||||
printf("// urgpointer = %u\n", urgpointer);
|
||||
printf("// \n");
|
||||
|
||||
int c, i, j, n;
|
||||
for (i = 20; i + 1 < dataoffset * 4;) {
|
||||
printf("// option");
|
||||
switch ((c = tcp[i] & 255)) {
|
||||
case 0:
|
||||
case 1:
|
||||
printf(" %u", c);
|
||||
++i;
|
||||
break;
|
||||
default:
|
||||
n = tcp[i + 1] & 255;
|
||||
printf(" %u %u", c, n);
|
||||
for (j = 2; j < n; ++j) {
|
||||
printf(" %u", tcp[i + j] & 255);
|
||||
}
|
||||
i += n;
|
||||
break;
|
||||
}
|
||||
if (kTcpOptionNames[c]) {
|
||||
printf(" (%s)", kTcpOptionNames[c]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -50,8 +50,9 @@ TOOL_VIZ_DIRECTDEPS = \
|
|||
THIRD_PARTY_DLMALLOC \
|
||||
THIRD_PARTY_GDTOA \
|
||||
THIRD_PARTY_GETOPT \
|
||||
THIRD_PARTY_STB \
|
||||
THIRD_PARTY_MAXMIND \
|
||||
THIRD_PARTY_MUSL \
|
||||
THIRD_PARTY_STB \
|
||||
THIRD_PARTY_XED \
|
||||
THIRD_PARTY_ZLIB \
|
||||
TOOL_DECODE_LIB \
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue