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