OSDN Git Service

Add support for MLSD responses from some broken hosts.
[ffftp/ffftp.git] / putty / WINDOWS / WINSER.C
1 /*\r
2  * Serial back end (Windows-specific).\r
3  */\r
4 \r
5 #include <stdio.h>\r
6 #include <stdlib.h>\r
7 #include <limits.h>\r
8 \r
9 #include "putty.h"\r
10 \r
11 #define SERIAL_MAX_BACKLOG 4096\r
12 \r
13 typedef struct serial_backend_data {\r
14     HANDLE port;\r
15     struct handle *out, *in;\r
16     void *frontend;\r
17     int bufsize;\r
18     long clearbreak_time;\r
19     int break_in_progress;\r
20 } *Serial;\r
21 \r
22 static void serial_terminate(Serial serial)\r
23 {\r
24     if (serial->out) {\r
25         handle_free(serial->out);\r
26         serial->out = NULL;\r
27     }\r
28     if (serial->in) {\r
29         handle_free(serial->in);\r
30         serial->in = NULL;\r
31     }\r
32     if (serial->port != INVALID_HANDLE_VALUE) {\r
33         if (serial->break_in_progress)\r
34             ClearCommBreak(serial->port);\r
35         CloseHandle(serial->port);\r
36         serial->port = INVALID_HANDLE_VALUE;\r
37     }\r
38 }\r
39 \r
40 static int serial_gotdata(struct handle *h, void *data, int len)\r
41 {\r
42     Serial serial = (Serial)handle_get_privdata(h);\r
43     if (len <= 0) {\r
44         const char *error_msg;\r
45 \r
46         /*\r
47          * Currently, len==0 should never happen because we're\r
48          * ignoring EOFs. However, it seems not totally impossible\r
49          * that this same back end might be usable to talk to named\r
50          * pipes or some other non-serial device, in which case EOF\r
51          * may become meaningful here.\r
52          */\r
53         if (len == 0)\r
54             error_msg = "End of file reading from serial device";\r
55         else\r
56             error_msg = "Error reading from serial device";\r
57 \r
58         serial_terminate(serial);\r
59 \r
60         notify_remote_exit(serial->frontend);\r
61 \r
62         logevent(serial->frontend, error_msg);\r
63 \r
64         connection_fatal(serial->frontend, "%s", error_msg);\r
65 \r
66         return 0;                      /* placate optimiser */\r
67     } else {\r
68         return from_backend(serial->frontend, 0, data, len);\r
69     }\r
70 }\r
71 \r
72 static void serial_sentdata(struct handle *h, int new_backlog)\r
73 {\r
74     Serial serial = (Serial)handle_get_privdata(h);\r
75     if (new_backlog < 0) {\r
76         const char *error_msg = "Error writing to serial device";\r
77 \r
78         serial_terminate(serial);\r
79 \r
80         notify_remote_exit(serial->frontend);\r
81 \r
82         logevent(serial->frontend, error_msg);\r
83 \r
84         connection_fatal(serial->frontend, "%s", error_msg);\r
85     } else {\r
86         serial->bufsize = new_backlog;\r
87     }\r
88 }\r
89 \r
90 static const char *serial_configure(Serial serial, HANDLE serport, Config *cfg)\r
91 {\r
92     DCB dcb;\r
93     COMMTIMEOUTS timeouts;\r
94 \r
95     /*\r
96      * Set up the serial port parameters. If we can't even\r
97      * GetCommState, we ignore the problem on the grounds that the\r
98      * user might have pointed us at some other type of two-way\r
99      * device instead of a serial port.\r
100      */\r
101     if (GetCommState(serport, &dcb)) {\r
102         char *msg;\r
103         const char *str;\r
104 \r
105         /*\r
106          * Boilerplate.\r
107          */\r
108         dcb.fBinary = TRUE;\r
109         dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
110         dcb.fDsrSensitivity = FALSE;\r
111         dcb.fTXContinueOnXoff = FALSE;\r
112         dcb.fOutX = FALSE;\r
113         dcb.fInX = FALSE;\r
114         dcb.fErrorChar = FALSE;\r
115         dcb.fNull = FALSE;\r
116         dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
117         dcb.fAbortOnError = FALSE;\r
118         dcb.fOutxCtsFlow = FALSE;\r
119         dcb.fOutxDsrFlow = FALSE;\r
120 \r
121         /*\r
122          * Configurable parameters.\r
123          */\r
124         dcb.BaudRate = cfg->serspeed;\r
125         msg = dupprintf("Configuring baud rate %d", cfg->serspeed);\r
126         logevent(serial->frontend, msg);\r
127         sfree(msg);\r
128 \r
129         dcb.ByteSize = cfg->serdatabits;\r
130         msg = dupprintf("Configuring %d data bits", cfg->serdatabits);\r
131         logevent(serial->frontend, msg);\r
132         sfree(msg);\r
133 \r
134         switch (cfg->serstopbits) {\r
135           case 2: dcb.StopBits = ONESTOPBIT; str = "1"; break;\r
136           case 3: dcb.StopBits = ONE5STOPBITS; str = "1.5"; break;\r
137           case 4: dcb.StopBits = TWOSTOPBITS; str = "2"; break;\r
138           default: return "Invalid number of stop bits (need 1, 1.5 or 2)";\r
139         }\r
140         msg = dupprintf("Configuring %s data bits", str);\r
141         logevent(serial->frontend, msg);\r
142         sfree(msg);\r
143 \r
144         switch (cfg->serparity) {\r
145           case SER_PAR_NONE: dcb.Parity = NOPARITY; str = "no"; break;\r
146           case SER_PAR_ODD: dcb.Parity = ODDPARITY; str = "odd"; break;\r
147           case SER_PAR_EVEN: dcb.Parity = EVENPARITY; str = "even"; break;\r
148           case SER_PAR_MARK: dcb.Parity = MARKPARITY; str = "mark"; break;\r
149           case SER_PAR_SPACE: dcb.Parity = SPACEPARITY; str = "space"; break;\r
150         }\r
151         msg = dupprintf("Configuring %s parity", str);\r
152         logevent(serial->frontend, msg);\r
153         sfree(msg);\r
154 \r
155         switch (cfg->serflow) {\r
156           case SER_FLOW_NONE:\r
157             str = "no";\r
158             break;\r
159           case SER_FLOW_XONXOFF:\r
160             dcb.fOutX = dcb.fInX = TRUE;\r
161             str = "XON/XOFF";\r
162             break;\r
163           case SER_FLOW_RTSCTS:\r
164             dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;\r
165             dcb.fOutxCtsFlow = TRUE;\r
166             str = "RTS/CTS";\r
167             break;\r
168           case SER_FLOW_DSRDTR:\r
169             dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;\r
170             dcb.fOutxDsrFlow = TRUE;\r
171             str = "DSR/DTR";\r
172             break;\r
173         }\r
174         msg = dupprintf("Configuring %s flow control", str);\r
175         logevent(serial->frontend, msg);\r
176         sfree(msg);\r
177 \r
178         if (!SetCommState(serport, &dcb))\r
179             return "Unable to configure serial port";\r
180 \r
181         timeouts.ReadIntervalTimeout = 1;\r
182         timeouts.ReadTotalTimeoutMultiplier = 0;\r
183         timeouts.ReadTotalTimeoutConstant = 0;\r
184         timeouts.WriteTotalTimeoutMultiplier = 0;\r
185         timeouts.WriteTotalTimeoutConstant = 0;\r
186         if (!SetCommTimeouts(serport, &timeouts))\r
187             return "Unable to configure serial timeouts";\r
188     }\r
189 \r
190     return NULL;\r
191 }\r
192 \r
193 /*\r
194  * Called to set up the serial connection.\r
195  * \r
196  * Returns an error message, or NULL on success.\r
197  *\r
198  * Also places the canonical host name into `realhost'. It must be\r
199  * freed by the caller.\r
200  */\r
201 static const char *serial_init(void *frontend_handle, void **backend_handle,\r
202                                Config *cfg,\r
203                                char *host, int port, char **realhost, int nodelay,\r
204                                int keepalive)\r
205 {\r
206     Serial serial;\r
207     HANDLE serport;\r
208     const char *err;\r
209 \r
210     serial = snew(struct serial_backend_data);\r
211     serial->port = INVALID_HANDLE_VALUE;\r
212     serial->out = serial->in = NULL;\r
213     serial->bufsize = 0;\r
214     serial->break_in_progress = FALSE;\r
215     *backend_handle = serial;\r
216 \r
217     serial->frontend = frontend_handle;\r
218 \r
219     {\r
220         char *msg = dupprintf("Opening serial device %s", cfg->serline);\r
221         logevent(serial->frontend, msg);\r
222     }\r
223 \r
224     {\r
225         /*\r
226          * Munge the string supplied by the user into a Windows filename.\r
227          *\r
228          * Windows supports opening a few "legacy" devices (including\r
229          * COM1-9) by specifying their names verbatim as a filename to\r
230          * open. (Thus, no files can ever have these names. See\r
231          * <http://msdn2.microsoft.com/en-us/library/aa365247.aspx>\r
232          * ("Naming a File") for the complete list of reserved names.)\r
233          *\r
234          * However, this doesn't let you get at devices COM10 and above.\r
235          * For that, you need to specify a filename like "\\.\COM10".\r
236          * This is also necessary for special serial and serial-like\r
237          * devices such as \\.\WCEUSBSH001. It also works for the "legacy"\r
238          * names, so you can do \\.\COM1 (verified as far back as Win95).\r
239          * See <http://msdn2.microsoft.com/en-us/library/aa363858.aspx>\r
240          * (CreateFile() docs).\r
241          *\r
242          * So, we believe that prepending "\\.\" should always be the\r
243          * Right Thing. However, just in case someone finds something to\r
244          * talk to that doesn't exist under there, if the serial line\r
245          * contains a backslash, we use it verbatim. (This also lets\r
246          * existing configurations using \\.\ continue working.)\r
247          */\r
248         char *serfilename =\r
249             dupprintf("%s%s",\r
250                       strchr(cfg->serline, '\\') ? "" : "\\\\.\\",\r
251                       cfg->serline);\r
252         serport = CreateFile(serfilename, GENERIC_READ | GENERIC_WRITE, 0, NULL,\r
253                              OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
254         sfree(serfilename);\r
255     }\r
256 \r
257     if (serport == INVALID_HANDLE_VALUE)\r
258         return "Unable to open serial port";\r
259 \r
260     err = serial_configure(serial, serport, cfg);\r
261     if (err)\r
262         return err;\r
263 \r
264     serial->port = serport;\r
265     serial->out = handle_output_new(serport, serial_sentdata, serial,\r
266                                     HANDLE_FLAG_OVERLAPPED);\r
267     serial->in = handle_input_new(serport, serial_gotdata, serial,\r
268                                   HANDLE_FLAG_OVERLAPPED |\r
269                                   HANDLE_FLAG_IGNOREEOF |\r
270                                   HANDLE_FLAG_UNITBUFFER);\r
271 \r
272     *realhost = dupstr(cfg->serline);\r
273 \r
274     /*\r
275      * Specials are always available.\r
276      */\r
277     update_specials_menu(serial->frontend);\r
278 \r
279     return NULL;\r
280 }\r
281 \r
282 static void serial_free(void *handle)\r
283 {\r
284     Serial serial = (Serial) handle;\r
285 \r
286     serial_terminate(serial);\r
287     expire_timer_context(serial);\r
288     sfree(serial);\r
289 }\r
290 \r
291 static void serial_reconfig(void *handle, Config *cfg)\r
292 {\r
293     Serial serial = (Serial) handle;\r
294     const char *err;\r
295 \r
296     err = serial_configure(serial, serial->port, cfg);\r
297 \r
298     /*\r
299      * FIXME: what should we do if err returns something?\r
300      */\r
301 }\r
302 \r
303 /*\r
304  * Called to send data down the serial connection.\r
305  */\r
306 static int serial_send(void *handle, char *buf, int len)\r
307 {\r
308     Serial serial = (Serial) handle;\r
309 \r
310     if (serial->out == NULL)\r
311         return 0;\r
312 \r
313     serial->bufsize = handle_write(serial->out, buf, len);\r
314     return serial->bufsize;\r
315 }\r
316 \r
317 /*\r
318  * Called to query the current sendability status.\r
319  */\r
320 static int serial_sendbuffer(void *handle)\r
321 {\r
322     Serial serial = (Serial) handle;\r
323     return serial->bufsize;\r
324 }\r
325 \r
326 /*\r
327  * Called to set the size of the window\r
328  */\r
329 static void serial_size(void *handle, int width, int height)\r
330 {\r
331     /* Do nothing! */\r
332     return;\r
333 }\r
334 \r
335 static void serbreak_timer(void *ctx, long now)\r
336 {\r
337     Serial serial = (Serial)ctx;\r
338 \r
339     if (now >= serial->clearbreak_time && serial->port) {\r
340         ClearCommBreak(serial->port);\r
341         serial->break_in_progress = FALSE;\r
342         logevent(serial->frontend, "Finished serial break");\r
343     }\r
344 }\r
345 \r
346 /*\r
347  * Send serial special codes.\r
348  */\r
349 static void serial_special(void *handle, Telnet_Special code)\r
350 {\r
351     Serial serial = (Serial) handle;\r
352 \r
353     if (serial->port && code == TS_BRK) {\r
354         logevent(serial->frontend, "Starting serial break at user request");\r
355         SetCommBreak(serial->port);\r
356         /*\r
357          * To send a serial break on Windows, we call SetCommBreak\r
358          * to begin the break, then wait a bit, and then call\r
359          * ClearCommBreak to finish it. Hence, I must use timing.c\r
360          * to arrange a callback when it's time to do the latter.\r
361          * \r
362          * SUS says that a default break length must be between 1/4\r
363          * and 1/2 second. FreeBSD apparently goes with 2/5 second,\r
364          * and so will I. \r
365          */\r
366         serial->clearbreak_time =\r
367             schedule_timer(TICKSPERSEC * 2 / 5, serbreak_timer, serial);\r
368         serial->break_in_progress = TRUE;\r
369     }\r
370 \r
371     return;\r
372 }\r
373 \r
374 /*\r
375  * Return a list of the special codes that make sense in this\r
376  * protocol.\r
377  */\r
378 static const struct telnet_special *serial_get_specials(void *handle)\r
379 {\r
380     static const struct telnet_special specials[] = {\r
381         {"Break", TS_BRK},\r
382         {NULL, TS_EXITMENU}\r
383     };\r
384     return specials;\r
385 }\r
386 \r
387 static int serial_connected(void *handle)\r
388 {\r
389     return 1;                          /* always connected */\r
390 }\r
391 \r
392 static int serial_sendok(void *handle)\r
393 {\r
394     return 1;\r
395 }\r
396 \r
397 static void serial_unthrottle(void *handle, int backlog)\r
398 {\r
399     Serial serial = (Serial) handle;\r
400     if (serial->in)\r
401         handle_unthrottle(serial->in, backlog);\r
402 }\r
403 \r
404 static int serial_ldisc(void *handle, int option)\r
405 {\r
406     /*\r
407      * Local editing and local echo are off by default.\r
408      */\r
409     return 0;\r
410 }\r
411 \r
412 static void serial_provide_ldisc(void *handle, void *ldisc)\r
413 {\r
414     /* This is a stub. */\r
415 }\r
416 \r
417 static void serial_provide_logctx(void *handle, void *logctx)\r
418 {\r
419     /* This is a stub. */\r
420 }\r
421 \r
422 static int serial_exitcode(void *handle)\r
423 {\r
424     Serial serial = (Serial) handle;\r
425     if (serial->port != INVALID_HANDLE_VALUE)\r
426         return -1;                     /* still connected */\r
427     else\r
428         /* Exit codes are a meaningless concept with serial ports */\r
429         return INT_MAX;\r
430 }\r
431 \r
432 /*\r
433  * cfg_info for Serial does nothing at all.\r
434  */\r
435 static int serial_cfg_info(void *handle)\r
436 {\r
437     return 0;\r
438 }\r
439 \r
440 Backend serial_backend = {\r
441     serial_init,\r
442     serial_free,\r
443     serial_reconfig,\r
444     serial_send,\r
445     serial_sendbuffer,\r
446     serial_size,\r
447     serial_special,\r
448     serial_get_specials,\r
449     serial_connected,\r
450     serial_exitcode,\r
451     serial_sendok,\r
452     serial_ldisc,\r
453     serial_provide_ldisc,\r
454     serial_provide_logctx,\r
455     serial_unthrottle,\r
456     serial_cfg_info,\r
457     "serial",\r
458     PROT_SERIAL,\r
459     0\r
460 };\r