Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/winpr/libwinpr/comm/comm_ioctl.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * Serial Communication API
4
 *
5
 * Copyright 2011 O.S. Systems Software Ltda.
6
 * Copyright 2011 Eduardo Fiss Beloni <beloni@ossystems.com.br>
7
 * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
8
 * Copyright 2014 Hewlett-Packard Development Company, L.P.
9
 *
10
 * Licensed under the Apache License, Version 2.0 (the "License");
11
 * you may not use this file except in compliance with the License.
12
 * You may obtain a copy of the License at
13
 *
14
 *     http://www.apache.org/licenses/LICENSE-2.0
15
 *
16
 * Unless required by applicable law or agreed to in writing, software
17
 * distributed under the License is distributed on an "AS IS" BASIS,
18
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
 * See the License for the specific language governing permissions and
20
 * limitations under the License.
21
 */
22
23
#include <winpr/config.h>
24
25
#include <winpr/assert.h>
26
#include <errno.h>
27
28
#include <winpr/wlog.h>
29
30
#include "comm.h"
31
#include "comm_ioctl.h"
32
#include "comm_serial_sys.h"
33
#include "comm_sercx_sys.h"
34
#include "comm_sercx2_sys.h"
35
36
static const char* comm_ioctl_modem_status_string(ULONG status, char* buffer, size_t size);
37
38
/* NB: MS-RDPESP's recommendation:
39
 *
40
 * <2> Section 3.2.5.1.6: Windows Implementations use IOCTL constants
41
 * for IoControlCode values.  The content and values of the IOCTLs are
42
 * opaque to the protocol. On the server side, the data contained in
43
 * an IOCTL is simply packaged and sent to the client side. For
44
 * maximum compatibility between the different versions of the Windows
45
 * operating system, the client implementation only singles out
46
 * critical IOCTLs and invokes the applicable Win32 port API. The
47
 * other IOCTLS are passed directly to the client-side driver, and the
48
 * processing of this value depends on the drivers installed on the
49
 * client side. The values and parameters for these IOCTLS can be
50
 * found in [MSFT-W2KDDK] Volume 2, Part 2—Serial and Parallel
51
 * Drivers, and in [MSDN-PORTS].
52
 */
53
static BOOL s_CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer,
54
                                  DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize,
55
                                  LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped)
56
0
{
57
0
  char buffer[128] = { 0 };
58
0
  WINPR_COMM* pComm = (WINPR_COMM*)hDevice;
59
0
  const SERIAL_DRIVER* pServerSerialDriver = NULL;
60
61
0
  if (!CommIsHandleValid(hDevice))
62
0
    return FALSE;
63
64
0
  if (lpOverlapped)
65
0
  {
66
0
    SetLastError(ERROR_NOT_SUPPORTED);
67
0
    return FALSE;
68
0
  }
69
70
0
  if (lpBytesReturned == NULL)
71
0
  {
72
0
    SetLastError(ERROR_INVALID_PARAMETER); /* since we doesn't support lpOverlapped != NULL */
73
0
    return FALSE;
74
0
  }
75
76
  /* clear any previous last error */
77
0
  SetLastError(ERROR_SUCCESS);
78
79
0
  *lpBytesReturned = 0; /* will be adjusted if required ... */
80
81
0
  CommLog_Print(WLOG_DEBUG, "CommDeviceIoControl: IoControlCode: %s [0x%08" PRIx32 "]",
82
0
                _comm_serial_ioctl_name(dwIoControlCode), dwIoControlCode);
83
84
  /* remoteSerialDriver to be use ...
85
   *
86
   * FIXME: might prefer to use an automatic rather than static structure
87
   */
88
0
  switch (pComm->serverSerialDriverId)
89
0
  {
90
0
    case SerialDriverSerialSys:
91
0
      pServerSerialDriver = SerialSys_s();
92
0
      break;
93
94
0
    case SerialDriverSerCxSys:
95
0
      pServerSerialDriver = SerCxSys_s();
96
0
      break;
97
98
0
    case SerialDriverSerCx2Sys:
99
0
      pServerSerialDriver = SerCx2Sys_s();
100
0
      break;
101
102
0
    case SerialDriverUnknown:
103
0
    default:
104
0
      CommLog_Print(WLOG_DEBUG, "Unknown remote serial driver (%d), using SerCx2.sys",
105
0
                    pComm->serverSerialDriverId);
106
0
      pServerSerialDriver = SerCx2Sys_s();
107
0
      break;
108
0
  }
109
110
0
  WINPR_ASSERT(pServerSerialDriver != NULL);
111
112
0
  switch (dwIoControlCode)
113
0
  {
114
0
    case IOCTL_USBPRINT_GET_1284_ID:
115
0
    {
116
      /* FIXME:
117
       * http://msdn.microsoft.com/en-us/library/windows/hardware/ff551803(v=vs.85).aspx */
118
0
      *lpBytesReturned = nOutBufferSize; /* an empty OutputBuffer will be returned */
119
0
      SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
120
0
      return FALSE;
121
0
    }
122
0
    case IOCTL_SERIAL_SET_BAUD_RATE:
123
0
    {
124
0
      if (pServerSerialDriver->set_baud_rate)
125
0
      {
126
0
        SERIAL_BAUD_RATE* pBaudRate = (SERIAL_BAUD_RATE*)lpInBuffer;
127
128
0
        WINPR_ASSERT(nInBufferSize >= sizeof(SERIAL_BAUD_RATE));
129
0
        if (nInBufferSize < sizeof(SERIAL_BAUD_RATE))
130
0
        {
131
0
          SetLastError(ERROR_INVALID_PARAMETER);
132
0
          return FALSE;
133
0
        }
134
135
0
        return pServerSerialDriver->set_baud_rate(pComm, pBaudRate);
136
0
      }
137
0
      break;
138
0
    }
139
0
    case IOCTL_SERIAL_GET_BAUD_RATE:
140
0
    {
141
0
      if (pServerSerialDriver->get_baud_rate)
142
0
      {
143
0
        SERIAL_BAUD_RATE* pBaudRate = (SERIAL_BAUD_RATE*)lpOutBuffer;
144
145
0
        WINPR_ASSERT(nOutBufferSize >= sizeof(SERIAL_BAUD_RATE));
146
0
        if (nOutBufferSize < sizeof(SERIAL_BAUD_RATE))
147
0
        {
148
0
          SetLastError(ERROR_INSUFFICIENT_BUFFER);
149
0
          return FALSE;
150
0
        }
151
152
0
        if (!pServerSerialDriver->get_baud_rate(pComm, pBaudRate))
153
0
          return FALSE;
154
155
0
        *lpBytesReturned = sizeof(SERIAL_BAUD_RATE);
156
0
        return TRUE;
157
0
      }
158
0
      break;
159
0
    }
160
0
    case IOCTL_SERIAL_GET_PROPERTIES:
161
0
    {
162
0
      if (pServerSerialDriver->get_properties)
163
0
      {
164
0
        COMMPROP* pProperties = (COMMPROP*)lpOutBuffer;
165
166
0
        WINPR_ASSERT(nOutBufferSize >= sizeof(COMMPROP));
167
0
        if (nOutBufferSize < sizeof(COMMPROP))
168
0
        {
169
0
          SetLastError(ERROR_INSUFFICIENT_BUFFER);
170
0
          return FALSE;
171
0
        }
172
173
0
        if (!pServerSerialDriver->get_properties(pComm, pProperties))
174
0
          return FALSE;
175
176
0
        *lpBytesReturned = sizeof(COMMPROP);
177
0
        return TRUE;
178
0
      }
179
0
      break;
180
0
    }
181
0
    case IOCTL_SERIAL_SET_CHARS:
182
0
    {
183
0
      if (pServerSerialDriver->set_serial_chars)
184
0
      {
185
0
        SERIAL_CHARS* pSerialChars = (SERIAL_CHARS*)lpInBuffer;
186
187
0
        WINPR_ASSERT(nInBufferSize >= sizeof(SERIAL_CHARS));
188
0
        if (nInBufferSize < sizeof(SERIAL_CHARS))
189
0
        {
190
0
          SetLastError(ERROR_INVALID_PARAMETER);
191
0
          return FALSE;
192
0
        }
193
194
0
        return pServerSerialDriver->set_serial_chars(pComm, pSerialChars);
195
0
      }
196
0
      break;
197
0
    }
198
0
    case IOCTL_SERIAL_GET_CHARS:
199
0
    {
200
0
      if (pServerSerialDriver->get_serial_chars)
201
0
      {
202
0
        SERIAL_CHARS* pSerialChars = (SERIAL_CHARS*)lpOutBuffer;
203
204
0
        WINPR_ASSERT(nOutBufferSize >= sizeof(SERIAL_CHARS));
205
0
        if (nOutBufferSize < sizeof(SERIAL_CHARS))
206
0
        {
207
0
          SetLastError(ERROR_INSUFFICIENT_BUFFER);
208
0
          return FALSE;
209
0
        }
210
211
0
        if (!pServerSerialDriver->get_serial_chars(pComm, pSerialChars))
212
0
          return FALSE;
213
214
0
        *lpBytesReturned = sizeof(SERIAL_CHARS);
215
0
        return TRUE;
216
0
      }
217
0
      break;
218
0
    }
219
0
    case IOCTL_SERIAL_SET_LINE_CONTROL:
220
0
    {
221
0
      if (pServerSerialDriver->set_line_control)
222
0
      {
223
0
        SERIAL_LINE_CONTROL* pLineControl = (SERIAL_LINE_CONTROL*)lpInBuffer;
224
225
0
        WINPR_ASSERT(nInBufferSize >= sizeof(SERIAL_LINE_CONTROL));
226
0
        if (nInBufferSize < sizeof(SERIAL_LINE_CONTROL))
227
0
        {
228
0
          SetLastError(ERROR_INVALID_PARAMETER);
229
0
          return FALSE;
230
0
        }
231
232
0
        return pServerSerialDriver->set_line_control(pComm, pLineControl);
233
0
      }
234
0
      break;
235
0
    }
236
0
    case IOCTL_SERIAL_GET_LINE_CONTROL:
237
0
    {
238
0
      if (pServerSerialDriver->get_line_control)
239
0
      {
240
0
        SERIAL_LINE_CONTROL* pLineControl = (SERIAL_LINE_CONTROL*)lpOutBuffer;
241
242
0
        WINPR_ASSERT(nOutBufferSize >= sizeof(SERIAL_LINE_CONTROL));
243
0
        if (nOutBufferSize < sizeof(SERIAL_LINE_CONTROL))
244
0
        {
245
0
          SetLastError(ERROR_INSUFFICIENT_BUFFER);
246
0
          return FALSE;
247
0
        }
248
249
0
        if (!pServerSerialDriver->get_line_control(pComm, pLineControl))
250
0
          return FALSE;
251
252
0
        *lpBytesReturned = sizeof(SERIAL_LINE_CONTROL);
253
0
        return TRUE;
254
0
      }
255
0
      break;
256
0
    }
257
0
    case IOCTL_SERIAL_SET_HANDFLOW:
258
0
    {
259
0
      if (pServerSerialDriver->set_handflow)
260
0
      {
261
0
        SERIAL_HANDFLOW* pHandflow = (SERIAL_HANDFLOW*)lpInBuffer;
262
263
0
        WINPR_ASSERT(nInBufferSize >= sizeof(SERIAL_HANDFLOW));
264
0
        if (nInBufferSize < sizeof(SERIAL_HANDFLOW))
265
0
        {
266
0
          SetLastError(ERROR_INVALID_PARAMETER);
267
0
          return FALSE;
268
0
        }
269
270
0
        return pServerSerialDriver->set_handflow(pComm, pHandflow);
271
0
      }
272
0
      break;
273
0
    }
274
0
    case IOCTL_SERIAL_GET_HANDFLOW:
275
0
    {
276
0
      if (pServerSerialDriver->get_handflow)
277
0
      {
278
0
        SERIAL_HANDFLOW* pHandflow = (SERIAL_HANDFLOW*)lpOutBuffer;
279
280
0
        WINPR_ASSERT(nOutBufferSize >= sizeof(SERIAL_HANDFLOW));
281
0
        if (nOutBufferSize < sizeof(SERIAL_HANDFLOW))
282
0
        {
283
0
          SetLastError(ERROR_INSUFFICIENT_BUFFER);
284
0
          return FALSE;
285
0
        }
286
287
0
        if (!pServerSerialDriver->get_handflow(pComm, pHandflow))
288
0
          return FALSE;
289
290
0
        *lpBytesReturned = sizeof(SERIAL_HANDFLOW);
291
0
        return TRUE;
292
0
      }
293
0
      break;
294
0
    }
295
0
    case IOCTL_SERIAL_SET_TIMEOUTS:
296
0
    {
297
0
      if (pServerSerialDriver->set_timeouts)
298
0
      {
299
0
        SERIAL_TIMEOUTS* pHandflow = (SERIAL_TIMEOUTS*)lpInBuffer;
300
301
0
        WINPR_ASSERT(nInBufferSize >= sizeof(SERIAL_TIMEOUTS));
302
0
        if (nInBufferSize < sizeof(SERIAL_TIMEOUTS))
303
0
        {
304
0
          SetLastError(ERROR_INVALID_PARAMETER);
305
0
          return FALSE;
306
0
        }
307
308
0
        return pServerSerialDriver->set_timeouts(pComm, pHandflow);
309
0
      }
310
0
      break;
311
0
    }
312
0
    case IOCTL_SERIAL_GET_TIMEOUTS:
313
0
    {
314
0
      if (pServerSerialDriver->get_timeouts)
315
0
      {
316
0
        SERIAL_TIMEOUTS* pHandflow = (SERIAL_TIMEOUTS*)lpOutBuffer;
317
318
0
        WINPR_ASSERT(nOutBufferSize >= sizeof(SERIAL_TIMEOUTS));
319
0
        if (nOutBufferSize < sizeof(SERIAL_TIMEOUTS))
320
0
        {
321
0
          SetLastError(ERROR_INSUFFICIENT_BUFFER);
322
0
          return FALSE;
323
0
        }
324
325
0
        if (!pServerSerialDriver->get_timeouts(pComm, pHandflow))
326
0
          return FALSE;
327
328
0
        *lpBytesReturned = sizeof(SERIAL_TIMEOUTS);
329
0
        return TRUE;
330
0
      }
331
0
      break;
332
0
    }
333
0
    case IOCTL_SERIAL_SET_DTR:
334
0
    {
335
0
      if (pServerSerialDriver->set_dtr)
336
0
      {
337
0
        return pServerSerialDriver->set_dtr(pComm);
338
0
      }
339
0
      break;
340
0
    }
341
0
    case IOCTL_SERIAL_CLR_DTR:
342
0
    {
343
0
      if (pServerSerialDriver->clear_dtr)
344
0
      {
345
0
        return pServerSerialDriver->clear_dtr(pComm);
346
0
      }
347
0
      break;
348
0
    }
349
0
    case IOCTL_SERIAL_SET_RTS:
350
0
    {
351
0
      if (pServerSerialDriver->set_rts)
352
0
      {
353
0
        return pServerSerialDriver->set_rts(pComm);
354
0
      }
355
0
      break;
356
0
    }
357
0
    case IOCTL_SERIAL_CLR_RTS:
358
0
    {
359
0
      if (pServerSerialDriver->clear_rts)
360
0
      {
361
0
        return pServerSerialDriver->clear_rts(pComm);
362
0
      }
363
0
      break;
364
0
    }
365
0
    case IOCTL_SERIAL_GET_MODEMSTATUS:
366
0
    {
367
0
      if (pServerSerialDriver->get_modemstatus)
368
0
      {
369
0
        ULONG* pRegister = (ULONG*)lpOutBuffer;
370
371
0
        WINPR_ASSERT(nOutBufferSize >= sizeof(ULONG));
372
0
        if (nOutBufferSize < sizeof(ULONG))
373
0
        {
374
0
          SetLastError(ERROR_INSUFFICIENT_BUFFER);
375
0
          return FALSE;
376
0
        }
377
378
0
        if (!pServerSerialDriver->get_modemstatus(pComm, pRegister))
379
0
          return FALSE;
380
381
0
        CommLog_Print(WLOG_DEBUG, "modem status %s" PRIx32,
382
0
                      comm_ioctl_modem_status_string(*pRegister, buffer, sizeof(buffer)));
383
0
        *lpBytesReturned = sizeof(ULONG);
384
0
        return TRUE;
385
0
      }
386
0
      break;
387
0
    }
388
0
    case IOCTL_SERIAL_SET_WAIT_MASK:
389
0
    {
390
0
      if (pServerSerialDriver->set_wait_mask)
391
0
      {
392
0
        ULONG* pWaitMask = (ULONG*)lpInBuffer;
393
394
0
        WINPR_ASSERT(nInBufferSize >= sizeof(ULONG));
395
0
        if (nInBufferSize < sizeof(ULONG))
396
0
        {
397
0
          SetLastError(ERROR_INVALID_PARAMETER);
398
0
          return FALSE;
399
0
        }
400
401
0
        const BOOL rc = pServerSerialDriver->set_wait_mask(pComm, pWaitMask);
402
0
        CommLog_Print(WLOG_DEBUG, "set_wait_mask %s -> %d",
403
0
                      CommSerialEvString(*pWaitMask, buffer, sizeof(buffer)), rc);
404
0
        return rc;
405
0
      }
406
0
      break;
407
0
    }
408
0
    case IOCTL_SERIAL_GET_WAIT_MASK:
409
0
    {
410
0
      if (pServerSerialDriver->get_wait_mask)
411
0
      {
412
0
        ULONG* pWaitMask = (ULONG*)lpOutBuffer;
413
414
0
        WINPR_ASSERT(nOutBufferSize >= sizeof(ULONG));
415
0
        if (nOutBufferSize < sizeof(ULONG))
416
0
        {
417
0
          SetLastError(ERROR_INSUFFICIENT_BUFFER);
418
0
          return FALSE;
419
0
        }
420
421
0
        if (!pServerSerialDriver->get_wait_mask(pComm, pWaitMask))
422
0
          return FALSE;
423
424
0
        CommLog_Print(WLOG_DEBUG, "get_wait_mask %s",
425
0
                      CommSerialEvString(*pWaitMask, buffer, sizeof(buffer)));
426
0
        *lpBytesReturned = sizeof(ULONG);
427
0
        return TRUE;
428
0
      }
429
0
      break;
430
0
    }
431
0
    case IOCTL_SERIAL_WAIT_ON_MASK:
432
0
    {
433
0
      if (pServerSerialDriver->wait_on_mask)
434
0
      {
435
0
        ULONG* pOutputMask = (ULONG*)lpOutBuffer;
436
437
0
        WINPR_ASSERT(nOutBufferSize >= sizeof(ULONG));
438
0
        if (nOutBufferSize < sizeof(ULONG))
439
0
        {
440
0
          SetLastError(ERROR_INSUFFICIENT_BUFFER);
441
0
          return FALSE;
442
0
        }
443
444
0
        const BOOL rc = pServerSerialDriver->wait_on_mask(pComm, pOutputMask);
445
446
0
        *lpBytesReturned = sizeof(ULONG);
447
0
        CommLog_Print(WLOG_DEBUG, "wait_on_mask %s -> %d",
448
0
                      CommSerialEvString(*pOutputMask, buffer, sizeof(buffer)), rc);
449
0
        return rc;
450
0
      }
451
0
      break;
452
0
    }
453
0
    case IOCTL_SERIAL_SET_QUEUE_SIZE:
454
0
    {
455
0
      if (pServerSerialDriver->set_queue_size)
456
0
      {
457
0
        SERIAL_QUEUE_SIZE* pQueueSize = (SERIAL_QUEUE_SIZE*)lpInBuffer;
458
459
0
        WINPR_ASSERT(nInBufferSize >= sizeof(SERIAL_QUEUE_SIZE));
460
0
        if (nInBufferSize < sizeof(SERIAL_QUEUE_SIZE))
461
0
        {
462
0
          SetLastError(ERROR_INVALID_PARAMETER);
463
0
          return FALSE;
464
0
        }
465
466
0
        return pServerSerialDriver->set_queue_size(pComm, pQueueSize);
467
0
      }
468
0
      break;
469
0
    }
470
0
    case IOCTL_SERIAL_PURGE:
471
0
    {
472
0
      if (pServerSerialDriver->purge)
473
0
      {
474
0
        ULONG* pPurgeMask = (ULONG*)lpInBuffer;
475
476
0
        WINPR_ASSERT(nInBufferSize >= sizeof(ULONG));
477
0
        if (nInBufferSize < sizeof(ULONG))
478
0
        {
479
0
          SetLastError(ERROR_INVALID_PARAMETER);
480
0
          return FALSE;
481
0
        }
482
483
0
        return pServerSerialDriver->purge(pComm, pPurgeMask);
484
0
      }
485
0
      break;
486
0
    }
487
0
    case IOCTL_SERIAL_GET_COMMSTATUS:
488
0
    {
489
0
      if (pServerSerialDriver->get_commstatus)
490
0
      {
491
0
        SERIAL_STATUS* pCommstatus = (SERIAL_STATUS*)lpOutBuffer;
492
493
0
        WINPR_ASSERT(nOutBufferSize >= sizeof(SERIAL_STATUS));
494
0
        if (nOutBufferSize < sizeof(SERIAL_STATUS))
495
0
        {
496
0
          SetLastError(ERROR_INSUFFICIENT_BUFFER);
497
0
          return FALSE;
498
0
        }
499
500
0
        if (!pServerSerialDriver->get_commstatus(pComm, pCommstatus))
501
0
          return FALSE;
502
503
0
        *lpBytesReturned = sizeof(SERIAL_STATUS);
504
0
        return TRUE;
505
0
      }
506
0
      break;
507
0
    }
508
0
    case IOCTL_SERIAL_SET_BREAK_ON:
509
0
    {
510
0
      if (pServerSerialDriver->set_break_on)
511
0
      {
512
0
        return pServerSerialDriver->set_break_on(pComm);
513
0
      }
514
0
      break;
515
0
    }
516
0
    case IOCTL_SERIAL_SET_BREAK_OFF:
517
0
    {
518
0
      if (pServerSerialDriver->set_break_off)
519
0
      {
520
0
        return pServerSerialDriver->set_break_off(pComm);
521
0
      }
522
0
      break;
523
0
    }
524
0
    case IOCTL_SERIAL_SET_XOFF:
525
0
    {
526
0
      if (pServerSerialDriver->set_xoff)
527
0
      {
528
0
        return pServerSerialDriver->set_xoff(pComm);
529
0
      }
530
0
      break;
531
0
    }
532
0
    case IOCTL_SERIAL_SET_XON:
533
0
    {
534
0
      if (pServerSerialDriver->set_xon)
535
0
      {
536
0
        return pServerSerialDriver->set_xon(pComm);
537
0
      }
538
0
      break;
539
0
    }
540
0
    case IOCTL_SERIAL_GET_DTRRTS:
541
0
    {
542
0
      if (pServerSerialDriver->get_dtrrts)
543
0
      {
544
0
        ULONG* pMask = (ULONG*)lpOutBuffer;
545
546
0
        WINPR_ASSERT(nOutBufferSize >= sizeof(ULONG));
547
0
        if (nOutBufferSize < sizeof(ULONG))
548
0
        {
549
0
          SetLastError(ERROR_INSUFFICIENT_BUFFER);
550
0
          return FALSE;
551
0
        }
552
553
0
        if (!pServerSerialDriver->get_dtrrts(pComm, pMask))
554
0
          return FALSE;
555
556
0
        *lpBytesReturned = sizeof(ULONG);
557
0
        return TRUE;
558
0
      }
559
0
      break;
560
0
    }
561
0
    case IOCTL_SERIAL_CONFIG_SIZE:
562
0
    {
563
0
      if (pServerSerialDriver->config_size)
564
0
      {
565
0
        ULONG* pSize = (ULONG*)lpOutBuffer;
566
567
0
        WINPR_ASSERT(nOutBufferSize >= sizeof(ULONG));
568
0
        if (nOutBufferSize < sizeof(ULONG))
569
0
        {
570
0
          SetLastError(ERROR_INSUFFICIENT_BUFFER);
571
0
          return FALSE;
572
0
        }
573
574
0
        if (!pServerSerialDriver->config_size(pComm, pSize))
575
0
          return FALSE;
576
577
0
        *lpBytesReturned = sizeof(ULONG);
578
0
        return TRUE;
579
0
      }
580
0
      break;
581
0
    }
582
0
    case IOCTL_SERIAL_IMMEDIATE_CHAR:
583
0
    {
584
0
      if (pServerSerialDriver->immediate_char)
585
0
      {
586
0
        UCHAR* pChar = (UCHAR*)lpInBuffer;
587
588
0
        WINPR_ASSERT(nInBufferSize >= sizeof(UCHAR));
589
0
        if (nInBufferSize < sizeof(UCHAR))
590
0
        {
591
0
          SetLastError(ERROR_INVALID_PARAMETER);
592
0
          return FALSE;
593
0
        }
594
595
0
        return pServerSerialDriver->immediate_char(pComm, pChar);
596
0
      }
597
0
      break;
598
0
    }
599
0
    case IOCTL_SERIAL_RESET_DEVICE:
600
0
    {
601
0
      if (pServerSerialDriver->reset_device)
602
0
      {
603
0
        return pServerSerialDriver->reset_device(pComm);
604
0
      }
605
0
      break;
606
0
    }
607
0
    default:
608
0
      break;
609
0
  }
610
611
0
  CommLog_Print(
612
0
      WLOG_WARN, _T("unsupported IoControlCode=[0x%08" PRIX32 "] %s (remote serial driver: %s)"),
613
0
      dwIoControlCode, _comm_serial_ioctl_name(dwIoControlCode), pServerSerialDriver->name);
614
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED); /* => STATUS_NOT_IMPLEMENTED */
615
0
  return FALSE;
616
0
}
617
618
/**
619
 * FIXME: to be used through winpr-io's DeviceIoControl
620
 *
621
 * Any previous error as returned by GetLastError is cleared.
622
 *
623
 * ERRORS:
624
 *   ERROR_INVALID_HANDLE
625
 *   ERROR_INVALID_PARAMETER
626
 *   ERROR_NOT_SUPPORTED lpOverlapped is not supported
627
 *   ERROR_INSUFFICIENT_BUFFER
628
 *   ERROR_CALL_NOT_IMPLEMENTED unimplemented ioctl
629
 */
630
BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer,
631
                         DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize,
632
                         LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped)
633
0
{
634
0
  WINPR_COMM* pComm = (WINPR_COMM*)hDevice;
635
0
  BOOL result = 0;
636
637
0
  if (hDevice == INVALID_HANDLE_VALUE)
638
0
  {
639
0
    SetLastError(ERROR_INVALID_HANDLE);
640
0
    return FALSE;
641
0
  }
642
643
0
  if (!CommIsHandled(hDevice))
644
0
    return FALSE;
645
646
0
  if (!pComm->fd)
647
0
  {
648
0
    SetLastError(ERROR_INVALID_HANDLE);
649
0
    return FALSE;
650
0
  }
651
652
0
  result = s_CommDeviceIoControl(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer,
653
0
                                 nOutBufferSize, lpBytesReturned, lpOverlapped);
654
655
0
  if (lpBytesReturned && *lpBytesReturned != nOutBufferSize)
656
0
  {
657
    /* This might be a hint for a bug, especially when result==TRUE */
658
0
    CommLog_Print(WLOG_WARN,
659
0
                  "IoControlCode=[0x%08" PRIX32 "] %s: lpBytesReturned=%" PRIu32
660
0
                  " and nOutBufferSize=%" PRIu32 " are different!",
661
0
                  dwIoControlCode, _comm_serial_ioctl_name(dwIoControlCode), *lpBytesReturned,
662
0
                  nOutBufferSize);
663
0
  }
664
665
0
  if (pComm->permissive)
666
0
  {
667
0
    if (!result)
668
0
    {
669
0
      CommLog_Print(WLOG_WARN,
670
0
                    "[permissive]: IoControlCode=[0x%08" PRIX32 "] %s failed, ignoring",
671
0
                    dwIoControlCode, _comm_serial_ioctl_name(dwIoControlCode));
672
0
    }
673
674
0
    return TRUE; /* always! */
675
0
  }
676
677
0
  return result;
678
0
}
679
680
int comm_ioctl_tcsetattr(int fd, int optional_actions, const struct termios* termios_p)
681
0
{
682
0
  struct termios currentState = { 0 };
683
0
  size_t count = 0;
684
0
  do
685
0
  {
686
0
    const int src = tcsetattr(fd, optional_actions, termios_p);
687
0
    if (src < 0)
688
0
    {
689
0
      char buffer[64] = { 0 };
690
0
      CommLog_Print(WLOG_WARN, "[%" PRIuz "] tcsetattr failure, errno: %s [%d]", count,
691
0
                    winpr_strerror(errno, buffer, sizeof(buffer)), errno);
692
0
      return src;
693
0
    }
694
695
    /* NB: tcsetattr() can succeed even if not all changes have been applied. */
696
0
    const int rrc = tcgetattr(fd, &currentState);
697
0
    if (rrc < 0)
698
0
    {
699
0
      char buffer[64] = { 0 };
700
0
      CommLog_Print(WLOG_WARN, "[%" PRIuz "] tcgetattr failure, errno: %s [%d]", count,
701
0
                    winpr_strerror(errno, buffer, sizeof(buffer)), errno);
702
0
      return rrc;
703
0
    }
704
    // NOLINTNEXTLINE(bugprone-suspicious-memory-comparison,cert-exp42-c,cert-flp37-c)
705
0
  } while ((memcmp(&currentState, termios_p, sizeof(struct termios)) != 0) && (count++ < 2));
706
707
0
  return 0;
708
0
}
709
710
static const char* comm_ioctl_modem_flag_str(ULONG flag)
711
0
{
712
0
  switch (flag)
713
0
  {
714
0
    case SERIAL_MSR_DCTS:
715
0
      return "SERIAL_MSR_DCTS";
716
0
    case SERIAL_MSR_DDSR:
717
0
      return "SERIAL_MSR_DDSR";
718
0
    case SERIAL_MSR_TERI:
719
0
      return "SERIAL_MSR_TERI";
720
0
    case SERIAL_MSR_DDCD:
721
0
      return "SERIAL_MSR_DDCD";
722
0
    case SERIAL_MSR_CTS:
723
0
      return "SERIAL_MSR_CTS";
724
0
    case SERIAL_MSR_DSR:
725
0
      return "SERIAL_MSR_DSR";
726
0
    case SERIAL_MSR_RI:
727
0
      return "SERIAL_MSR_RI";
728
0
    case SERIAL_MSR_DCD:
729
0
      return "SERIAL_MSR_DCD";
730
0
    default:
731
0
      return "SERIAL_MSR_UNKNOWN";
732
0
  }
733
0
}
734
735
const char* comm_ioctl_modem_status_string(ULONG status, char* buffer, size_t size)
736
0
{
737
0
  const ULONG flags[] = { SERIAL_MSR_DCTS, SERIAL_MSR_DDSR, SERIAL_MSR_TERI, SERIAL_MSR_DDCD,
738
0
                        SERIAL_MSR_CTS,  SERIAL_MSR_DSR,  SERIAL_MSR_RI,   SERIAL_MSR_DCD
739
740
0
  };
741
0
  winpr_str_append("{", buffer, size, "");
742
743
0
  const char* sep = "";
744
0
  for (size_t x = 0; x < ARRAYSIZE(flags); x++)
745
0
  {
746
0
    const ULONG flag = flags[x];
747
0
    if (status & flag)
748
0
    {
749
0
      winpr_str_append(comm_ioctl_modem_flag_str(flag), buffer, size, sep);
750
0
      sep = "|";
751
0
    }
752
0
  }
753
754
0
  char number[32] = { 0 };
755
0
  (void)_snprintf(number, sizeof(number), "}[0x%08" PRIx32 "]", status);
756
0
  winpr_str_append(number, buffer, size, "");
757
0
  return buffer;
758
0
}