Moontalk server and client (provided by many parties)
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

551 rinda
16KB

  1. require unix/socket.fs
  2. require libs/xstring/xstring.4th
  3. require util.4th
  4. require extensions.4th
  5. require connections.4th
  6. require commandline.4th
  7. require motd.4th
  8. require motd-parser.4th
  9. require proxyline-parser.4th
  10. require torcontrol.4th
  11. require dos.4th
  12. require sendbuffer.4th
  13. AF_INET constant SERVER_SOCKET_DOMAIN
  14. SOCK_STREAM
  15. SOCK_NONBLOCK or
  16. constant SERVER_SOCKET_TYPE
  17. 0 constant SERVER_SOCKET_PROTOCOL
  18. 0 constant SERVER_ADDR
  19. CONFIG_SERVER_PORT constant SERVER_PORT
  20. 4 constant SERVER_LISTEN_BACKLOG
  21. \ Listening file descriptor.
  22. 0 variable! listenfd
  23. \ If we should accept new connections.
  24. true variable! accept-connections
  25. \ If we should echo back command responses.
  26. true variable! command-echo
  27. \ Idle detection.
  28. false variable! idle
  29. : server-idle? ( -- flag ) idle @ ;
  30. : server-idle! ( flag -- ) idle ! ;
  31. \ Temporary structs.
  32. create con /CONNECTION allot
  33. create saddr /sockaddr_in allot
  34. create optval /option_value allot
  35. : (assert-setsockopt) ( result-n -- )
  36. 0< abort" making socket reusable failed" ;
  37. : (optval-true!) ( -- )
  38. 1 optval l! ;
  39. : (make-reusable) ( socket-fd-n -- )
  40. (optval-true!)
  41. SOL_SOCKET SO_REUSEADDR optval /option_value setsockopt (assert-setsockopt) ;
  42. : (saddr!) ( protocol-n sin_addr-n port-n -- )
  43. htons saddr port w!
  44. saddr sin_addr l!
  45. saddr family w! ;
  46. : (assert-socket) ( result-n -- result-n )
  47. dup 0< abort" socket failed." ;
  48. : (assert-bind) ( result-n -- )
  49. 0< abort" bind failed." ;
  50. : (assert-listen) ( result-n -- )
  51. 0< abort" listen failed." ;
  52. : (erase-saddr) ( -- )
  53. saddr /sockaddr_in erase ;
  54. : (create-socket) ( -- )
  55. SERVER_SOCKET_DOMAIN
  56. SERVER_SOCKET_TYPE
  57. SERVER_SOCKET_PROTOCOL
  58. socket (assert-socket) listenfd ! ;
  59. : (make-socket-reusable) ( -- )
  60. listenfd @ (make-reusable) ;
  61. : (set-saddr) ( -- )
  62. SERVER_SOCKET_DOMAIN SERVER_ADDR SERVER_PORT (saddr!) ;
  63. : (bind-socket) ( -- )
  64. listenfd @ saddr /sockaddr_in bind (assert-bind) ;
  65. : (listen-socket) ( -- )
  66. listenfd @ SERVER_LISTEN_BACKLOG listen() (assert-listen) ;
  67. : (server-info) ( -- )
  68. cr cr ." Server listening at port: " SERVER_PORT . cr ;
  69. : initialize-server ( -- )
  70. (erase-saddr)
  71. (create-socket)
  72. (make-socket-reusable)
  73. (set-saddr)
  74. (bind-socket)
  75. (listen-socket)
  76. (server-info) ;
  77. : (perform-disconnect) ( connection-addr -- )
  78. dup connection.circuitid @ 0<> IF
  79. dup connections.indexOf dos-remove-connection
  80. THEN
  81. dup connection.connected false swap !
  82. connection.fd @ close throw ;
  83. : (close-clients) ( -- )
  84. connections.count 0= IF
  85. EXIT
  86. THEN
  87. connections.count 0 DO
  88. I connections.at connection.connected @ true = IF
  89. I connections.at (perform-disconnect)
  90. THEN
  91. LOOP ;
  92. : (assert-close) ( result-n -- )
  93. 0<> abort" close failed" ;
  94. : (close-server) ( -- )
  95. listenfd @ close (assert-close) ;
  96. : (close-server-info) ( -- )
  97. cr ." Closed server connections." cr ;
  98. : close-server ( -- )
  99. (close-clients) (close-server) (close-server-info) ;
  100. : (queue-disconnect) ( connection-addr -- )
  101. dup (perform-disconnect) EVENT_CONNECTION_CLOSED events.enqueue ;
  102. : (try-accept) ( -- c-result-n )
  103. listenfd @ 0 0 accept() ;
  104. : (accept-error) ( accept-result-n -- )
  105. errno EAGAIN <> abort" accept error" drop ;
  106. : (erase-connection) ( connection-addr -- )
  107. /CONNECTION erase ;
  108. : (set-connection-number) ( -- )
  109. connections.last dup connections.indexOf 1+ swap connection.number ! ;
  110. : (enqueue-new-connection) ( -- )
  111. connections.last EVENT_CONNECTION_NEW events.enqueue ;
  112. : (store-connection) ( connection-addr -- )
  113. dup >r connections.append IF
  114. (set-connection-number)
  115. (enqueue-new-connection)
  116. rdrop
  117. ELSE
  118. ." Warning: failed to store connection, disconnecting client." cr
  119. r> (perform-disconnect)
  120. THEN ;
  121. : (con!) ( fd-n connected-flag -- )
  122. con connection.connected !
  123. con connection.fd ! ;
  124. : (accept-connection) ( fd-n -- )
  125. con (erase-connection)
  126. true (con!)
  127. con (store-connection) ;
  128. : (server-idle-accept) ( -- )
  129. accept-connections @ invert IF
  130. EXIT
  131. THEN
  132. (try-accept) dup 0< IF
  133. (accept-error)
  134. ELSE
  135. (accept-connection)
  136. THEN ;
  137. : (connected?) ( connection-addr -- flag )
  138. connection.connected @ ;
  139. : (try-recv) ( connection-addr -- recv-result-n )
  140. dup connection.fd @
  141. swap connection.buffer
  142. CONNECTION_BUFFER_SIZE
  143. MSG_DONTWAIT
  144. recv ;
  145. : (recv-error?) ( c-result-n -- flag ) 0< ;
  146. : (recv) ( recv-result-n connection-addr -- )
  147. 2dup connection.bufferlen ! swap
  148. 0> IF
  149. EVENT_CONNECTION_RECV events.enqueue
  150. ELSE \ disconnected
  151. (queue-disconnect)
  152. THEN ;
  153. : (recv-warning) ( -- ) ." Warning: recv failed, disconnecting client." cr ;
  154. : (recv-error) ( recv-result-n connection-addr -- )
  155. errno EAGAIN <> IF
  156. (queue-disconnect) drop
  157. (recv-warning)
  158. ELSE
  159. 2drop
  160. THEN ;
  161. : (connection-recv) ( connection-addr -- )
  162. dup (try-recv) tuck (recv-error?) IF
  163. (recv-error)
  164. ELSE
  165. (recv)
  166. THEN ;
  167. : (server-idle-recv) ( -- )
  168. true server-idle!
  169. connections.count 0= IF
  170. EXIT
  171. THEN
  172. connections.count 0 DO
  173. I connections.at dup (connected?) IF
  174. (connection-recv)
  175. ELSE
  176. drop
  177. THEN
  178. LOOP ;
  179. : (connection.number>string) ( connection-addr -- c-addr u )
  180. connection.number @ to-string ;
  181. : (connection.buffer>string) ( connection-addr -- c-addr u )
  182. dup connection.buffer swap connection.bufferlen @ ;
  183. : (connection>name) ( connection-addr -- c-addr u )
  184. s" Anon " pad place
  185. (connection.number>string) pad +place
  186. pad count ;
  187. : (expect-proxyline?) ( connection-addr -- flag )
  188. connection.circuitid @ 0= ;
  189. : (parse-proxyline) ( connection-addr -- )
  190. dup >r (connection.buffer>string) proxyline>circuitid
  191. dup r@ connection.bufferlen ! r@ connection.buffer swap move
  192. r> connection.circuitid ! ;
  193. : (last-sendbuffer-char) ( -- c )
  194. sendbuffer@ + 1- c@ ;
  195. : (maybe-append-newline) ( -- )
  196. (last-sendbuffer-char) 10 <> IF
  197. s\" \n" sendbuffer-append
  198. THEN ;
  199. : (format-sendbuffer) ( msg-str from-str -- )
  200. sendbuffer-reset
  201. sendbuffer-append
  202. s" : " sendbuffer-append
  203. sendbuffer-append
  204. (maybe-append-newline)
  205. sendbuffer-sanitize ;
  206. : (connected?) ( connection-addr -- )
  207. connection.connected @ ;
  208. : (different-connection?) ( from-connection-addr to-connection-addr -- )
  209. <> ;
  210. : (send?) ( from-connection-addr to-connection-addr -- )
  211. tuck (different-connection?) swap (connected?) and ;
  212. : (check-send) ( result-n -- )
  213. 0< IF ." Warning: send failed." cr THEN ;
  214. : (send-sendbuffer) ( to-connection-addr -- )
  215. connection.fd @ sendbuffer@ MSG_NOSIGNAL send (check-send) ;
  216. : (try-send) ( from-connection-addr to-connection-addr -- )
  217. 2dup (send?) IF
  218. nip (send-sendbuffer)
  219. ELSE
  220. 2drop
  221. THEN ;
  222. : (dos-update-stats) ( from-connection-addr -- )
  223. dup connections.indexOf
  224. swap (connection.buffer>string) nip over dos-add-bytes
  225. 1 swap dos-add-lines ;
  226. : (dos-protect?) ( connection-addr -- flag )
  227. connections.indexOf dos? ;
  228. : (dos-protect) ( connection-addr -- )
  229. ." DOS protection enabled for circuit:" cr
  230. dup connections.indexOf .dos-info
  231. dup connections.indexOf true swap dos-handled!
  232. connection.circuitid @ torcontrol-close-circuit ;
  233. : (is-command?) ( str -- flag )
  234. 1 min s" /" compare 0= ;
  235. create command-parser PARSER_SIZE allot
  236. : (extract-command) ( str -- str )
  237. command-parser new-parser 1 parser>> parser-remaining ;
  238. : (parse-command) ( str -- str flag )
  239. 2dup (is-command?) IF
  240. (extract-command) true
  241. ELSE
  242. false
  243. THEN ;
  244. 4096 constant REDIRECT_BUFFER_SIZE
  245. create server-redirect-buffer REDIRECT_BUFFER_SIZE allot
  246. create server-emit-buffer 1 chars allot
  247. variable redirect-broadcast-xt
  248. : (server-redirect-reset) ( -- )
  249. s" " server-redirect-buffer xplace ;
  250. : (server-redirect-flush) ( -- )
  251. server-redirect-buffer xcount redirect-broadcast-xt @ execute
  252. (server-redirect-reset) ;
  253. : (server-type) ( str -- )
  254. \ overflow check
  255. dup cell + server-redirect-buffer xcount nip + REDIRECT_BUFFER_SIZE <= IF
  256. server-redirect-buffer +xplace
  257. ELSE
  258. 2drop
  259. THEN ;
  260. : (server-emit) ( c -- )
  261. server-emit-buffer c!
  262. server-emit-buffer 1 chars (server-type) ;
  263. : (enable-redirect) ( -- )
  264. ['] (server-emit) stdout-hook-emit
  265. ['] (server-type) stdout-hook-type
  266. (server-redirect-reset) ;
  267. : (disable-redirect) ( -- )
  268. (server-redirect-flush)
  269. stdout-hook-reset ;
  270. : (depth-evaluate) ( command-str -- )
  271. depth 2 - >r
  272. ['] evaluate catch IF
  273. 2drop ." An error has occured." cr
  274. THEN
  275. depth r> <> abort" aborting to fix stack." ;
  276. : (dispatch-admin-command) ( connection-addr command-str -- flag )
  277. rot connection.admin @ IF
  278. ['] (depth-evaluate) catch IF 2drop THEN true
  279. ELSE
  280. 2drop false
  281. THEN ;
  282. \ TODO: user command dispatching is very basic for now
  283. \ TODO: maybe make commands extendible at runtime?
  284. defer user-command-help ( -- )
  285. defer user-command-users ( -- )
  286. defer user-command-whoami ( connection-addr -- )
  287. ' noop is user-command-help
  288. ' noop is user-command-users
  289. ' drop is user-command-whoami
  290. : (dispatch-user-command) ( connection-addr command-str -- )
  291. 2dup s" help" startswith IF
  292. 3drop user-command-help
  293. ELSE 2dup s" users" startswith IF
  294. 3drop user-command-users
  295. ELSE 2dup s" whoami" startswith IF
  296. 2drop user-command-whoami
  297. ELSE
  298. 3drop ." Unknown user command." cr
  299. THEN THEN THEN ;
  300. : (handle-command) ( connection-addr -- )
  301. dup (connection.buffer>string) (parse-command) IF
  302. (enable-redirect)
  303. 3dup (dispatch-admin-command) IF
  304. 3drop
  305. ELSE
  306. (dispatch-user-command)
  307. THEN
  308. (disable-redirect)
  309. ELSE
  310. 2drop drop
  311. THEN ;
  312. : (handle-broadcast) ( connection-addr -- )
  313. dup >r (connection.buffer>string) r@ (connection>name) (format-sendbuffer)
  314. r> (dos-update-stats)
  315. sendbuffer@ type
  316. connections.count 0 DO
  317. dup I connections.at (try-send)
  318. LOOP ;
  319. : server-recv ( from-connection-addr eventid-n )
  320. drop
  321. dup (expect-proxyline?) IF
  322. dup (parse-proxyline)
  323. dup connection.circuitid @ over connections.indexOf dos-add-connection
  324. dup (connection.buffer>string) nip 0= IF
  325. drop EXIT
  326. THEN
  327. THEN
  328. dup connections.indexOf dos-handled? IF
  329. drop EXIT
  330. THEN
  331. dup (dos-protect?) IF
  332. (dos-protect)
  333. ELSE
  334. dup (handle-broadcast)
  335. (handle-command)
  336. THEN ;
  337. : server-idle-accept ( eventdata-n eventid-n -- )
  338. 2drop (server-idle-accept) ;
  339. : server-idle-recv ( eventdata-n eventid-n -- )
  340. 2drop (server-idle-recv) ;
  341. false variable! motd-cached
  342. create motd-cache SENDBUFFER_SIZE allot
  343. 0 variable! motd-cache-length
  344. : (sendbuffer-motd-line-append) ( str -- )
  345. s" Server: " sendbuffer-append
  346. sendbuffer-append
  347. s\" \n" sendbuffer-append ;
  348. : (prepare-motd) ( -- )
  349. sendbuffer-reset
  350. motd-cached @ IF
  351. motd-cache motd-cache-length @ sendbuffer-append
  352. EXIT
  353. THEN
  354. motd@ ['] (sendbuffer-motd-line-append) parse-motd
  355. sendbuffer@ dup motd-cache-length ! motd-cache swap move ;
  356. : (prepare-empty-line) ( -- )
  357. sendbuffer-reset s\" Server: \n" sendbuffer-append ;
  358. : (prepare-identity) ( connection-addr -- )
  359. sendbuffer-reset
  360. s\" Server: You are now known as \"" sendbuffer-append
  361. (connection>name) sendbuffer-append
  362. s\" \".\n" sendbuffer-append ;
  363. : server-connection-new ( connection-addr eventid-n -- )
  364. drop ." New client connected!" cr
  365. dup (prepare-motd) (send-sendbuffer)
  366. dup (prepare-empty-line) (send-sendbuffer)
  367. dup (prepare-identity)
  368. (send-sendbuffer) ;
  369. : server-connection-closed ( connection-addr eventid-n -- )
  370. 2drop ." Client disconnected." cr ;
  371. : server-commandline ( eventdata-n eventid-n -- )
  372. 2drop commandline-ready? IF
  373. commandline-getline 2dup logger.log cr
  374. ['] evaluate catch dup 0= IF
  375. drop
  376. ELSE
  377. ." error code: " . 2drop
  378. THEN
  379. cr
  380. commandline-reset
  381. ELSE
  382. commandline-key? IF
  383. commandline-key commandline-handlekey
  384. commandline-redraw
  385. THEN
  386. THEN ;
  387. : server-motd-changed ( eventdata-n eventid-n -- )
  388. 2drop (prepare-motd)
  389. connections.count 0 DO
  390. I connections.at (send-sendbuffer)
  391. LOOP ;
  392. : user-help ( -- )
  393. ." User commands: " cr
  394. ." help ( -- ) \ this help command" cr
  395. ." users ( -- ) \ display the connected users" cr
  396. ." whoami ( -- ) \ display your name" cr ;
  397. : user-users ( -- )
  398. connections.count 0= IF
  399. EXIT
  400. THEN
  401. connections.count 0 DO
  402. I connections.at connection.connected @ IF
  403. ." Anon " I connections.at (connection.number>string) type cr
  404. THEN
  405. LOOP ." TODO: implement last active time." cr ;
  406. : user-whoami ( connection-addr -- )
  407. ." You are Anon " (connection.number>string) type ." ." cr ;
  408. ' user-help IS user-command-help
  409. ' user-users IS user-command-users
  410. ' user-whoami IS user-command-whoami
  411. : server-commands ( -- )
  412. \ List server commands.
  413. ." Server commands: " cr cr
  414. ." You may enter any valid forth expression" cr cr
  415. ." server-commands ( -- ) \ this help command" cr
  416. ." server-admin ( user-n -- ) \ make a user admin" cr
  417. ." server-users ( -- ) \ list connected users" cr
  418. ." server-accept ( flag -- ) \ accept new connections" cr
  419. ." server-accepting? ( -- ) \ check if the server is" cr
  420. ." \ accepting connections" cr
  421. ." server-disconnect ( user-n -- ) \ disconnect a user by closing the circuit" cr
  422. ." server-broadcast ( msg-str -- ) \ broadcast a server message to" cr
  423. ." \ all users" cr
  424. ." server-message ( msg-str user-n -- ) \ send a server message to" cr
  425. ." \ a specific user" cr
  426. ;
  427. : help ( -- ) server-commands ;
  428. : (userid>connection) ( user-n -- connection-addr )
  429. 1- connections.at ;
  430. : server-admin ( user-n -- )
  431. (userid>connection) connection.admin true swap ! ;
  432. : server-users ( -- )
  433. connections.count 0= IF
  434. ." No connected users." cr
  435. EXIT
  436. THEN
  437. connections.count 0 DO
  438. I connections.at dup connection.connected @ IF
  439. dup ." Anon " (connection.number>string) type
  440. ." CircuitID " connection.circuitid @ . cr
  441. ELSE
  442. drop
  443. THEN
  444. LOOP ;
  445. : server-accept ( flag -- )
  446. dup accept-connections ! IF
  447. ." Server is set to accept new connections." cr
  448. ELSE
  449. ." Server is set to not accept new connections." cr
  450. THEN ;
  451. : server-accepting? ( -- )
  452. accept-connections @ IF
  453. ." Server is currently accepting new connnections." cr
  454. ELSE
  455. ." Server is currently not accepting new connections." cr
  456. THEN ;
  457. : server-disconnect ( user-n -- )
  458. (userid>connection) dup connection.connected @ IF
  459. connection.circuitid @ torcontrol-close-circuit
  460. ." Tor circuit closed." cr
  461. ELSE
  462. drop ." User not connected." cr
  463. THEN ;
  464. create broadcast-parser PARSER_SIZE allot
  465. : (nextline) ( -- line-str flag )
  466. s\" \n" parser>>string IF
  467. parser-extract 1 parser>>
  468. parser-mark true
  469. ELSE
  470. parser-remaining 2dup nip 0> IF
  471. parser>>| true
  472. ELSE
  473. false
  474. THEN
  475. THEN ;
  476. : server-broadcast ( msg-str -- )
  477. connections.count 0= IF
  478. EXIT
  479. THEN
  480. broadcast-parser new-parser
  481. BEGIN
  482. (nextline)
  483. WHILE
  484. s" Server" (format-sendbuffer)
  485. connections.count 0 DO
  486. I connections.at dup connection.connected @ IF
  487. (send-sendbuffer)
  488. ELSE
  489. drop
  490. THEN
  491. LOOP
  492. REPEAT 2drop ;
  493. : (assert-connected) ( connection-addr -- )
  494. connection.connected @ invert abort" Not connected" ;
  495. : server-message ( msg-str user-n -- )
  496. >r 2dup type
  497. s" Server" (format-sendbuffer)
  498. r> (userid>connection) dup (assert-connected)
  499. (send-sendbuffer) ;
  500. ' server-broadcast redirect-broadcast-xt !
  501. ' server-idle-accept EVENT_IDLE eventhandlers.append
  502. ' server-idle-recv EVENT_IDLE eventhandlers.append
  503. ' server-connection-new EVENT_CONNECTION_NEW eventhandlers.append
  504. ' server-connection-closed EVENT_CONNECTION_CLOSED eventhandlers.append
  505. ' server-recv EVENT_CONNECTION_RECV eventhandlers.append
  506. ' server-commandline EVENT_COMMANDLINE eventhandlers.append
  507. ' server-motd-changed EVENT_MOTD_CHANGED eventhandlers.append
  508. initialize-server