Advertisement
Guest User

doorparty-rlogin-proxy.js

a guest
Oct 3rd, 2016
104
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.38 KB | None | 0 0
  1. /* DoorParty RLogin Proxy Service for Synchronet BBS
  2. echicken@bbs.electronicchicken.com, October 2016
  3.  
  4. This service listens for connections from RLogin clients, then ensures that
  5. an account exists for the connecting user before sending them to the BBS.
  6.  
  7. Setup:
  8.  
  9. Place a copy of this file in the 'mods' directory. (If you don't have
  10. a 'mods' directory, create one at the top level of your Synchronet BBS
  11. installation, alongside 'exec', 'ctrl', 'data', etc.)
  12.  
  13. Shut down the BBS.
  14.  
  15. Edit ctrl/sbbs.ini, and change the RLoginPort value in the [BBS]
  16. section to 10513.
  17.  
  18. I would also advise adding the NO_HOST_LOOKUP option to the Options
  19. value in the [BBS] section of sbbs.ini. This can speed up the login
  20. process and make the connection to your server closer to seamless.
  21.  
  22. Edit ctrl/services.ini, and add the following section at the bottom:
  23.  
  24. [RLoginProxy]
  25. Port=513
  26. Options=NO_HOST_LOOKUP
  27. Command=doorparty-rlogin-proxy.js
  28.  
  29. Start the BBS back up again.
  30.  
  31. Notes:
  32.  
  33. RLogin clients send three strings immediately after connecting: the
  34. local username, the remote username, and the terminal type & speed. None
  35. of these fields was actually meant to carry a password, but occasionally
  36. that's what they're used for.
  37.  
  38. Some clients (SyncTERM) send the password first, then the username, then
  39. the terminal type & speed; other clients may send the username first and
  40. the password second. The server needs to know which field is which. If
  41. the alias that's being set for a new user is not what you're expecting,
  42. try swapping the values of the USERNAME_FIELD and PASSWORD_FIELD
  43. settings below.
  44.  
  45. The 'password' sent by the RLogin client is being ignored and is not
  46. being compared with the user's password on this BBS (if they already
  47. exist here). What I've gleaned from the DoorParty wiki suggests this is
  48. appropriate, and that inbound RLogin connections are 'trusted' because
  49. they come from authenticated systems via an SSH tunnel.
  50.  
  51. A different random password is being generated for each new user and is
  52. not based on anything that was sent by the RLogin client.
  53.  
  54. If you find that the BBS is still prompting new users for more info when
  55. they first connect, this script can be adjusted to populate more user
  56. data fields so that these questions appear to have been answered.
  57.  
  58. I have commented the shit out of this script for the benefit of anyone
  59. who needs to work on it in the future, including myself.
  60.  
  61. If you're not maskreet and you've stumbled upon this script by accident,
  62. I would not advise setting it up on the Synchronet BBS that you may or
  63. may not operate. This script serves a very specific purpose for an
  64. uncommon situation and would not be wise to run on a 'normal' BBS.
  65.  
  66. */
  67.  
  68. // Configuration
  69. const RLOGIN_PORT = 10513; // The port that your actual RLogin server is on
  70. const PROXY_PORT = 513; // The port that you want this server to listen on
  71. const LOGIN_TIMEOUT = 30; // Seconds to wait for RLogin client details
  72. const USERNAME_FIELD = 1; // '1' if clients send username first, '2' otherwise
  73. const PASSWORD_FIELD = 2; // '1' if clients send password first, '2' otherwise
  74. const MAX_READ = 512; // This probably doesn't need to be changed, but you
  75. // can tweak it up or down and see if it reduces lag
  76. // or improves responsiveness.
  77.  
  78. // Don't edit below this line.
  79. load('sbbsdefs.js');
  80.  
  81. // If running via jsexec, set up a server and wait for a client
  82. if (typeof js.global.server === 'undefined') {
  83. load('sockdefs.js');
  84. var s = {};
  85. s.socket = new Socket(SOCK_STREAM, 'RLOGIN-PROXY');
  86. s.socket.bind(PROXY_PORT);
  87. s.socket.listen();
  88. var client = s.socket.accept();
  89. }
  90. // However this was launched, 'client' should now be our client's Socket object
  91.  
  92. // Global constants
  93. const NUL = ascii(0);
  94. const START_TIME = time();
  95. const STATE_INIT_CLIENT = 0;
  96. const STATE_INIT_USER = 1;
  97. const STATE_INIT_PROXY = 2;
  98. const STATE_PROXY = 3;
  99.  
  100. // Global variables
  101. var proxy, username, password, terminal;
  102. var state = STATE_INIT_CLIENT, from_client = '';
  103.  
  104. // Crappy but good enough
  105. function generatePassword(len) {
  106. var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'.split('');
  107. var pass = '';
  108. while (pass.length < len) {
  109. pass += chars[Math.floor(Math.random() * chars.length)];
  110. }
  111. return pass;
  112. }
  113.  
  114. // The client has just connected, and we're waiting for their initial string
  115. while (state === STATE_INIT_CLIENT && !js.terminated && client.socket.is_connected) {
  116.  
  117. try {
  118.  
  119. if (client.socket.nread > 0) from_client += client.socket.recv(MAX_READ);
  120.  
  121. var matches = from_client.split(NUL);
  122.  
  123. // If we've received four null-terminated values, parse them
  124. if (matches.length >= 4) {
  125.  
  126. username = matches[USERNAME_FIELD];
  127. password = matches[PASSWORD_FIELD];
  128. terminal = matches[3];
  129.  
  130. from_client = undefined; // We don't need this anymore
  131. client.socket.sendBin(0, 1); // Acknowledge that we got the client's data
  132.  
  133. // Proceed to user lookup/creation
  134. state = STATE_INIT_USER;
  135.  
  136. log(
  137. LOG_INFO,
  138. format(
  139. '%s/%s/%s connected from %s',
  140. username, password, terminal, client.socket.remote_ip_address
  141. )
  142. );
  143.  
  144. // If we've been waiting too long for user details, throw an exception
  145. } else if (time() - START_TIME >= LOGIN_TIMEOUT) {
  146.  
  147. throw 'Login timeout exceeded.';
  148.  
  149. }
  150.  
  151. yield(); // Don't set the compooter on fire.
  152.  
  153. } catch (err) {
  154.  
  155. log(LOG_ERR, 'INIT_CLIENT: ' + err);
  156.  
  157. }
  158.  
  159. }
  160.  
  161. // If the client successfully initiated the session, find or create the user
  162. if (state === STATE_INIT_USER) {
  163.  
  164. try {
  165.  
  166. var un = system.matchuser(username); // Try to find the specified user
  167.  
  168. // If a match was found, grab the user's actual password
  169. if (un > 0) {
  170.  
  171. var u = new User(un);
  172.  
  173. // Otherwise create a new user and set the password
  174. } else {
  175.  
  176. var u = system.new_user(username);
  177. u.alias = username;
  178. u.name = username;
  179. u.handle = username;
  180. u.security.password = generatePassword(8);
  181. // We may need to populate other user data here if the BBS keeps asking questions upon connect
  182.  
  183. log(LOG_INFO, 'Created user #' + u.number + ', ' + u.alias);
  184.  
  185. }
  186.  
  187. password = u.security.password;
  188.  
  189. // Proceed with connecting to the RLogin server
  190. state = STATE_INIT_PROXY;
  191.  
  192. } catch (err) {
  193. log(LOG_ERR, 'INIT_USER: ' + err)
  194. }
  195.  
  196. }
  197.  
  198. // We now have an existing user to log in as; connect to the RLogin server
  199. if (state === STATE_INIT_PROXY) {
  200.  
  201. try {
  202.  
  203. proxy = new Socket();
  204. proxy.connect('127.0.0.1', RLOGIN_PORT);
  205.  
  206. // If we were able to connect to the RLogin server, send the user's info
  207. if (proxy.is_connected) {
  208.  
  209. proxy.send(NUL + password + NUL + username + NUL + terminal + NUL);
  210.  
  211. // Proceed to the proxying stage
  212. state = STATE_PROXY;
  213.  
  214. // If we were unable to connect to the RLogin server, throw an exception
  215. } else {
  216.  
  217. throw 'Failed to connect to RLogin server';
  218.  
  219. }
  220.  
  221. } catch (err) {
  222. log(LOG_ERR, 'INIT_PROXY: ' + err);
  223. }
  224.  
  225. }
  226.  
  227. // If we made it to STATE_PROXY, enter the proxy loop
  228. if (state === STATE_PROXY) {
  229.  
  230. try {
  231. // Pass traffic between the client and the rlogin server
  232. while (!js.terminated && client.socket.is_connected && proxy.is_connected) {
  233. if (proxy.nread > 0) client.socket.send(proxy.recv(MAX_READ));
  234. if (client.socket.nread > 0) proxy.send(client.socket.recv(MAX_READ));
  235. yield();
  236. }
  237. } catch (err) {
  238. log(LOG_ERR, 'PROXY: ' + err);
  239. }
  240.  
  241. }
  242.  
  243. // Clean up
  244. if (proxy && proxy.is_connected) proxy.close();
  245. if (client.socket.is_connected) client.socket.close();
  246. exit(0);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement