1 /*
2 * Rlogin backend.
3 */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <limits.h>
8 #include <ctype.h>
9
10 #include "putty.h"
11
12 #define RLOGIN_MAX_BACKLOG 4096
13
14 typedef struct Rlogin Rlogin;
15 struct Rlogin {
16 Socket *s;
17 bool closed_on_socket_error;
18 int bufsize;
19 bool firstbyte;
20 bool cansize;
21 int term_width, term_height;
22 Seat *seat;
23 LogContext *logctx;
24
25 Conf *conf;
26
27 /* In case we need to read a username from the terminal before starting */
28 prompts_t *prompt;
29
30 Plug plug;
31 Backend backend;
32 };
33
c_write(Rlogin * rlogin,const void * buf,size_t len)34 static void c_write(Rlogin *rlogin, const void *buf, size_t len)
35 {
36 size_t backlog = seat_stdout(rlogin->seat, buf, len);
37 sk_set_frozen(rlogin->s, backlog > RLOGIN_MAX_BACKLOG);
38 }
39
rlogin_log(Plug * plug,PlugLogType type,SockAddr * addr,int port,const char * error_msg,int error_code)40 static void rlogin_log(Plug *plug, PlugLogType type, SockAddr *addr, int port,
41 const char *error_msg, int error_code)
42 {
43 Rlogin *rlogin = container_of(plug, Rlogin, plug);
44 backend_socket_log(rlogin->seat, rlogin->logctx, type, addr, port,
45 error_msg, error_code,
46 rlogin->conf, !rlogin->firstbyte);
47 }
48
rlogin_closing(Plug * plug,const char * error_msg,int error_code,bool calling_back)49 static void rlogin_closing(Plug *plug, const char *error_msg, int error_code,
50 bool calling_back)
51 {
52 Rlogin *rlogin = container_of(plug, Rlogin, plug);
53
54 /*
55 * We don't implement independent EOF in each direction for Telnet
56 * connections; as soon as we get word that the remote side has
57 * sent us EOF, we wind up the whole connection.
58 */
59
60 if (rlogin->s) {
61 sk_close(rlogin->s);
62 rlogin->s = NULL;
63 if (error_msg)
64 rlogin->closed_on_socket_error = true;
65 seat_notify_remote_exit(rlogin->seat);
66 }
67 if (error_msg) {
68 /* A socket error has occurred. */
69 logevent(rlogin->logctx, error_msg);
70 seat_connection_fatal(rlogin->seat, "%s", error_msg);
71 } /* Otherwise, the remote side closed the connection normally. */
72 }
73
rlogin_receive(Plug * plug,int urgent,const char * data,size_t len)74 static void rlogin_receive(
75 Plug *plug, int urgent, const char *data, size_t len)
76 {
77 Rlogin *rlogin = container_of(plug, Rlogin, plug);
78 if (len == 0)
79 return;
80 if (urgent == 2) {
81 char c;
82
83 c = *data++;
84 len--;
85 if (c == '\x80') {
86 rlogin->cansize = true;
87 backend_size(&rlogin->backend,
88 rlogin->term_width, rlogin->term_height);
89 }
90 /*
91 * We should flush everything (aka Telnet SYNCH) if we see
92 * 0x02, and we should turn off and on _local_ flow control
93 * on 0x10 and 0x20 respectively. I'm not convinced it's
94 * worth it...
95 */
96 } else {
97 /*
98 * Main rlogin protocol. This is really simple: the first
99 * byte is expected to be NULL and is ignored, and the rest
100 * is printed.
101 */
102 if (rlogin->firstbyte) {
103 if (data[0] == '\0') {
104 data++;
105 len--;
106 }
107 rlogin->firstbyte = false;
108 }
109 if (len > 0)
110 c_write(rlogin, data, len);
111 }
112 }
113
rlogin_sent(Plug * plug,size_t bufsize)114 static void rlogin_sent(Plug *plug, size_t bufsize)
115 {
116 Rlogin *rlogin = container_of(plug, Rlogin, plug);
117 rlogin->bufsize = bufsize;
118 }
119
rlogin_startup(Rlogin * rlogin,const char * ruser)120 static void rlogin_startup(Rlogin *rlogin, const char *ruser)
121 {
122 char z = 0;
123 char *p;
124
125 sk_write(rlogin->s, &z, 1);
126 p = conf_get_str(rlogin->conf, CONF_localusername);
127 sk_write(rlogin->s, p, strlen(p));
128 sk_write(rlogin->s, &z, 1);
129 sk_write(rlogin->s, ruser, strlen(ruser));
130 sk_write(rlogin->s, &z, 1);
131 p = conf_get_str(rlogin->conf, CONF_termtype);
132 sk_write(rlogin->s, p, strlen(p));
133 sk_write(rlogin->s, "/", 1);
134 p = conf_get_str(rlogin->conf, CONF_termspeed);
135 sk_write(rlogin->s, p, strspn(p, "0123456789"));
136 rlogin->bufsize = sk_write(rlogin->s, &z, 1);
137
138 rlogin->prompt = NULL;
139 }
140
141 static const PlugVtable Rlogin_plugvt = {
142 .log = rlogin_log,
143 .closing = rlogin_closing,
144 .receive = rlogin_receive,
145 .sent = rlogin_sent,
146 };
147
148 /*
149 * Called to set up the rlogin connection.
150 *
151 * Returns an error message, or NULL on success.
152 *
153 * Also places the canonical host name into `realhost'. It must be
154 * freed by the caller.
155 */
rlogin_init(const BackendVtable * vt,Seat * seat,Backend ** backend_handle,LogContext * logctx,Conf * conf,const char * host,int port,char ** realhost,bool nodelay,bool keepalive)156 static char *rlogin_init(const BackendVtable *vt, Seat *seat,
157 Backend **backend_handle, LogContext *logctx,
158 Conf *conf, const char *host, int port,
159 char **realhost, bool nodelay, bool keepalive)
160 {
161 SockAddr *addr;
162 const char *err;
163 Rlogin *rlogin;
164 char *ruser;
165 int addressfamily;
166 char *loghost;
167
168 rlogin = snew(Rlogin);
169 rlogin->plug.vt = &Rlogin_plugvt;
170 rlogin->backend.vt = vt;
171 rlogin->s = NULL;
172 rlogin->closed_on_socket_error = false;
173 rlogin->seat = seat;
174 rlogin->logctx = logctx;
175 rlogin->term_width = conf_get_int(conf, CONF_width);
176 rlogin->term_height = conf_get_int(conf, CONF_height);
177 rlogin->firstbyte = true;
178 rlogin->cansize = false;
179 rlogin->prompt = NULL;
180 rlogin->conf = conf_copy(conf);
181 *backend_handle = &rlogin->backend;
182
183 addressfamily = conf_get_int(conf, CONF_addressfamily);
184 /*
185 * Try to find host.
186 */
187 addr = name_lookup(host, port, realhost, conf, addressfamily,
188 rlogin->logctx, "rlogin connection");
189 if ((err = sk_addr_error(addr)) != NULL) {
190 sk_addr_free(addr);
191 return dupstr(err);
192 }
193
194 if (port < 0)
195 port = 513; /* default rlogin port */
196
197 /*
198 * Open socket.
199 */
200 rlogin->s = new_connection(addr, *realhost, port, true, false,
201 nodelay, keepalive, &rlogin->plug, conf);
202 if ((err = sk_socket_error(rlogin->s)) != NULL)
203 return dupstr(err);
204
205 loghost = conf_get_str(conf, CONF_loghost);
206 if (*loghost) {
207 char *colon;
208
209 sfree(*realhost);
210 *realhost = dupstr(loghost);
211
212 colon = host_strrchr(*realhost, ':');
213 if (colon)
214 *colon++ = '\0';
215 }
216
217 /*
218 * Send local username, remote username, terminal type and
219 * terminal speed - unless we don't have the remote username yet,
220 * in which case we prompt for it and may end up deferring doing
221 * anything else until the local prompt mechanism returns.
222 */
223 if ((ruser = get_remote_username(conf)) != NULL) {
224 /* Next terminal output will come from server */
225 seat_set_trust_status(rlogin->seat, false);
226 rlogin_startup(rlogin, ruser);
227 sfree(ruser);
228 } else {
229 int ret;
230
231 rlogin->prompt = new_prompts();
232 rlogin->prompt->to_server = true;
233 rlogin->prompt->from_server = false;
234 rlogin->prompt->name = dupstr("Rlogin login name");
235 add_prompt(rlogin->prompt, dupstr("rlogin username: "), true);
236 ret = seat_get_userpass_input(rlogin->seat, rlogin->prompt, NULL);
237 if (ret >= 0) {
238 /* Next terminal output will come from server */
239 seat_set_trust_status(rlogin->seat, false);
240 rlogin_startup(rlogin, prompt_get_result_ref(
241 rlogin->prompt->prompts[0]));
242 }
243 }
244
245 return NULL;
246 }
247
rlogin_free(Backend * be)248 static void rlogin_free(Backend *be)
249 {
250 Rlogin *rlogin = container_of(be, Rlogin, backend);
251
252 if (rlogin->prompt)
253 free_prompts(rlogin->prompt);
254 if (rlogin->s)
255 sk_close(rlogin->s);
256 conf_free(rlogin->conf);
257 sfree(rlogin);
258 }
259
260 /*
261 * Stub routine (we don't have any need to reconfigure this backend).
262 */
rlogin_reconfig(Backend * be,Conf * conf)263 static void rlogin_reconfig(Backend *be, Conf *conf)
264 {
265 }
266
267 /*
268 * Called to send data down the rlogin connection.
269 */
rlogin_send(Backend * be,const char * buf,size_t len)270 static size_t rlogin_send(Backend *be, const char *buf, size_t len)
271 {
272 Rlogin *rlogin = container_of(be, Rlogin, backend);
273 bufchain bc;
274
275 if (rlogin->s == NULL)
276 return 0;
277
278 bufchain_init(&bc);
279 bufchain_add(&bc, buf, len);
280
281 if (rlogin->prompt) {
282 /*
283 * We're still prompting for a username, and aren't talking
284 * directly to the network connection yet.
285 */
286 int ret = seat_get_userpass_input(rlogin->seat, rlogin->prompt, &bc);
287 if (ret >= 0) {
288 /* Next terminal output will come from server */
289 seat_set_trust_status(rlogin->seat, false);
290 rlogin_startup(rlogin, prompt_get_result_ref(
291 rlogin->prompt->prompts[0]));
292 /* that nulls out rlogin->prompt, so then we'll start sending
293 * data down the wire in the obvious way */
294 }
295 }
296
297 if (!rlogin->prompt) {
298 while (bufchain_size(&bc) > 0) {
299 ptrlen data = bufchain_prefix(&bc);
300 rlogin->bufsize = sk_write(rlogin->s, data.ptr, data.len);
301 bufchain_consume(&bc, len);
302 }
303 }
304
305 bufchain_clear(&bc);
306
307 return rlogin->bufsize;
308 }
309
310 /*
311 * Called to query the current socket sendability status.
312 */
rlogin_sendbuffer(Backend * be)313 static size_t rlogin_sendbuffer(Backend *be)
314 {
315 Rlogin *rlogin = container_of(be, Rlogin, backend);
316 return rlogin->bufsize;
317 }
318
319 /*
320 * Called to set the size of the window
321 */
rlogin_size(Backend * be,int width,int height)322 static void rlogin_size(Backend *be, int width, int height)
323 {
324 Rlogin *rlogin = container_of(be, Rlogin, backend);
325 char b[12] = { '\xFF', '\xFF', 0x73, 0x73, 0, 0, 0, 0, 0, 0, 0, 0 };
326
327 rlogin->term_width = width;
328 rlogin->term_height = height;
329
330 if (rlogin->s == NULL || !rlogin->cansize)
331 return;
332
333 b[6] = rlogin->term_width >> 8;
334 b[7] = rlogin->term_width & 0xFF;
335 b[4] = rlogin->term_height >> 8;
336 b[5] = rlogin->term_height & 0xFF;
337 rlogin->bufsize = sk_write(rlogin->s, b, 12);
338 return;
339 }
340
341 /*
342 * Send rlogin special codes.
343 */
rlogin_special(Backend * be,SessionSpecialCode code,int arg)344 static void rlogin_special(Backend *be, SessionSpecialCode code, int arg)
345 {
346 /* Do nothing! */
347 return;
348 }
349
350 /*
351 * Return a list of the special codes that make sense in this
352 * protocol.
353 */
rlogin_get_specials(Backend * be)354 static const SessionSpecial *rlogin_get_specials(Backend *be)
355 {
356 return NULL;
357 }
358
rlogin_connected(Backend * be)359 static bool rlogin_connected(Backend *be)
360 {
361 Rlogin *rlogin = container_of(be, Rlogin, backend);
362 return rlogin->s != NULL;
363 }
364
rlogin_sendok(Backend * be)365 static bool rlogin_sendok(Backend *be)
366 {
367 /* Rlogin *rlogin = container_of(be, Rlogin, backend); */
368 return true;
369 }
370
rlogin_unthrottle(Backend * be,size_t backlog)371 static void rlogin_unthrottle(Backend *be, size_t backlog)
372 {
373 Rlogin *rlogin = container_of(be, Rlogin, backend);
374 sk_set_frozen(rlogin->s, backlog > RLOGIN_MAX_BACKLOG);
375 }
376
rlogin_ldisc(Backend * be,int option)377 static bool rlogin_ldisc(Backend *be, int option)
378 {
379 /* Rlogin *rlogin = container_of(be, Rlogin, backend); */
380 return false;
381 }
382
rlogin_provide_ldisc(Backend * be,Ldisc * ldisc)383 static void rlogin_provide_ldisc(Backend *be, Ldisc *ldisc)
384 {
385 /* This is a stub. */
386 }
387
rlogin_exitcode(Backend * be)388 static int rlogin_exitcode(Backend *be)
389 {
390 Rlogin *rlogin = container_of(be, Rlogin, backend);
391 if (rlogin->s != NULL)
392 return -1; /* still connected */
393 else if (rlogin->closed_on_socket_error)
394 return INT_MAX; /* a socket error counts as an unclean exit */
395 else
396 /* If we ever implement RSH, we'll probably need to do this properly */
397 return 0;
398 }
399
400 /*
401 * cfg_info for rlogin does nothing at all.
402 */
rlogin_cfg_info(Backend * be)403 static int rlogin_cfg_info(Backend *be)
404 {
405 return 0;
406 }
407
408 const BackendVtable rlogin_backend = {
409 .init = rlogin_init,
410 .free = rlogin_free,
411 .reconfig = rlogin_reconfig,
412 .send = rlogin_send,
413 .sendbuffer = rlogin_sendbuffer,
414 .size = rlogin_size,
415 .special = rlogin_special,
416 .get_specials = rlogin_get_specials,
417 .connected = rlogin_connected,
418 .exitcode = rlogin_exitcode,
419 .sendok = rlogin_sendok,
420 .ldisc_option_state = rlogin_ldisc,
421 .provide_ldisc = rlogin_provide_ldisc,
422 .unthrottle = rlogin_unthrottle,
423 .cfg_info = rlogin_cfg_info,
424 .id = "rlogin",
425 .displayname = "Rlogin",
426 .protocol = PROT_RLOGIN,
427 .default_port = 513,
428 };
429