From 4fb053fbe9d50c263b322400e523066e72277f61 Mon Sep 17 00:00:00 2001 From: Emil Williams Date: Sat, 17 Feb 2024 18:26:14 +0000 Subject: [PATCH] significant update from OP --- bots/moonchat/moonchat.service | 13 + client/moontalk-tcl/LICENSE | 1 + client/moontalk-tcl/moontk.tcl | 196 +++++++++++ client/moontalk-tcl/notification.wav | Bin 0 -> 119800 bytes client/moontalk.tcl | 37 --- server/eventloop-server-experiment/CHANGELOG | 28 ++ server/eventloop-server-experiment/commandline.4th | 9 +- .../eventloop-server-experiment/configuration.4th | 12 + server/eventloop-server-experiment/connections.4th | 11 +- server/eventloop-server-experiment/dos.4th | 123 +++++++ server/eventloop-server-experiment/events.4th | 100 ++---- server/eventloop-server-experiment/extensions.4th | 16 + .../extensions/generic.4th | 4 + .../extensions/gforth-0.7.3.4th | 26 ++ .../extensions/gforth-latest.4th | 6 + .../libs/parser/parser.4th | 64 ++++ .../libs/xstring/xstring.4th | 22 ++ server/eventloop-server-experiment/logger.4th | 28 ++ server/eventloop-server-experiment/main.4th | 33 +- server/eventloop-server-experiment/motd-parser.4th | 18 + server/eventloop-server-experiment/patches/README | 3 + .../eventloop-server-experiment/patches/motd.4th | 1 + .../patches/unsanitized-message.4th | 9 + .../proxyline-parser.4th | 33 ++ server/eventloop-server-experiment/sendbuffer.4th | 57 +++- server/eventloop-server-experiment/server.4th | 364 +++++++++++++++++---- server/eventloop-server-experiment/stdout-hook.4th | 66 ++++ .../torcontrol-constants.4th | 2 + server/eventloop-server-experiment/torcontrol.4th | 97 ++++++ server/eventloop-server-experiment/util.4th | 25 ++ 30 files changed, 1212 insertions(+), 192 deletions(-) create mode 100644 bots/moonchat/moonchat.service create mode 120000 client/moontalk-tcl/LICENSE create mode 100755 client/moontalk-tcl/moontk.tcl create mode 100644 client/moontalk-tcl/notification.wav delete mode 100755 client/moontalk.tcl create mode 100644 server/eventloop-server-experiment/CHANGELOG create mode 100644 server/eventloop-server-experiment/configuration.4th create mode 100644 server/eventloop-server-experiment/dos.4th create mode 100644 server/eventloop-server-experiment/extensions.4th create mode 100644 server/eventloop-server-experiment/extensions/generic.4th create mode 100644 server/eventloop-server-experiment/extensions/gforth-0.7.3.4th create mode 100644 server/eventloop-server-experiment/extensions/gforth-latest.4th create mode 100644 server/eventloop-server-experiment/libs/parser/parser.4th create mode 100644 server/eventloop-server-experiment/libs/xstring/xstring.4th create mode 100644 server/eventloop-server-experiment/logger.4th create mode 100644 server/eventloop-server-experiment/motd-parser.4th create mode 100644 server/eventloop-server-experiment/patches/README create mode 100644 server/eventloop-server-experiment/patches/motd.4th create mode 100644 server/eventloop-server-experiment/patches/unsanitized-message.4th create mode 100644 server/eventloop-server-experiment/proxyline-parser.4th create mode 100644 server/eventloop-server-experiment/stdout-hook.4th create mode 100644 server/eventloop-server-experiment/torcontrol-constants.4th create mode 100644 server/eventloop-server-experiment/torcontrol.4th create mode 100644 server/eventloop-server-experiment/util.4th diff --git a/bots/moonchat/moonchat.service b/bots/moonchat/moonchat.service new file mode 100644 index 0000000..d2a09fb --- /dev/null +++ b/bots/moonchat/moonchat.service @@ -0,0 +1,13 @@ +[Unit] +Description=All bots for MoonChat +After=network.target + +[Service] +User=moonchat +Group=moonchat +ExecStart=/opt/moonchat/moonchat.sh +ExecReload=/opt/moonchat/moonchat.sh +Restart=always + +[Install] +WantedBy=default.target diff --git a/client/moontalk-tcl/LICENSE b/client/moontalk-tcl/LICENSE new file mode 120000 index 0000000..30cff74 --- /dev/null +++ b/client/moontalk-tcl/LICENSE @@ -0,0 +1 @@ +../../LICENSE \ No newline at end of file diff --git a/client/moontalk-tcl/moontk.tcl b/client/moontalk-tcl/moontk.tcl new file mode 100755 index 0000000..c6a284b --- /dev/null +++ b/client/moontalk-tcl/moontk.tcl @@ -0,0 +1,196 @@ +#!/usr/bin/wish + +# Default values. +set host "7ks473deh6ggtwqsvbqdurepv5i6iblpbkx33b6cydon3ajph73sssad.onion" +set port "50000" +set username "anonymous" +set reconnect_max_tries -1 +set reconnect_time 10000 + +set notification_exe "/usr/bin/aplay" +set notification_file "./notification.wav" +set notification_delay 1000 + +# Don't touch these +set identity "Anon ?" +set reconnect_try 0 +set sock 0 +set notification_cooldown 0 + +proc window_visibility {w val} { + if {$val} { + wm deiconify $w + } else { + wm withdraw $w + } +} + +proc on_user_connect {w} { + global reconnect_try + set reconnect_try 0 + window_visibility . true + socket_connect + window_visibility $w false +} + +proc display_connect_dialog {} { + global reconnect_try + global host + global port + set w .wconnect + if { [winfo exists $w] } { + window_visibility $w true + focus $w + } else { + toplevel $w + wm title $w "MoonTk - Connect to MoonTalk" + set callback_wrapper "on_user_connect $w" + pack [label $w.lh -text "Host:"] -anchor w + pack [entry $w.eh -textvariable host] -fill x + pack [label $w.lp -text "Port:"] -anchor w + pack [entry $w.ep -textvariable port] -fill x + pack [button $w.bc -text "connect" -command $callback_wrapper] + bind $w.eh $callback_wrapper + bind $w.eh $callback_wrapper + bind $w.ep $callback_wrapper + bind $w.ep $callback_wrapper + focus $w + } +} + +wm title . "MoonTk" +pack [entry .input] -side bottom -fill x +pack [scrollbar .sy -command {.messages yview}] -side right -fill y +pack [text .messages -wrap none -xscrollcommand {.sx set} -yscrollcommand {.sy set}] -fill both -expand 1 +pack [scrollbar .sx -orient horizontal -command {.messages xview}] -fill x + +proc socket_connect {} { + global sock + global host + global port + append_message "Connecting to $host:$port..." + if {[catch {socket -async $host $port} sock]} { + on_socket_connection_failed + } else { + fconfigure $sock -blocking false + fconfigure $sock -translation binary + fileevent $sock readable on_socket_receive + fileevent $sock writable on_socket_connect + } +} + +proc on_socket_connect {} { + global reconnect_try + global sock + set error [fconfigure $sock -error] + if {$error ne ""} { + catch {close $sock} + on_socket_connection_failed + return + } + append_message "Successfully connected to the server." + fileevent $sock writable {} + set reconnect_try 0 +} + +proc on_socket_connection_failed {} { + global reconnect_time + global reconnect_max_tries + global reconnect_try + if { $reconnect_max_tries != -1 + && $reconnect_try >= $reconnect_max_tries } { + tk_messageBox -message "Maximum reconnect tries reached." -type ok + display_connect_dialog + } else { + set reconnect_try [expr {$reconnect_try + 1}] + append_message "Failed to connect to the server, retrying in [expr {$reconnect_time/1000}] seconds." + after $reconnect_time socket_connect + } +} + +proc on_socket_disconnect {} { + append_message "Disconnected from server..." + socket_connect +} + +proc parse_identity {data} { + global identity + regexp -all {^Server: You are now known as \"(.+)\"\.} $data whole_match ident + if {[info exists ident]} { + set identity $ident + } +} + +proc reset_notification {} { + global notification_cooldown + set notification_cooldown 0 +} + +proc play_notification {} { + global notification_exe + global notification_file + global notification_cooldown + global notification_delay + set notification_cooldown 1 + exec $notification_exe $notification_file "&" + after $notification_delay reset_notification +} + +proc on_socket_receive {} { + global sock + global identity + global notification_cooldown + set error [fconfigure $sock -error] + # catch gets = read error + # eof = other side disconnected + if { $error ne "" + || [catch {gets $sock} data] + || [eof $sock]} { + catch {close $sock} + on_socket_disconnect + return + } + if {[string match "Server: *" $data]} { + parse_identity $data + } + if { !$notification_cooldown } { + play_notification + } + append_message $data +} + +proc send_message {msg} { + global sock + global identity + set formatted_msg [format_message $msg] + puts $sock $formatted_msg + flush $sock + append_message "$identity: $formatted_msg" +} +proc timestamp {} { + return [clock format [clock seconds] -gmt true -format {%Y/%m/%d %H:%M:XX}] +} +proc format_message {msg} { + global username + set ts [timestamp] + if {[string match "/*" $msg]} { + return $msg + } else { + return "<$ts $username> $msg" + } +} + +proc append_message {msg} { + .messages insert end "$msg\n" + .messages see end +} + +proc user_enter {} { + send_message [.input get] + .input delete 0 end +} +bind .input user_enter +bind .input user_enter + +window_visibility . false +display_connect_dialog diff --git a/client/moontalk-tcl/notification.wav b/client/moontalk-tcl/notification.wav new file mode 100644 index 0000000000000000000000000000000000000000..5366105e52e33bb37fe605978b52327aaeac84ef GIT binary patch literal 119800 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

