OSDN Git Service

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