CPatch: New memory management
[TortoiseGit.git] / src / TortoisePlink / RLOGIN.C
blob336f494b4df873b4a238247a8155c1a8927bfb07
1 /*\r
2  * Rlogin backend.\r
3  */\r
4 \r
5 #include <stdio.h>\r
6 #include <stdlib.h>\r
7 #include <limits.h>\r
8 #include <ctype.h>\r
9 \r
10 #include "putty.h"\r
12 #ifndef FALSE\r
13 #define FALSE 0\r
14 #endif\r
15 #ifndef TRUE\r
16 #define TRUE 1\r
17 #endif\r
19 #define RLOGIN_MAX_BACKLOG 4096\r
21 typedef struct rlogin_tag {\r
22     const struct plug_function_table *fn;\r
23     /* the above field _must_ be first in the structure */\r
25     Socket s;\r
26     int closed_on_socket_error;\r
27     int bufsize;\r
28     int firstbyte;\r
29     int cansize;\r
30     int term_width, term_height;\r
31     void *frontend;\r
33     Conf *conf;\r
35     /* In case we need to read a username from the terminal before starting */\r
36     prompts_t *prompt;\r
37 } *Rlogin;\r
39 static void rlogin_size(void *handle, int width, int height);\r
41 static void c_write(Rlogin rlogin, char *buf, int len)\r
42 {\r
43     int backlog = from_backend(rlogin->frontend, 0, buf, len);\r
44     sk_set_frozen(rlogin->s, backlog > RLOGIN_MAX_BACKLOG);\r
45 }\r
47 static void rlogin_log(Plug plug, int type, SockAddr addr, int port,\r
48                        const char *error_msg, int error_code)\r
49 {\r
50     Rlogin rlogin = (Rlogin) plug;\r
51     backend_socket_log(rlogin->frontend, type, addr, port,\r
52                        error_msg, error_code,\r
53                        rlogin->conf, !rlogin->firstbyte);\r
54 }\r
56 static void rlogin_closing(Plug plug, const char *error_msg, int error_code,\r
57                            int calling_back)\r
58 {\r
59     Rlogin rlogin = (Rlogin) plug;\r
61     /*\r
62      * We don't implement independent EOF in each direction for Telnet\r
63      * connections; as soon as we get word that the remote side has\r
64      * sent us EOF, we wind up the whole connection.\r
65      */\r
67     if (rlogin->s) {\r
68         sk_close(rlogin->s);\r
69         rlogin->s = NULL;\r
70         if (error_msg)\r
71             rlogin->closed_on_socket_error = TRUE;\r
72         notify_remote_exit(rlogin->frontend);\r
73     }\r
74     if (error_msg) {\r
75         /* A socket error has occurred. */\r
76         logevent(rlogin->frontend, error_msg);\r
77         connection_fatal(rlogin->frontend, "%s", error_msg);\r
78     }                                  /* Otherwise, the remote side closed the connection normally. */\r
79 }\r
81 static void rlogin_receive(Plug plug, int urgent, char *data, int len)\r
82 {\r
83     Rlogin rlogin = (Rlogin) plug;\r
84     if (urgent == 2) {\r
85         char c;\r
87         c = *data++;\r
88         len--;\r
89         if (c == '\x80') {\r
90             rlogin->cansize = 1;\r
91             rlogin_size(rlogin, rlogin->term_width, rlogin->term_height);\r
92         }\r
93         /*\r
94          * We should flush everything (aka Telnet SYNCH) if we see\r
95          * 0x02, and we should turn off and on _local_ flow control\r
96          * on 0x10 and 0x20 respectively. I'm not convinced it's\r
97          * worth it...\r
98          */\r
99     } else {\r
100         /*\r
101          * Main rlogin protocol. This is really simple: the first\r
102          * byte is expected to be NULL and is ignored, and the rest\r
103          * is printed.\r
104          */\r
105         if (rlogin->firstbyte) {\r
106             if (data[0] == '\0') {\r
107                 data++;\r
108                 len--;\r
109             }\r
110             rlogin->firstbyte = 0;\r
111         }\r
112         if (len > 0)\r
113             c_write(rlogin, data, len);\r
114     }\r
117 static void rlogin_sent(Plug plug, int bufsize)\r
119     Rlogin rlogin = (Rlogin) plug;\r
120     rlogin->bufsize = bufsize;\r
123 static void rlogin_startup(Rlogin rlogin, const char *ruser)\r
125     char z = 0;\r
126     char *p;\r
128     sk_write(rlogin->s, &z, 1);\r
129     p = conf_get_str(rlogin->conf, CONF_localusername);\r
130     sk_write(rlogin->s, p, strlen(p));\r
131     sk_write(rlogin->s, &z, 1);\r
132     sk_write(rlogin->s, ruser, strlen(ruser));\r
133     sk_write(rlogin->s, &z, 1);\r
134     p = conf_get_str(rlogin->conf, CONF_termtype);\r
135     sk_write(rlogin->s, p, strlen(p));\r
136     sk_write(rlogin->s, "/", 1);\r
137     p = conf_get_str(rlogin->conf, CONF_termspeed);\r
138     sk_write(rlogin->s, p, strspn(p, "0123456789"));\r
139     rlogin->bufsize = sk_write(rlogin->s, &z, 1);\r
141     rlogin->prompt = NULL;\r
144 /*\r
145  * Called to set up the rlogin connection.\r
146  * \r
147  * Returns an error message, or NULL on success.\r
148  *\r
149  * Also places the canonical host name into `realhost'. It must be\r
150  * freed by the caller.\r
151  */\r
152 static const char *rlogin_init(void *frontend_handle, void **backend_handle,\r
153                                Conf *conf,\r
154                                const char *host, int port, char **realhost,\r
155                                int nodelay, int keepalive)\r
157     static const struct plug_function_table fn_table = {\r
158         rlogin_log,\r
159         rlogin_closing,\r
160         rlogin_receive,\r
161         rlogin_sent\r
162     };\r
163     SockAddr addr;\r
164     const char *err;\r
165     Rlogin rlogin;\r
166     char *ruser;\r
167     int addressfamily;\r
168     char *loghost;\r
170     rlogin = snew(struct rlogin_tag);\r
171     rlogin->fn = &fn_table;\r
172     rlogin->s = NULL;\r
173     rlogin->closed_on_socket_error = FALSE;\r
174     rlogin->frontend = frontend_handle;\r
175     rlogin->term_width = conf_get_int(conf, CONF_width);\r
176     rlogin->term_height = conf_get_int(conf, CONF_height);\r
177     rlogin->firstbyte = 1;\r
178     rlogin->cansize = 0;\r
179     rlogin->prompt = NULL;\r
180     rlogin->conf = conf_copy(conf);\r
181     *backend_handle = rlogin;\r
183     addressfamily = conf_get_int(conf, CONF_addressfamily);\r
184     /*\r
185      * Try to find host.\r
186      */\r
187     addr = name_lookup(host, port, realhost, conf, addressfamily,\r
188                        rlogin->frontend, "rlogin connection");\r
189     if ((err = sk_addr_error(addr)) != NULL) {\r
190         sk_addr_free(addr);\r
191         return err;\r
192     }\r
194     if (port < 0)\r
195         port = 513;                    /* default rlogin port */\r
197     /*\r
198      * Open socket.\r
199      */\r
200     rlogin->s = new_connection(addr, *realhost, port, 1, 0,\r
201                                nodelay, keepalive, (Plug) rlogin, conf);\r
202     if ((err = sk_socket_error(rlogin->s)) != NULL)\r
203         return err;\r
205     loghost = conf_get_str(conf, CONF_loghost);\r
206     if (*loghost) {\r
207         char *colon;\r
209         sfree(*realhost);\r
210         *realhost = dupstr(loghost);\r
212         colon = host_strrchr(*realhost, ':');\r
213         if (colon)\r
214             *colon++ = '\0';\r
215     }\r
217     /*\r
218      * Send local username, remote username, terminal type and\r
219      * terminal speed - unless we don't have the remote username yet,\r
220      * in which case we prompt for it and may end up deferring doing\r
221      * anything else until the local prompt mechanism returns.\r
222      */\r
223     if ((ruser = get_remote_username(conf)) != NULL) {\r
224         rlogin_startup(rlogin, ruser);\r
225         sfree(ruser);\r
226     } else {\r
227         int ret;\r
229         rlogin->prompt = new_prompts(rlogin->frontend);\r
230         rlogin->prompt->to_server = TRUE;\r
231         rlogin->prompt->name = dupstr("Rlogin login name");\r
232         add_prompt(rlogin->prompt, dupstr("rlogin username: "), TRUE); \r
233         ret = get_userpass_input(rlogin->prompt, NULL, 0);\r
234         if (ret >= 0) {\r
235             rlogin_startup(rlogin, rlogin->prompt->prompts[0]->result);\r
236         }\r
237     }\r
239     return NULL;\r
242 static void rlogin_free(void *handle)\r
244     Rlogin rlogin = (Rlogin) handle;\r
246     if (rlogin->prompt)\r
247         free_prompts(rlogin->prompt);\r
248     if (rlogin->s)\r
249         sk_close(rlogin->s);\r
250     conf_free(rlogin->conf);\r
251     sfree(rlogin);\r
254 /*\r
255  * Stub routine (we don't have any need to reconfigure this backend).\r
256  */\r
257 static void rlogin_reconfig(void *handle, Conf *conf)\r
261 /*\r
262  * Called to send data down the rlogin connection.\r
263  */\r
264 static int rlogin_send(void *handle, const char *buf, int len)\r
266     Rlogin rlogin = (Rlogin) handle;\r
268     if (rlogin->s == NULL)\r
269         return 0;\r
271     if (rlogin->prompt) {\r
272         /*\r
273          * We're still prompting for a username, and aren't talking\r
274          * directly to the network connection yet.\r
275          */\r
276         int ret = get_userpass_input(rlogin->prompt,\r
277                                      (unsigned char *)buf, len);\r
278         if (ret >= 0) {\r
279             rlogin_startup(rlogin, rlogin->prompt->prompts[0]->result);\r
280             /* that nulls out rlogin->prompt, so then we'll start sending\r
281              * data down the wire in the obvious way */\r
282         }\r
283     } else {\r
284         rlogin->bufsize = sk_write(rlogin->s, buf, len);\r
285     }\r
287     return rlogin->bufsize;\r
290 /*\r
291  * Called to query the current socket sendability status.\r
292  */\r
293 static int rlogin_sendbuffer(void *handle)\r
295     Rlogin rlogin = (Rlogin) handle;\r
296     return rlogin->bufsize;\r
299 /*\r
300  * Called to set the size of the window\r
301  */\r
302 static void rlogin_size(void *handle, int width, int height)\r
304     Rlogin rlogin = (Rlogin) handle;\r
305     char b[12] = { '\xFF', '\xFF', 0x73, 0x73, 0, 0, 0, 0, 0, 0, 0, 0 };\r
307     rlogin->term_width = width;\r
308     rlogin->term_height = height;\r
310     if (rlogin->s == NULL || !rlogin->cansize)\r
311         return;\r
313     b[6] = rlogin->term_width >> 8;\r
314     b[7] = rlogin->term_width & 0xFF;\r
315     b[4] = rlogin->term_height >> 8;\r
316     b[5] = rlogin->term_height & 0xFF;\r
317     rlogin->bufsize = sk_write(rlogin->s, b, 12);\r
318     return;\r
321 /*\r
322  * Send rlogin special codes.\r
323  */\r
324 static void rlogin_special(void *handle, Telnet_Special code)\r
326     /* Do nothing! */\r
327     return;\r
330 /*\r
331  * Return a list of the special codes that make sense in this\r
332  * protocol.\r
333  */\r
334 static const struct telnet_special *rlogin_get_specials(void *handle)\r
336     return NULL;\r
339 static int rlogin_connected(void *handle)\r
341     Rlogin rlogin = (Rlogin) handle;\r
342     return rlogin->s != NULL;\r
345 static int rlogin_sendok(void *handle)\r
347     /* Rlogin rlogin = (Rlogin) handle; */\r
348     return 1;\r
351 static void rlogin_unthrottle(void *handle, int backlog)\r
353     Rlogin rlogin = (Rlogin) handle;\r
354     sk_set_frozen(rlogin->s, backlog > RLOGIN_MAX_BACKLOG);\r
357 static int rlogin_ldisc(void *handle, int option)\r
359     /* Rlogin rlogin = (Rlogin) handle; */\r
360     return 0;\r
363 static void rlogin_provide_ldisc(void *handle, void *ldisc)\r
365     /* This is a stub. */\r
368 static void rlogin_provide_logctx(void *handle, void *logctx)\r
370     /* This is a stub. */\r
373 static int rlogin_exitcode(void *handle)\r
375     Rlogin rlogin = (Rlogin) handle;\r
376     if (rlogin->s != NULL)\r
377         return -1;                     /* still connected */\r
378     else if (rlogin->closed_on_socket_error)\r
379         return INT_MAX;     /* a socket error counts as an unclean exit */\r
380     else\r
381         /* If we ever implement RSH, we'll probably need to do this properly */\r
382         return 0;\r
385 /*\r
386  * cfg_info for rlogin does nothing at all.\r
387  */\r
388 static int rlogin_cfg_info(void *handle)\r
390     return 0;\r
393 Backend rlogin_backend = {\r
394     rlogin_init,\r
395     rlogin_free,\r
396     rlogin_reconfig,\r
397     rlogin_send,\r
398     rlogin_sendbuffer,\r
399     rlogin_size,\r
400     rlogin_special,\r
401     rlogin_get_specials,\r
402     rlogin_connected,\r
403     rlogin_exitcode,\r
404     rlogin_sendok,\r
405     rlogin_ldisc,\r
406     rlogin_provide_ldisc,\r
407     rlogin_provide_logctx,\r
408     rlogin_unthrottle,\r
409     rlogin_cfg_info,\r
410     NULL /* test_for_upstream */,\r
411     "rlogin",\r
412     PROT_RLOGIN,\r
413     513\r
414 };\r