^BaHnM>-*O($ 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 pzcYp&EDd+VD_1-Ziw} [.entry get]" -} - -bind .entry { - set msg [get_msg] - puts $::fd $msg - flush $::fd - add_msg $msg - .entry delete 0 end -} - -fileevent [set fd [socket $::host 50000]] readable { - add_msg [gets $::fd] -} - -chan configure $::fd -translation binary diff --git a/server/eventloop-server-experiment/CHANGELOG b/server/eventloop-server-experiment/CHANGELOG new file mode 100644 index 0000000..f6fdbe7 --- /dev/null +++ b/server/eventloop-server-experiment/CHANGELOG @@ -0,0 +1,28 @@ +16.02.2024 +* stdout redirection/hooking implemented +* added admin commands +* added user commands +* implemented logging + +14.02.2024 +* C FFI has been made optional + this is to allow this server to run on android using the existing gforth app + +09.02.2024 +* improved denial of service protections: + - we now parse the proxy line from Tor to get the circuit id which we can + use to close Tor circuits + - we now track the connections, bytes and the lines per tor circuit + +08.02.2024 +* implemented torcontrol +* simplified and improved the motd parser + +06.02.2024 +* added this changelog +* simplified and improved performance of the event queue events.4th +* refactoring variable -> variable! +* added server commands: + - server-commands, server-users, server-accept, server-accepting?, + server-disconnect, server-ban-circuit (placeholder), server-broadcast, + server-message diff --git a/server/eventloop-server-experiment/commandline.4th b/server/eventloop-server-experiment/commandline.4th index cd1f74b..87f74c3 100644 --- a/server/eventloop-server-experiment/commandline.4th +++ b/server/eventloop-server-experiment/commandline.4th @@ -1,3 +1,4 @@ +require util.4th 80 constant COMMANDLINE_SIZE create commandline COMMANDLINE_SIZE allot @@ -40,7 +41,7 @@ variable cmdready k-right of (cursor-right) endof endcase else ( keyboard-event ) - drop \ just ignore an unknown keyboard event type + drop \ just ignore an unknown keyboard event then then ; : commandline-getline ( -- c-addr u ) @@ -48,14 +49,16 @@ variable cmdready : (update-cursorpos) ( -- ) s\" \033[" type - (cursor@) 1+ s>d <# #s #> type + (cursor@) 1+ to-string type s" G" type ; : (carriage-return) ( -- ) 13 emit ; : commandline-redraw ( -- ) + false stdout-logger (carriage-return) commandline-getline type - (update-cursorpos) ; + (update-cursorpos) + true stdout-logger ; : commandline-reset ( -- ) commandline COMMANDLINE_SIZE bl fill diff --git a/server/eventloop-server-experiment/configuration.4th b/server/eventloop-server-experiment/configuration.4th new file mode 100644 index 0000000..9afa80e --- /dev/null +++ b/server/eventloop-server-experiment/configuration.4th @@ -0,0 +1,12 @@ + +true constant CONFIG_C_FFI + +23232 constant CONFIG_SERVER_PORT + +s" logs/" sconstant CONFIG_LOG_DIR + + TOR_CONTROL_AUTHMETHOD_COOKIE constant CONFIG_TOR_CONTROL_AUTHMETHOD + 1 24 lshift 127 or constant CONFIG_TOR_CONTROL_ADDR + 9051 constant CONFIG_TOR_CONTROL_PORT +s" /run/tor/control.authcookie" sconstant CONFIG_TOR_CONTROL_COOKIE_FILEPATH +\ s" mypassword" sconstant CONFIG_TOR_CONTROL_PASSWORD diff --git a/server/eventloop-server-experiment/connections.4th b/server/eventloop-server-experiment/connections.4th index 63423a6..c9c4aa7 100644 --- a/server/eventloop-server-experiment/connections.4th +++ b/server/eventloop-server-experiment/connections.4th @@ -1,18 +1,21 @@ +require util.4th 256 constant CONNECTION_BUFFER_SIZE - 0 cell +field connection.number + cell +field connection.admin cell +field connection.fd cell +field connection.connected + cell +field connection.sendcount + cell +field connection.circuitid cell +field connection.bufferlen CONNECTION_BUFFER_SIZE +field connection.buffer constant /CONNECTION -1024 10 * constant MAX_CONNECTIONS +32 constant MAX_CONNECTIONS -variable last-connection -1 last-connection ! -variable largest-index -1 largest-index ! +-1 variable! last-connection +-1 variable! largest-index MAX_CONNECTIONS /CONNECTION * constant CONNECTIONS_SIZE create connections CONNECTIONS_SIZE allot connections CONNECTIONS_SIZE erase diff --git a/server/eventloop-server-experiment/dos.4th b/server/eventloop-server-experiment/dos.4th new file mode 100644 index 0000000..02881e0 --- /dev/null +++ b/server/eventloop-server-experiment/dos.4th @@ -0,0 +1,123 @@ +\ Denial of service protection. +\ TODO: Use hash table instead? + +require util.4th + +[UNDEFINED] MAX_CONNECTIONS [IF] 32 constant MAX_CONNECTIONS [THEN] + + 10 constant DOS_UPDATE_INTERVAL + 1024 constant DOS_BYTES_PER_INTERVAL + 10 constant DOS_LINES_PER_INTERVAL + 3 constant DOS_MAX_CONNECTIONS +MAX_CONNECTIONS constant DOS_MAX_MAPPINGS + +\ TODO: is 0 a valid circuit id? +\ TODO: if not, then we could use the circuit id instead of dos.set +0 + cell +field dos.set + cell +field dos.circuit-id + cell +field dos.handled + cell +field dos.connections + cell +field dos.total-bytes + cell +field dos.total-lines + cell +field dos.bytes + cell +field dos.lines +constant /DOS + +/DOS DOS_MAX_MAPPINGS * constant DOS_ARRAY_SIZE +create dos DOS_ARRAY_SIZE alloterase + +\ Lookup table: connection index -> dos stats +create doslt DOS_MAX_MAPPINGS cells alloterase + +: (translate) ( index-n -- dos-addr ) + /DOS * dos + ; +: (init-dos) ( free-addr circuit-id-n -- dos-addr ) + over dos.circuit-id ! + true over dos.set ! ; +: (find) ( circuit-id-n -- dos-addr ) + 0 swap dos DOS_ARRAY_SIZE + dos DO ( -- free-addr circuit-id-n ) + I dos.set @ IF + dup I dos.circuit-id @ = IF + 2drop I UNLOOP EXIT + THEN + ELSE + over 0= IF + nip I swap + THEN + THEN + /DOS +LOOP + (init-dos) ; +: (lttranslate) ( connection-index-n -- lookup-addr ) + cells doslt + ; +: (lookup) ( connection-index-n -- dos-addr ) + (lttranslate) @ ; + +: (mod-connections) ( n dos-addr -- ) dos.connections +! ; +: (inc-connections) ( dos-addr -- ) 1 swap (mod-connections) ; +: (dec-connections) ( dos-addr -- ) -1 swap (mod-connections) ; +: dos-add-connection ( circuit-id-n connection-index-n -- ) + (lttranslate) dup @ 0= IF + swap (find) tuck (inc-connections) ! + ELSE + 2drop EXIT + THEN ; +: dos-remove-connection ( connection-index-n -- ) +\ erase if last connection + (lttranslate) dup 0<> IF + dup @ >r 0 swap ! r> + dup (dec-connections) + dup dos.connections @ 0= IF + /DOS erase + ELSE + drop + THEN + ELSE + drop + THEN ; +: dos-add-bytes ( bytes-n connection-index-n -- ) + (lookup) dos.bytes +! ; +: dos-add-lines ( lines-n connection-index-n -- ) + (lookup) dos.lines +! ; +: (update) ( dos-addr -- ) + dup dos.bytes @ over dos.total-bytes +! + dup dos.lines @ over dos.total-lines +! + 0 over dos.bytes ! + 0 over dos.lines ! + drop ; +: dos-update ( -- ) +\ add time interval bytes and lines to total and set to 0 + dos DOS_ARRAY_SIZE + dos DO + I dos.set @ IF + I (update) + THEN + /DOS +LOOP ; + +: (check-bytes) ( dos-addr -- flag ) dos.bytes @ DOS_BYTES_PER_INTERVAL > ; +: (check-lines) ( dos-addr -- flag ) dos.lines @ DOS_LINES_PER_INTERVAL > ; +: (check-connections) ( dos-addr -- flag ) dos.connections @ DOS_MAX_CONNECTIONS > ; +: dos-handled! ( flag connection-index-n -- ) + (lttranslate) @ dos.handled ! ; +: dos-handled? ( connection-index-n -- flag ) + (lttranslate) @ dos.handled @ ; +: dos? ( connection-index-n -- flag ) + (lttranslate) @ + dup (check-bytes) over (check-lines) + rot (check-connections) or or ; + +: (.dos-info) ( dos-addr -- ) + dup ." CircuitID: " dos.circuit-id @ . cr + dup ." Connections: " dos.connections @ . cr + dup ." Total bytes: " dos.total-bytes @ . cr + dup ." Total lines: " dos.total-lines @ . cr + dup ." Bytes: " dos.bytes @ . cr + dup ." Lines: " dos.lines @ . cr + drop ; +: .dos-info ( connection-index-u -- ) + (lttranslate) @ (.dos-info) ; +: .dos ( -- ) + dos DOS_ARRAY_SIZE + dos DO + I dos.set @ IF + cr I (.dos-info) + THEN + /DOS +LOOP ; diff --git a/server/eventloop-server-experiment/events.4th b/server/eventloop-server-experiment/events.4th index 3e962f0..0b67800 100644 --- a/server/eventloop-server-experiment/events.4th +++ b/server/eventloop-server-experiment/events.4th @@ -1,88 +1,38 @@ -256 constant MAX_EVENTS +require util.4th + +1024 constant MAX_EVENTS 0 cell +field event.id cell +field event.data constant /EVENT -0 - cell +field eventlink.next - /EVENT +field eventlink.event -constant /EVENTLINK +0 variable! current-event +0 variable! last-event -variable first-event -variable last-event -variable free-event -MAX_EVENTS /EVENTLINK * constant EVENTS_SIZE +MAX_EVENTS /EVENT * constant EVENTS_SIZE create events EVENTS_SIZE allot -: (translate) ( index-u -- eventlink-addr ) - /EVENTLINK * events + ; -: (link-to-next) ( index-u -- ) - dup 1+ (translate) swap (translate) eventlink.next ! ; -: (fix-last-link) ( -- ) - MAX_EVENTS 1- (translate) eventlink.next 0 swap ! ; -: (set-first-free) ( -- ) - 0 (translate) free-event ! ; -: (link-free) ( -- ) - MAX_EVENTS 0 DO I (link-to-next) LOOP - (fix-last-link) - (set-first-free) ; -: (free-available?) ( -- flag ) - free-event @ 0<> ; -: (assert-free-available) ( -- ) - (free-available?) invert abort" no free eventlinks available." ; -: (next-free) ( -- eventlink-addr ) - (assert-free-available) - free-event @ dup eventlink.next @ free-event ! ; - -: events.clear ( -- ) - 0 first-event ! 0 last-event ! - events EVENTS_SIZE erase - (link-free) ; -: (set-next-null) ( eventlink-addr -- ) - dup eventlink.next 0 swap ! ; -: (first-event-exists?) ( -- flag ) first-event @ 0<> ; -: (last-event-exists?) ( -- flag ) last-event @ 0<> ; -: (as-first-event) ( eventlink-addr -- ) first-event ! ; -: (as-last-event) ( eventlink-addr -- ) last-event ! ; -: (after-last-event) ( eventlink-addr -- ) - dup last-event @ eventlink.next ! - last-event ! ; -: (append-event) ( eventlink-addr -- ) - (set-next-null) - (first-event-exists?) invert IF - dup (as-first-event) - THEN - (last-event-exists?) IF - dup (after-last-event) - ELSE - dup (as-last-event) - THEN drop ; -: (set-eventdata) ( data-u id-u eventlink-addr -- ) - eventlink.event tuck event.id ! event.data ! ; +: (translate) ( index-u -- event-addr ) + ]] /EVENT * events + [[ ; IMMEDIATE +: (wrap) ( index-u -- index-u ) + ]] MAX_EVENTS mod [[ ; IMMEDIATE +: (read) ( addr -- data-u id-u ) + ]] dup event.data @ swap event.id @ [[ ; IMMEDIATE +: (write) ( data-u id-u addr -- ) + ]] tuck event.id ! event.data ! [[ ; IMMEDIATE +: events.has-item? ( -- flag ) + current-event @ last-event @ <> ; : events.enqueue ( data-u id-u -- ) - (next-free) dup >r (set-eventdata) r> (append-event) ; -: (get-eventdata) ( eventlink-addr -- data-u id-u ) - eventlink.event dup event.data @ swap event.id @ ; -: (assert-first-exists) ( -- ) - (first-event-exists?) invert abort" no events in queue" ; -: (check-first-and-last) ( -- ) - first-event @ 0= IF - 0 last-event ! - THEN ; -: (get-first-event) ( -- eventlink-addr ) - first-event @ ; -: (free-event) ( eventlink-addr -- ) - dup eventlink.next free-event @ swap ! - free-event ! ; -: (set-first-event-to-next) ( -- ) - first-event @ eventlink.next @ first-event ! - (check-first-and-last) ; + last-event @ dup 1+ dup >r current-event @ = abort" Queue is full." + (translate) (write) r> (wrap) last-event ! ; : events.dequeue ( -- data-u id-u ) - (assert-first-exists) (get-first-event) (set-first-event-to-next) - dup (free-event) (get-eventdata) ; -: events.has-item? ( -- flag ) (first-event-exists?) ; + events.has-item? invert abort" No events in queue." + current-event @ dup (translate) (read) + rot 1+ (wrap) current-event ! ; +: events.clear ( -- ) + 0 current-event ! + 0 last-event ! + events EVENTS_SIZE erase ; -\ Clear events, initialize events array. events.clear diff --git a/server/eventloop-server-experiment/extensions.4th b/server/eventloop-server-experiment/extensions.4th new file mode 100644 index 0000000..62a34c2 --- /dev/null +++ b/server/eventloop-server-experiment/extensions.4th @@ -0,0 +1,16 @@ +require unix/socket.fs + +require configuration.4th + +s" gforth" environment? [IF] + s" 0.7.3" compare 0= [IF] + require extensions/gforth-0.7.3.4th + [ELSE] +\ we assume the latest version, as 0.7.3 is more than 10 years old already + require extensions/gforth-latest.4th + [THEN] + require extensions/generic.4th +[ELSE] + 2drop cr ." We should never reach this." cr + abort +[THEN] diff --git a/server/eventloop-server-experiment/extensions/generic.4th b/server/eventloop-server-experiment/extensions/generic.4th new file mode 100644 index 0000000..18ce16d --- /dev/null +++ b/server/eventloop-server-experiment/extensions/generic.4th @@ -0,0 +1,4 @@ + +: time ( a -- n ) + abort" argument not supported" + utime #1000000 um/mod nip ; diff --git a/server/eventloop-server-experiment/extensions/gforth-0.7.3.4th b/server/eventloop-server-experiment/extensions/gforth-0.7.3.4th new file mode 100644 index 0000000..b193b59 --- /dev/null +++ b/server/eventloop-server-experiment/extensions/gforth-0.7.3.4th @@ -0,0 +1,26 @@ + + +CONFIG_C_FFI invert [IF] + cr + ." To run this program on Gforth 0.7.3 we need the C FFI, as setsockopt" cr + ." is not available in 0.7.3." cr + abort +[THEN] + +\ Gforth 0.7.3 doesn't seem to have these defined. + 2 Constant AF_INET + $40 Constant MSG_DONTWAIT + $4000 constant MSG_NOSIGNAL + 2048 constant SOCK_NONBLOCK + 1 constant SOL_SOCKET + 2 Constant SO_REUSEADDR + 11 constant EAGAIN + +sockaddr_in nip constant /sockaddr_in +4 constant /option_value + +' closesocket alias close + +c-library socketextlib + c-function setsockopt setsockopt n n n a n -- n ( sockfd level optname optval optlen -- r ) +end-c-library diff --git a/server/eventloop-server-experiment/extensions/gforth-latest.4th b/server/eventloop-server-experiment/extensions/gforth-latest.4th new file mode 100644 index 0000000..cdf12ea --- /dev/null +++ b/server/eventloop-server-experiment/extensions/gforth-latest.4th @@ -0,0 +1,6 @@ + +$4000 constant MSG_NOSIGNAL +2048 constant SOCK_NONBLOCK + +sockaddr_in constant /sockaddr_in +4 constant /option_value diff --git a/server/eventloop-server-experiment/libs/parser/parser.4th b/server/eventloop-server-experiment/libs/parser/parser.4th new file mode 100644 index 0000000..dfc4d2d --- /dev/null +++ b/server/eventloop-server-experiment/libs/parser/parser.4th @@ -0,0 +1,64 @@ +\ Simple stateful parsing module. + +0 + cell +field parser-string + cell +field parser-size + cell +field parser-marker + cell +field parser-cursor +constant PARSER_SIZE + +variable context +: (context@) ( -- parser-addr ) context @ ; +: (context!) ( parser-addr -- ) context ! ; + +: (string@) ( -- c-addr ) (context@) parser-string @ ; +: (string!) ( c-addr -- ) (context@) parser-string ! ; +: (size@) ( -- u ) (context@) parser-size @ ; +: (size!) ( u -- ) (context@) parser-size ! ; +: (marker@) ( -- u ) (context@) parser-marker @ ; +: (marker!) ( u -- ) (context@) parser-marker ! ; +: (cursor@) ( -- u ) (context@) parser-cursor @ ; +: (cursor!) ( u -- ) (context@) parser-cursor ! ; + +: new-parser ( c-addr u parser-addr -- ) + (context!) (size!) (string!) 0 dup (marker!) (cursor!) ; +: restore-parser ( parser-addr -- ) (context!) ; +: current-parser ( -- parser-addr ) (context@) ; + +: parser-here ( -- u ) (cursor@) ; +: parser-marker ( -- u ) (marker@) ; +: parser-mark ( -- ) (cursor@) (marker!) ; +: parser-backtrack ( -- ) (marker@) (cursor!) ; + +: parser-remaining ( -- c-addr u ) + (string@) (cursor@) + (size@) (cursor@) - ; + +: parser-extract ( -- c-addr u ) + (string@) (marker@) + (cursor@) (marker@) - ; + +: parser>>| ( -- ) (size@) (cursor!) ; +: parser|<< ( -- ) 0 (cursor!) ; +: parser>> ( u -- ) (cursor@) + (size@) min 0 max (cursor!) ; +: parser<< ( u -- ) negate parser>> ; + +: parser>>string ( c-addr u -- flag ) + parser-remaining 2swap search IF + drop (string@) - (cursor!) true + ELSE + 2drop false + THEN ; + +: parser>>|string ( c-addr u -- flag ) + parser>>string ; + +: parser>>string| ( c-addr u -- flag ) + dup -rot parser>>string IF + parser>> true + ELSE + drop false + THEN ; + +: with-parser ( xt parser-addr -- ) + (context@) >r (context!) execute r> (context!) ; +: with-new-parser ( xt str parser-addr -- ) + (context@) >r new-parser execute r> (context!) ; diff --git a/server/eventloop-server-experiment/libs/xstring/xstring.4th b/server/eventloop-server-experiment/libs/xstring/xstring.4th new file mode 100644 index 0000000..4429d87 --- /dev/null +++ b/server/eventloop-server-experiment/libs/xstring/xstring.4th @@ -0,0 +1,22 @@ +\ An extended string is essentially the same +\ as a counted string, with the only difference that +\ instead of storing max 1 char length of a string, +\ we can store up to cell sized strings. + +\ Copy an extended string to +: xplace ( c-addr u a-addr -- ) + swap dup >r over ! ( c-addr a-addr ) + cell + r> move ; +: xcount ( a-addr -- c-addr u ) + dup cell + swap @ ; +: +xplace ( c-addr u a-addr -- ) + 2dup >r >r xcount ( c-addr u c-addr u ) + + swap ( c-addr a-addr u ) + move ( -- ) + r> r> +! ; + +\ single char +create somechar 1 chars allot align +: +xplace-char ( n a-addr -- ) + swap somechar c! somechar 1 rot +xplace ; + diff --git a/server/eventloop-server-experiment/logger.4th b/server/eventloop-server-experiment/logger.4th new file mode 100644 index 0000000..9879b07 --- /dev/null +++ b/server/eventloop-server-experiment/logger.4th @@ -0,0 +1,28 @@ +require libs/xstring/xstring.4th + +require extensions.4th + +0 variable! logfd + +: (log-filepath) ( -- str ) + CONFIG_LOG_DIR pad xplace + 0 time to-string pad +xplace + s" .log" pad +xplace + pad xcount ; + +(log-filepath) sconstant log-filepath + +: logger.open ( -- ) + log-filepath r/w create-file throw logfd ! ; +: logger.close ( -- ) + logfd @ close-file drop ; + +: logger.flush ( -- ) + logfd @ flush-file drop ; + +: logger.log ( str -- ) + logfd @ 0<> IF + logfd @ write-file drop + ELSE + 2drop + THEN ; diff --git a/server/eventloop-server-experiment/main.4th b/server/eventloop-server-experiment/main.4th index 306a282..bbef961 100644 --- a/server/eventloop-server-experiment/main.4th +++ b/server/eventloop-server-experiment/main.4th @@ -1,10 +1,15 @@ +require util.4th +require torcontrol-constants.4th +require configuration.4th +require stdout-hook.4th require check-gforth.4th require eventloop.4th require event-constants.4th require server.4th -variable clcounter -0 clcounter ! +\ TODO: integrate generic timed event handling into the event loop? + +0 variable! clcounter : handle-command-line? ( -- flag ) clcounter @ 10 >= dup IF 0 clcounter ! @@ -12,20 +17,40 @@ variable clcounter 1 clcounter +! THEN ; +0 variable! dostimer +: handle-dos? ( -- flag ) + 0 time dup dostimer @ > IF + DOS_UPDATE_INTERVAL + dostimer ! + true + ELSE + drop false + THEN ; + : custom-eventloop ( -- ) BEGIN handle-command-line? IF 0 EVENT_COMMANDLINE events.enqueue THEN + handle-dos? IF + dos-update + THEN eventloop.has-events? IF eventloop.dispatch ELSE server-idle? IF - 1 ms false server-idle! + 10 ms false server-idle! THEN 0 0 events.enqueue eventloop.dispatch THEN AGAIN ; -: main ( -- ) ['] custom-eventloop catch close-server throw ; +: main ( -- ) + logger.open + ['] custom-eventloop catch close-server throw + logger.close ; +\ : main ( -- ) +\ logger.open +\ custom-eventloop close-server +\ logger.close ; + main diff --git a/server/eventloop-server-experiment/motd-parser.4th b/server/eventloop-server-experiment/motd-parser.4th new file mode 100644 index 0000000..33f7dfc --- /dev/null +++ b/server/eventloop-server-experiment/motd-parser.4th @@ -0,0 +1,18 @@ +require libs/parser/parser.4th + +create motd-parser PARSER_SIZE allot +create motd-line-delim 10 c, + +: (motd-delim) ( -- str ) + motd-line-delim 1 ; +variable (append-xt) +: (append) ( str -- ) + (append-xt) @ execute ; +: parse-motd ( motd-str append-line-xt -- ) + (append-xt) ! motd-parser new-parser + BEGIN + parser-mark (motd-delim) parser>>string + WHILE + parser-extract (append) + (motd-delim) nip parser>> + REPEAT ; diff --git a/server/eventloop-server-experiment/patches/README b/server/eventloop-server-experiment/patches/README new file mode 100644 index 0000000..1b93cca --- /dev/null +++ b/server/eventloop-server-experiment/patches/README @@ -0,0 +1,3 @@ +Optional runtime patches that can be applied with: + +require patches/mypatch.4th diff --git a/server/eventloop-server-experiment/patches/motd.4th b/server/eventloop-server-experiment/patches/motd.4th new file mode 100644 index 0000000..6bfb32e --- /dev/null +++ b/server/eventloop-server-experiment/patches/motd.4th @@ -0,0 +1 @@ +s\" https://git.lain.church/emil/moontalk\n\nType /help for commands." motd-current-banner motd-compose diff --git a/server/eventloop-server-experiment/patches/unsanitized-message.4th b/server/eventloop-server-experiment/patches/unsanitized-message.4th new file mode 100644 index 0000000..53c2367 --- /dev/null +++ b/server/eventloop-server-experiment/patches/unsanitized-message.4th @@ -0,0 +1,9 @@ + +: server-message-unsanitized ( msg-str user-n -- ) + sendbuffer-reset + cr >r + s" Server: " sendbuffer-append + sendbuffer-append + s\" \n" sendbuffer-append + r> 1- connections.at dup (assert-connected) + (send-sendbuffer) ; diff --git a/server/eventloop-server-experiment/proxyline-parser.4th b/server/eventloop-server-experiment/proxyline-parser.4th new file mode 100644 index 0000000..cf03fda --- /dev/null +++ b/server/eventloop-server-experiment/proxyline-parser.4th @@ -0,0 +1,33 @@ +\ Tor specific words. + +require libs/parser/parser.4th + +create proxyline-parser PARSER_SIZE allot + +: (expect&skip) ( str -- ) + tuck parser>>string invert abort" parsing exception" parser>> ; +: (extract-before) ( str -- ) + parser-mark (expect&skip) 1 parser<< parser-extract 1 parser>> ; +: (hexstr>value) ( str -- n ) + hex 2>r 0 0 2r> >number 2drop d>s decimal ; +: (parse-circuitid) ( -- circuitid-n ) + s" :" (extract-before) pad place + s" " (extract-before) pad +place + pad count (hexstr>value) ; +: proxyline>circuitid ( line-str -- circuitid-n remaining-str ) + proxyline-parser new-parser + s" PROXY TCP6 fc00:dead:beef:4dad::" (expect&skip) (parse-circuitid) + s\" \r\n" (expect&skip) parser-remaining ; + +\ TODO: removeme +: proxyline-test1 ( -- ) + s\" PROXY TCP6 fc00:dead:beef:4dad::ffff:ffff ::1 65535 42\r\n" proxyline>circuitid + 2drop 4294967295 <> abort" ASDF" ; + +: proxyline-test2 ( -- ) + s\" PROXY TCP6 fc00:dead:beef:4dad::AABB:CCDD ::1 65535 42\r\n" proxyline>circuitid + 2drop 2864434397 <> abort" ASDF" ; + +proxyline-test1 +proxyline-test2 + diff --git a/server/eventloop-server-experiment/sendbuffer.4th b/server/eventloop-server-experiment/sendbuffer.4th index e6a50fb..2048e4f 100644 --- a/server/eventloop-server-experiment/sendbuffer.4th +++ b/server/eventloop-server-experiment/sendbuffer.4th @@ -1,19 +1,48 @@ -variable sendbuffer-len 0 sendbuffer-len ! +require util.4th +require configuration.4th + +0 variable! sendbuffer-len 4096 constant SENDBUFFER_SIZE create sendbuffer SENDBUFFER_SIZE allot -\ Calling C here is just optimization. -c-library sanitizelib -\c void csanitize(char *buffer, int buffersize) { -\c int lastIsNewline = buffer[buffersize-1] == '\n' ? 1 : 0; -\c for(int i = 0; i126) { buffer[i] = '?'; } -\c } -\c if(lastIsNewline) { buffer[buffersize-1] = '\n'; } -\c return; -\c } - c-function csanitize csanitize a n -- void -end-c-library +CONFIG_C_FFI invert [IF] + variable last-is-newline + : (last) ( c-addr u -- c-addr ) + 1- + ; + : (sanitize-char) ( c-addr -- ) + dup c@ dup 32 < swap 126 > or IF + [char] ? swap c! + ELSE + drop + THEN ; + : sanitize ( c-addr u -- ) + dup 0<= IF + 2drop EXIT + THEN + 2dup (last) c@ 10 = last-is-newline ! + 2dup + bounds DO + I (sanitize-char) + LOOP + last-is-newline @ IF + (last) 10 swap c! + ELSE + 2drop + THEN ; +[ELSE] + \ Calling C here is just optimization. + c-library sanitizelib + \c void sanitize(char *buffer, int buffersize) { + \c int lastIsNewline = buffer[buffersize-1] == '\n' ? 1 : 0; + \c for(int i = 0; i126) { buffer[i] = '?'; } + \c } + \c if(lastIsNewline) { buffer[buffersize-1] = '\n'; } + \c return; + \c } + c-function sanitize sanitize a n -- void + end-c-library +[THEN] : sendbuffer-reset ( -- ) 0 sendbuffer-len ! ; : (overflow?) ( n -- flag ) @@ -24,5 +53,5 @@ end-c-library : sendbuffer-append ( str -- ) dup (overflow?) abort" sendbuffer overflow" (append) ; : sendbuffer-sanitize ( -- ) - sendbuffer sendbuffer-len @ csanitize ; + sendbuffer sendbuffer-len @ sanitize ; : sendbuffer@ ( -- str ) sendbuffer sendbuffer-len @ ; diff --git a/server/eventloop-server-experiment/server.4th b/server/eventloop-server-experiment/server.4th index 5116c91..bccc6b9 100644 --- a/server/eventloop-server-experiment/server.4th +++ b/server/eventloop-server-experiment/server.4th @@ -1,9 +1,16 @@ require unix/socket.fs -require socket-extensions.4th +require libs/xstring/xstring.4th + +require util.4th +require extensions.4th require connections.4th require commandline.4th require motd.4th +require motd-parser.4th +require proxyline-parser.4th +require torcontrol.4th +require dos.4th require sendbuffer.4th AF_INET constant SERVER_SOCKET_DOMAIN @@ -12,15 +19,20 @@ AF_INET constant SERVER_SOCKET_DOMAIN constant SERVER_SOCKET_TYPE 0 constant SERVER_SOCKET_PROTOCOL 0 constant SERVER_ADDR - 50000 constant SERVER_PORT - 128 constant SERVER_LISTEN_BACKLOG +CONFIG_SERVER_PORT constant SERVER_PORT + 4 constant SERVER_LISTEN_BACKLOG \ Listening file descriptor. -variable listenfd -0 listenfd ! +0 variable! listenfd + +\ If we should accept new connections. +true variable! accept-connections + +\ If we should echo back command responses. +true variable! command-echo \ Idle detection. -variable idle false idle ! +false variable! idle : server-idle? ( -- flag ) idle @ ; : server-idle! ( flag -- ) idle ! ; @@ -43,7 +55,7 @@ create optval /option_value allot saddr family w! ; : (assert-socket) ( result-n -- result-n ) - dup 0< abort" socket() failed." ; + dup 0< abort" socket failed." ; : (assert-bind) ( result-n -- ) 0< abort" bind failed." ; : (assert-listen) ( result-n -- ) @@ -76,8 +88,11 @@ create optval /option_value allot (server-info) ; : (perform-disconnect) ( connection-addr -- ) + dup connection.circuitid @ 0<> IF + dup connections.indexOf dos-remove-connection + THEN dup connection.connected false swap ! - connection.fd @ close() throw ; + connection.fd @ close throw ; : (close-clients) ( -- ) connections.count 0= IF @@ -89,10 +104,10 @@ create optval /option_value allot THEN LOOP ; -: (assert-close()) ( result-n -- ) +: (assert-close) ( result-n -- ) 0<> abort" close failed" ; : (close-server) ( -- ) - listenfd @ close() (assert-close()) ; + listenfd @ close (assert-close) ; : (close-server-info) ( -- ) cr ." Closed server connections." cr ; @@ -129,6 +144,9 @@ create optval /option_value allot true (con!) con (store-connection) ; : (server-idle-accept) ( -- ) + accept-connections @ invert IF + EXIT + THEN (try-accept) dup 0< IF (accept-error) ELSE @@ -179,17 +197,32 @@ create optval /option_value allot THEN LOOP ; -: (to-string) ( n -- addr c ) s>d <# #s #> ; : (connection.number>string) ( connection-addr -- c-addr u ) - connection.number @ (to-string) ; + connection.number @ to-string ; : (connection.buffer>string) ( connection-addr -- c-addr u ) - dup connection.buffer swap connection.bufferlen @ ; -: (format-sendbuffer) ( from-connection-addr -- ) - >r sendbuffer-reset - s" Anon " sendbuffer-append - r@ (connection.number>string) sendbuffer-append - s" : " sendbuffer-append - r> (connection.buffer>string) sendbuffer-append + dup connection.buffer swap connection.bufferlen @ ; +: (connection>name) ( connection-addr -- c-addr u ) + s" Anon " pad place + (connection.number>string) pad +place + pad count ; +: (expect-proxyline?) ( connection-addr -- flag ) + connection.circuitid @ 0= ; +: (parse-proxyline) ( connection-addr -- ) + dup >r (connection.buffer>string) proxyline>circuitid + dup r@ connection.bufferlen ! r@ connection.buffer swap move + r> connection.circuitid ! ; +: (last-sendbuffer-char) ( -- c ) + sendbuffer@ + 1- c@ ; +: (maybe-append-newline) ( -- ) + (last-sendbuffer-char) 10 <> IF + s\" \n" sendbuffer-append + THEN ; +: (format-sendbuffer) ( msg-str from-str -- ) + sendbuffer-reset + sendbuffer-append + s" : " sendbuffer-append + sendbuffer-append + (maybe-append-newline) sendbuffer-sanitize ; : (connected?) ( connection-addr -- ) connection.connected @ ; @@ -200,59 +233,156 @@ create optval /option_value allot : (check-send) ( result-n -- ) 0< IF ." Warning: send failed." cr THEN ; : (send-sendbuffer) ( to-connection-addr -- ) - connection.fd @ sendbuffer@ 0 send (check-send) ; -: (send) ( from-connection-addr to-connection-addr -- ) - (send-sendbuffer) ; + connection.fd @ sendbuffer@ MSG_NOSIGNAL send (check-send) ; : (try-send) ( from-connection-addr to-connection-addr -- ) 2dup (send?) IF nip (send-sendbuffer) ELSE 2drop THEN ; -: server-recv ( from-connection-addr eventid-n ) - drop dup (format-sendbuffer) +: (dos-update-stats) ( from-connection-addr -- ) + dup connections.indexOf + swap (connection.buffer>string) nip over dos-add-bytes + 1 swap dos-add-lines ; +: (dos-protect?) ( connection-addr -- flag ) + connections.indexOf dos? ; +: (dos-protect) ( connection-addr -- ) + ." DOS protection enabled for circuit:" cr + dup connections.indexOf .dos-info + dup connections.indexOf true swap dos-handled! + connection.circuitid @ torcontrol-close-circuit ; +: (is-command?) ( str -- flag ) + 1 min s" /" compare 0= ; +create command-parser PARSER_SIZE allot +: (extract-command) ( str -- str ) + command-parser new-parser 1 parser>> parser-remaining ; +: (parse-command) ( str -- str flag ) + 2dup (is-command?) IF + (extract-command) true + ELSE + false + THEN ; +4096 constant REDIRECT_BUFFER_SIZE +create server-redirect-buffer REDIRECT_BUFFER_SIZE allot +create server-emit-buffer 1 chars allot +variable redirect-broadcast-xt +: (server-redirect-reset) ( -- ) + s" " server-redirect-buffer xplace ; +: (server-redirect-flush) ( -- ) + server-redirect-buffer xcount redirect-broadcast-xt @ execute + (server-redirect-reset) ; +: (server-type) ( str -- ) +\ overflow check + dup cell + server-redirect-buffer xcount nip + REDIRECT_BUFFER_SIZE <= IF + server-redirect-buffer +xplace + ELSE + 2drop + THEN ; +: (server-emit) ( c -- ) + server-emit-buffer c! + server-emit-buffer 1 chars (server-type) ; +: (enable-redirect) ( -- ) + ['] (server-emit) stdout-hook-emit + ['] (server-type) stdout-hook-type + (server-redirect-reset) ; +: (disable-redirect) ( -- ) + (server-redirect-flush) + stdout-hook-reset ; +: (depth-evaluate) ( command-str -- ) + depth 2 - >r + ['] evaluate catch IF + 2drop ." An error has occured." cr + THEN + depth r> <> abort" aborting to fix stack." ; +: (dispatch-admin-command) ( connection-addr command-str -- flag ) + rot connection.admin @ IF + ['] (depth-evaluate) catch IF 2drop THEN true + ELSE + 2drop false + THEN ; +\ TODO: user command dispatching is very basic for now +\ TODO: maybe make commands extendible at runtime? +defer user-command-help ( -- ) +defer user-command-users ( -- ) +defer user-command-whoami ( connection-addr -- ) +' noop is user-command-help +' noop is user-command-users +' drop is user-command-whoami +: (dispatch-user-command) ( connection-addr command-str -- ) + 2dup s" help" startswith IF + 3drop user-command-help + ELSE 2dup s" users" startswith IF + 3drop user-command-users + ELSE 2dup s" whoami" startswith IF + 2drop user-command-whoami + ELSE + 3drop ." Unknown user command." cr + THEN THEN THEN ; +: (handle-command) ( connection-addr -- ) + dup (connection.buffer>string) (parse-command) IF + (enable-redirect) + 3dup (dispatch-admin-command) IF + 3drop + ELSE + (dispatch-user-command) + THEN + (disable-redirect) + ELSE + 2drop drop + THEN ; +: (handle-broadcast) ( connection-addr -- ) + dup >r (connection.buffer>string) r@ (connection>name) (format-sendbuffer) + r> (dos-update-stats) + sendbuffer@ type connections.count 0 DO dup I connections.at (try-send) - LOOP drop ; + LOOP ; +: server-recv ( from-connection-addr eventid-n ) + drop + dup (expect-proxyline?) IF + dup (parse-proxyline) + dup connection.circuitid @ over connections.indexOf dos-add-connection + dup (connection.buffer>string) nip 0= IF + drop EXIT + THEN + THEN + dup connections.indexOf dos-handled? IF + drop EXIT + THEN + dup (dos-protect?) IF + (dos-protect) + ELSE + dup (handle-broadcast) + (handle-command) + THEN ; : server-idle-accept ( eventdata-n eventid-n -- ) 2drop (server-idle-accept) ; : server-idle-recv ( eventdata-n eventid-n -- ) 2drop (server-idle-recv) ; -variable (strstart) -variable (strend) -: (>str) ( startindex-n endindex-n str-addr -- c-addr u ) - tuck + -rot + tuck - ; -: (newline?) ( char -- flag ) 10 = ; -\ TODO: FIXME: refactor and create words to be able to conveniently -\ TODO: FIXME: send "Server: ..." messages. This will be useful in the repl too. + +false variable! motd-cached +create motd-cache SENDBUFFER_SIZE allot +0 variable! motd-cache-length +: (sendbuffer-motd-line-append) ( str -- ) + s" Server: " sendbuffer-append + sendbuffer-append + s\" \n" sendbuffer-append ; : (prepare-motd) ( -- ) -\ TODO: FIXME: just write a proper parser at this point.... sendbuffer-reset - -1 (strstart) ! - -1 (strend) ! - motd@ 0 DO - (strstart) @ -1 = IF - I (strstart) ! - THEN - dup I + c@ (newline?) IF - I (strend) ! - THEN - (strend) @ -1 <> IF - s" Server: " sendbuffer-append - dup (strstart) @ (strend) @ rot (>str) sendbuffer-append - s\" \n" sendbuffer-append - -1 (strstart) ! - -1 (strend) ! - THEN - LOOP drop ; + motd-cached @ IF + motd-cache motd-cache-length @ sendbuffer-append + EXIT + THEN + motd@ ['] (sendbuffer-motd-line-append) parse-motd + sendbuffer@ dup motd-cache-length ! motd-cache swap move ; : (prepare-empty-line) ( -- ) sendbuffer-reset s\" Server: \n" sendbuffer-append ; : (prepare-identity) ( connection-addr -- ) sendbuffer-reset - s\" Server: You are now known as \"Anon " sendbuffer-append - (connection.number>string) sendbuffer-append - s\" \".\n" sendbuffer-append ; + s\" Server: You are now known as \"" sendbuffer-append + (connection>name) sendbuffer-append + s\" \".\n" sendbuffer-append ; : server-connection-new ( connection-addr eventid-n -- ) drop ." New client connected!" cr dup (prepare-motd) (send-sendbuffer) @@ -265,8 +395,9 @@ variable (strend) : server-commandline ( eventdata-n eventid-n -- ) 2drop commandline-ready? IF - space commandline-getline ['] evaluate catch dup 0= IF - drop ." ok" + commandline-getline 2dup logger.log cr + ['] evaluate catch dup 0= IF + drop ELSE ." error code: " . 2drop THEN @@ -285,6 +416,129 @@ variable (strend) I connections.at (send-sendbuffer) LOOP ; +: user-help ( -- ) + ." User commands: " cr + ." help ( -- ) \ this help command" cr + ." users ( -- ) \ display the connected users" cr + ." whoami ( -- ) \ display your name" cr ; + +: user-users ( -- ) + connections.count 0= IF + EXIT + THEN + connections.count 0 DO + I connections.at connection.connected @ IF + ." Anon " I connections.at (connection.number>string) type cr + THEN + LOOP ." TODO: implement last active time." cr ; + +: user-whoami ( connection-addr -- ) + ." You are Anon " (connection.number>string) type ." ." cr ; + +' user-help IS user-command-help +' user-users IS user-command-users +' user-whoami IS user-command-whoami + +: server-commands ( -- ) +\ List server commands. + ." Server commands: " cr cr + ." You may enter any valid forth expression" cr cr + ." server-commands ( -- ) \ this help command" cr + ." server-admin ( user-n -- ) \ make a user admin" cr + ." server-users ( -- ) \ list connected users" cr + ." server-accept ( flag -- ) \ accept new connections" cr + ." server-accepting? ( -- ) \ check if the server is" cr + ." \ accepting connections" cr + ." server-disconnect ( user-n -- ) \ disconnect a user by closing the circuit" cr + ." server-broadcast ( msg-str -- ) \ broadcast a server message to" cr + ." \ all users" cr + ." server-message ( msg-str user-n -- ) \ send a server message to" cr + ." \ a specific user" cr +; + +: help ( -- ) server-commands ; + +: (userid>connection) ( user-n -- connection-addr ) + 1- connections.at ; + +: server-admin ( user-n -- ) + (userid>connection) connection.admin true swap ! ; + +: server-users ( -- ) + connections.count 0= IF + ." No connected users." cr + EXIT + THEN + connections.count 0 DO + I connections.at dup connection.connected @ IF + dup ." Anon " (connection.number>string) type + ." CircuitID " connection.circuitid @ . cr + ELSE + drop + THEN + LOOP ; + +: server-accept ( flag -- ) + dup accept-connections ! IF + ." Server is set to accept new connections." cr + ELSE + ." Server is set to not accept new connections." cr + THEN ; + +: server-accepting? ( -- ) + accept-connections @ IF + ." Server is currently accepting new connnections." cr + ELSE + ." Server is currently not accepting new connections." cr + THEN ; + +: server-disconnect ( user-n -- ) + (userid>connection) dup connection.connected @ IF + connection.circuitid @ torcontrol-close-circuit + ." Tor circuit closed." cr + ELSE + drop ." User not connected." cr + THEN ; + +create broadcast-parser PARSER_SIZE allot +: (nextline) ( -- line-str flag ) + s\" \n" parser>>string IF + parser-extract 1 parser>> + parser-mark true + ELSE + parser-remaining 2dup nip 0> IF + parser>>| true + ELSE + false + THEN + THEN ; +: server-broadcast ( msg-str -- ) + connections.count 0= IF + EXIT + THEN + broadcast-parser new-parser + BEGIN + (nextline) + WHILE + s" Server" (format-sendbuffer) + connections.count 0 DO + I connections.at dup connection.connected @ IF + (send-sendbuffer) + ELSE + drop + THEN + LOOP + REPEAT 2drop ; +: (assert-connected) ( connection-addr -- ) + connection.connected @ invert abort" Not connected" ; +: server-message ( msg-str user-n -- ) + >r 2dup type + s" Server" (format-sendbuffer) + r> (userid>connection) dup (assert-connected) + (send-sendbuffer) ; + +' server-broadcast redirect-broadcast-xt ! + ' server-idle-accept EVENT_IDLE eventhandlers.append ' server-idle-recv EVENT_IDLE eventhandlers.append ' server-connection-new EVENT_CONNECTION_NEW eventhandlers.append diff --git a/server/eventloop-server-experiment/stdout-hook.4th b/server/eventloop-server-experiment/stdout-hook.4th new file mode 100644 index 0000000..77503d9 --- /dev/null +++ b/server/eventloop-server-experiment/stdout-hook.4th @@ -0,0 +1,66 @@ +require util.4th +require logger.4th + +\ The standard output will only be redirected in application code, +\ not globally in gforth. + +\ We always log to a file but we have an optional hook. + +true variable! (stdout) +true variable! (stdout-logger) +true variable! (stdout-hook) + +: oldtype type ; +: oldemit emit ; + +defer (emit) +defer (type) + +: type ( str -- ) + (stdout) @ IF 2dup oldtype THEN + (stdout-logger) @ IF 2dup logger.log THEN + (stdout-hook) @ IF 2dup (type) THEN + 2drop ; + +create (emit-buffer) 1 chars allot +: emit ( c -- ) + (emit-buffer) c! (emit-buffer) 1 chars type ; + +: ." ( "str" -- ) + [char] " parse + state @ IF + ]] sliteral type [[ + ELSE + type + THEN ; immediate +: space ( -- ) bl emit ; +: cr ( -- ) 10 emit ; +: . ( n -- ) + to-string type bl emit ; +: .s ( -- ) + ." < " depth . ." > " + depth 0> IF + depth 0 + BEGIN 2dup > WHILE 1+ rot >r REPEAT + drop 0 + BEGIN 2dup > WHILE 1+ r> dup . -rot REPEAT + 2drop + THEN ; + +: stdout ( flag -- ) (stdout) ! ; +: stdout-logger ( flag -- ) (stdout-logger) ! ; +: stdout-hook ( flag -- ) (stdout-hook) ! ; + +: stdout-hook-reset ( -- ) + ['] drop IS (emit) + ['] 2drop is (type) ; + +: stdout-hook-emit ( xt -- ) +\ xt ( c -- ) + is (emit) ; + +: stdout-hook-type ( xt -- ) +\ xt ( str -- ) + is (type) ; + +stdout-hook-reset diff --git a/server/eventloop-server-experiment/torcontrol-constants.4th b/server/eventloop-server-experiment/torcontrol-constants.4th new file mode 100644 index 0000000..cac1e19 --- /dev/null +++ b/server/eventloop-server-experiment/torcontrol-constants.4th @@ -0,0 +1,2 @@ +0 constant TOR_CONTROL_AUTHMETHOD_NULL +1 constant TOR_CONTROL_AUTHMETHOD_COOKIE diff --git a/server/eventloop-server-experiment/torcontrol.4th b/server/eventloop-server-experiment/torcontrol.4th new file mode 100644 index 0000000..6e1d660 --- /dev/null +++ b/server/eventloop-server-experiment/torcontrol.4th @@ -0,0 +1,97 @@ +\ Simple torcontrol interface that only supports closing circuits. +\ We only support the authcookie authentication. We can retrieve the authcookie +\ file location by doing the following: +\ +\ telnet localhost 9051 +\ PROTOCOLINFO +\ +\ The user that this server is running under must have permission to read +\ the tor cookie file. On Debian the user must be added to the debian-tor group. +\ +\ TODO: write a proper client for this? +\ TODO: at least check for success responses? +\ TODO: we only support ipv4 for now + +require unix/socket.fs + +require util.4th +require extensions.4th + +512 constant TORCONTROL_SENDBUFFER_SIZE +512 constant TORCONTROL_RECVBUFFER_SIZE + 32 constant TORCONTROL_COOKIE_FILESIZE + 64 constant TORCONTROL_COOKIE_SIZE + + CONFIG_TOR_CONTROL_ADDR constant TORCONTROL_ADDR + CONFIG_TOR_CONTROL_PORT constant TORCONTROL_PORT +CONFIG_TOR_CONTROL_COOKIE_FILEPATH sconstant TORCONTROL_COOKIE_FILEPATH + +create torcontrol-cookie TORCONTROL_COOKIE_SIZE allot +create torcontrol-sendbuffer TORCONTROL_SENDBUFFER_SIZE allot +create torcontrol-recvbuffer TORCONTROL_RECVBUFFER_SIZE allot + +CONFIG_TOR_CONTROL_AUTHMETHOD TOR_CONTROL_AUTHMETHOD_COOKIE = [IF] + variable (file) + : (zero-prefix) ( c -- str ) + 16 < IF s" 0" ELSE 0 0 THEN ; + : (byte>hex) ( c -- str ) + hex to-string decimal ; + : (binarycookie>hexcookie) ( binary-str -- ) + s" " pad place + over + swap DO + I c@ dup + (zero-prefix) pad +place + (byte>hex) pad +place + LOOP + pad count torcontrol-cookie swap move ; + : torcontrol-load-cookie ( str -- ) + r/o open-file throw (file) ! + torcontrol-recvbuffer TORCONTROL_COOKIE_FILESIZE (file) @ read-file abort" torcontrol read failed" + TORCONTROL_COOKIE_FILESIZE <> abort" torcontrol read failed." + torcontrol-recvbuffer TORCONTROL_COOKIE_FILESIZE (binarycookie>hexcookie) + (file) @ close-file abort" torcontrol close-file failed" ; + + TORCONTROL_COOKIE_FILEPATH torcontrol-load-cookie +[THEN] + +variable (tcsocket) +variable (tcsendbuffer-len) +create (tcsaddr) /sockaddr_in alloterase +: (reset) ( -- ) 0 (tcsendbuffer-len) ! ; +: (append) ( str -- ) + dup >r torcontrol-sendbuffer (tcsendbuffer-len) @ + swap move + r> (tcsendbuffer-len) +! ; +: (sendbuffer@) ( -- str ) + torcontrol-sendbuffer (tcsendbuffer-len) @ ; +: (cookie) ( -- str ) torcontrol-cookie TORCONTROL_COOKIE_SIZE ; +: (lf) ( -- str ) s\" \r\n" ; +: torcontrol-close-circuit ( circuit-id-n -- ) + (reset) + CONFIG_TOR_CONTROL_AUTHMETHOD CASE + TOR_CONTROL_AUTHMETHOD_NULL OF + s" AUTHENTICATE " (append) (lf) (append) + ENDOF + TOR_CONTROL_AUTHMETHOD_COOKIE OF + s" AUTHENTICATE " (append) (cookie) (append) (lf) (append) + ENDOF + ." unknown auth method with id " . abort + ENDCASE + + s" CLOSECIRCUIT " (append) to-string (append) (lf) (append) + S" QUIT" (append) (lf) (append) + + AF_INET SOCK_STREAM 0 socket (tcsocket) ! + + TORCONTROL_PORT htons (tcsaddr) port w! + TORCONTROL_ADDR (tcsaddr) sin_addr l! + AF_INET (tcsaddr) family w! + (tcsocket) @ (tcsaddr) /sockaddr_in connect 0<> abort" connect failed" + (tcsocket) @ torcontrol-sendbuffer (tcsendbuffer-len) @ 0 send (tcsendbuffer-len) @ <> abort" send failed" + BEGIN + (tcsocket) @ torcontrol-recvbuffer TORCONTROL_RECVBUFFER_SIZE 0 recv +\ dup 0> IF +\ torcontrol-recvbuffer over type +\ THEN + 0= + UNTIL + (tcsocket) @ close 0<> abort" close failed" ; diff --git a/server/eventloop-server-experiment/util.4th b/server/eventloop-server-experiment/util.4th new file mode 100644 index 0000000..97d8aee --- /dev/null +++ b/server/eventloop-server-experiment/util.4th @@ -0,0 +1,25 @@ + +: sconstant ( "name" str -- ) + 2>r : 2r> postpone sliteral postpone ; ; + +: variable! ( "name" value-n -- ) + create , ; + +: alloterase ( n -- ) + here over allot swap erase ; + +: 3dup ( a b c -- a b c a b c ) + >r 2dup r@ -rot r> ; + +: 3drop ( a b c -- ) + 2drop drop ; + +: to-string ( n -- str ) + s>d <# #s #> ; + +: startswith ( str prefix-str -- flag ) + 2>r over swap 2r> search IF + drop = + ELSE + 3drop false + THEN ;