WvStreams
streams.cc
1 #include "streams.h"
2 #include "wvstring.h"
3 #include <assert.h>
4 #include <errno.h>
5 #include <conio.h>
6 #include <stdio.h>
7 #include <io.h>
8 
9 #if _MSC_VER
10 // MS Visual C++ doesn't support varags preproc macros
11 # define DPRINTF
12 #else
13 #if 0
14 # define DPRINTF(x, args...) do { \
15  printf(x, ## args); fflush(stdout); \
16  } while (0)
17 #else
18 # define DPRINTF(x, args...) do { } while(0)
19 #endif
20 #endif
21 
22 
23 // this class changes the default libc stdout buffering to "line buffered"
24 // and stderr to "unbuffered", like they should be in any sane system.
25 // Apparently they start off as "fully buffered" in most Windows systems.
27 {
28 public:
30  {
31  setvbuf(stdout, NULL, _IOLBF, 0);
32  setvbuf(stderr, NULL, _IONBF, 0);
33  }
34 };
35 static FixLibcIoBuffers fixbufs;
36 
37 
38 // these versions of close/read/write try to work with both sockets and
39 // msvcrt file descriptors! (I hope we never get a socket with the same
40 // VALUE as a file descriptor!)
41 
42 
43 static void errcode(int err)
44 {
45  if (err == EIO)
46  err = EBADF; // sometimes we get EIO when Unix would be EBADF
47  if (err == WSAENOTSOCK)
48  err = EBADF; // if it's not a socket, it's also not a fd
49  SetLastError(err);
50  errno = err;
51 }
52 
53 
54 static bool is_socket(int fd)
55 {
56  // if _get_osfhandle doesn't work, it must not be a fd, so assume it's
57  // a socket.
58  return (HANDLE)_get_osfhandle(fd) == INVALID_HANDLE_VALUE;
59 }
60 
61 
62 int close(int fd)
63 {
64  int ret;
65  if (is_socket(fd))
66  {
67  ret = closesocket(fd);
68  errcode(GetLastError());
69  }
70  else
71  {
72  ret = _close(fd);
73  errcode(errno);
74  }
75  return ret;
76 }
77 
78 
79 int read(int fd, void *buf, size_t count)
80 {
81  int ret;
82  if (is_socket(fd))
83  {
84  ret = recv(fd, (char *)buf, count, 0);
85  errcode(GetLastError());
86  }
87  else
88  {
89  ret = _read(fd, buf, count);
90  errcode(errno);
91  }
92  return ret;
93 }
94 
95 
96 int write(int fd, const void *buf, size_t count)
97 {
98  int ret;
99  if (is_socket(fd))
100  {
101  ret = send(fd, (char *)buf, count, 0);
102  errcode(GetLastError());
103  }
104  else
105  {
106  ret = _write(fd, buf, count);
107  errcode(errno);
108  }
109  return ret;
110 }
111 
112 
113 int socketpair(int family, int type, int protocol, int *sb)
114 {
115  SOCKET insock, outsock, newsock;
116  struct sockaddr_in sock_in;
117 
118  if (type != SOCK_STREAM)
119  return -1;
120 
121  newsock = socket(AF_INET, type, 0);
122  if (newsock == INVALID_SOCKET)
123  return -1;
124 
125  sock_in.sin_family = AF_INET;
126  sock_in.sin_port = 0;
127  sock_in.sin_addr.s_addr = INADDR_ANY;
128  if (bind(newsock, (struct sockaddr *) &sock_in, sizeof (sock_in)) < 0)
129  return -1;
130 
131  int len = sizeof (sock_in);
132  if (getsockname(newsock, (struct sockaddr *)&sock_in, &len) < 0)
133  return -1;
134 
135  if (listen(newsock, 2) < 0)
136  return -1;
137 
138  outsock = socket(AF_INET, type, 0);
139  if (outsock == INVALID_SOCKET)
140  return -1;
141 
142  sock_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
143  if (connect(outsock, (struct sockaddr *)&sock_in, sizeof(sock_in)) < 0)
144  return -1;
145 
146  /* For stream sockets, accept the connection and close the listener */
147  len = sizeof(sock_in);
148  insock = accept(newsock, (struct sockaddr *)&sock_in, &len);
149  if (insock == INVALID_SOCKET)
150  return -1;
151 
152  if (closesocket(newsock) < 0)
153  return -1;
154 
155  sb[0] = insock;
156  sb[1] = outsock;
157  return 0;
158 }
159 
160 
161 static void CALLBACK completion(DWORD error, DWORD nread, LPOVERLAPPED ov)
162 {
163 }
164 
165 
166 static size_t fake_read(int fd, void *buf, size_t len)
167 {
168  HANDLE h = (HANDLE)_get_osfhandle(fd);
169  INPUT_RECORD p;
170  DPRINTF("fake_read(%d/%d,%p,%d) = ", fd, (int)h, buf, (int)len);
171 
172  DWORD ret = 0;
173  OVERLAPPED ov;
174  memset(&ov, 0, sizeof(ov));
175  ov.Offset = SetFilePointer(h, 0, NULL, FILE_CURRENT);
176 
177  if (PeekNamedPipe(h, NULL, 0, NULL, &ret, NULL))
178  {
179  // cygwin sshd/telnetd uses named pipes for stdin. We have to
180  // support these separately. Getting stuck in ReadFile on a named
181  // pipe appears to freeze up gethostbyname() for some reason on win2k!
182  DPRINTF("(stdin is a pipe)\n");
183  while (PeekNamedPipe(h, NULL, 0, NULL, &ret, NULL) && !ret)
184  {
185  DPRINTF(".");
186  Sleep(100);
187  }
188  ReadFile(h, buf, len, &ret, NULL);
189  }
190  else if (PeekConsoleInput(h, &p, 1, &ret))
191  {
192  // a typical stdin/out pair refers to a console. Unfortunately,
193  // console I/O is stupid: you can poll it to see if it's ready, but
194  // if you have it in line mode, then it's not *really* ready.
195  // ReadConsole/ReadFile will only return after the user hits enter.
196  // Unfortunately, it seems the only way around this is to disable
197  // line/echo mode and fake it ourselves. Hopefully this isn't too
198  // ugly...
199  DPRINTF("(stdin is a console)\n");
200 
201  size_t used = 0;
202  char *xbuf = (char *)buf;
203  HANDLE hout = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE,
204  FILE_SHARE_READ | FILE_SHARE_WRITE,
205  NULL, OPEN_EXISTING, 0, 0);
206 
207  DWORD conmode = 0;
208  GetConsoleMode(h, &conmode);
209  SetConsoleMode(h, conmode &
210  ~(ENABLE_LINE_INPUT | ENABLE_MOUSE_INPUT | ENABLE_ECHO_INPUT));
211 
212  while (PeekConsoleInput(h, &p, 1, &ret))
213  {
214  DWORD tmp;
215  if (ret)
216  {
217  ReadConsoleInput(h, &p, 1, &ret);
218  assert(ret);
219  if (p.EventType == KEY_EVENT && p.Event.KeyEvent.bKeyDown)
220  {
221  int key = p.Event.KeyEvent.uChar.AsciiChar;
222  if (key == '\r') // end of line
223  {
224  xbuf[used++] = '\n';
225  WriteConsole(hout, "\r\n", 2, &tmp, NULL);
226  ret = used;
227  break;
228  }
229  else if (key == '\b' && used > 0)
230  {
231  used--;
232  WriteConsole(hout, "\b \b", 3, &tmp, NULL);
233  }
234  else if (key && used < len-1)
235  {
236  xbuf[used++] = key;
237  WriteConsole(hout, xbuf+used-1, 1, &tmp, NULL);
238  }
239  }
240  }
241  else
242  {
243  DPRINTF(".");
244  WaitForSingleObjectEx(h, 1000, true);
245  }
246  }
247 
248  CloseHandle(hout);
249  }
250  else
251  {
252  // stdin might be redirected from a file, in which case we can
253  // probably safely (heh) assume it'll never block. Still, try
254  // ReadFileEx with a timeout first and see if that works.
255  DPRINTF("(stdin is a file)\n");
256  while (!ret)
257  {
258  DPRINTF(".");
259  int rv = 0;
260  if (ReadFileEx(h, buf, 0, &ov, &completion))
261  {
262  rv = SleepEx(1000, true);
263  CancelIo(h);
264  DPRINTF("(rv is %d)\n", rv);
265  if (rv == WAIT_IO_COMPLETION)
266  {
267  ReadFile(h, buf, len, &ret, NULL);
268  break;
269  }
270  else if (!rv) // timed out
271  Sleep(1); // ensure lock is released for nonzero time (1ms)
272  else
273  return 0; // unknown problem: assume EOF
274  }
275  else
276  {
277  // can't do ReadFileEx: probably stupid Win9x.
278  ReadFile(h, buf, len, &ret, NULL);
279  break;
280  }
281  }
282  }
283 
284  DPRINTF("[%d]\n", ret);
285  return ret;
286 }
287 
288 
289 DWORD WINAPI fd2socket_fwd(LPVOID lpThreadParameter)
290 {
291 // return 0;
292  DWORD retval = 0;
293  const int BUFSIZE = 512;
294  socket_fd_pair *pair = (socket_fd_pair *)lpThreadParameter;
295 
296  // fprintf(stderr, "forwarding %d -> %d\n",
297  // pair->fd, pair->socket); fflush(stderr);
298 
299  char buf[BUFSIZE];
300  while (true)
301  {
302  char *ptr = buf;
303 
304  size_t bytes = fake_read(pair->fd, ptr, BUFSIZE);
305  if (bytes <= 0) { retval = bytes; break; }
306  while (bytes > 0)
307  {
308  int written = send(pair->socket, ptr, bytes, 0);
309  if (written < 0) { retval = written; break; }
310 
311  bytes -= written;
312  ptr += written;
313  }
314  }
315 
316  shutdown(pair->socket, SD_BOTH);
317  closesocket(pair->socket);
318  // fprintf(stderr, "TERMINATING-%d\n", pair->fd); fflush(stderr);
319  return retval;
320 }
321 
322 
323 DWORD WINAPI socket2fd_fwd(LPVOID lpThreadParameter)
324 {
325  DWORD retval = 0;
326  const int BUFSIZE = 512;
327  socket_fd_pair *pair = (socket_fd_pair *)lpThreadParameter;
328 
329  char buf[BUFSIZE];
330  while (true)
331  {
332  char *ptr = buf;
333  int bytes = recv(pair->socket, ptr, BUFSIZE, 0);
334  if (bytes <= 0) { retval = bytes; break; }
335  while (bytes > 0)
336  {
337  int written = _write(pair->fd, ptr, bytes);
338  if (written < 0) { retval = written; break; }
339  bytes -= written;
340  ptr += written;
341  }
342  }
343  shutdown(pair->socket, SD_BOTH);
344  closesocket(pair->socket);
345  // fprintf(stderr, "TERMINATING-%d\n", pair->fd); fflush(stderr);
346  return retval;
347 }
348 
349 
350 SocketFromFDMaker::SocketFromFDMaker(int fd,
351  LPTHREAD_START_ROUTINE lpStartAddress, bool wait)
352  : m_hThread(0), m_socket(INVALID_SOCKET), m_wait(wait)
353 {
354  // might do this twice
355  WSAData wsaData;
356  WSAStartup(MAKEWORD(2,0), &wsaData);
357 
358  int s[2], result;
359  result = socketpair(AF_INET, SOCK_STREAM, 0, s);
360  assert(result == 0);
361 
362  m_pair.fd = fd;
363  m_pair.socket = s[0];
364  m_socket = s[1];
365 
366  DWORD threadid;
367  m_hThread = CreateThread(
368  NULL,
369  0,
370  lpStartAddress,
371  &m_pair,
372  0,
373  &threadid
374  );
375  assert(m_hThread);
376 }
377 
378 
379 SocketFromFDMaker::~SocketFromFDMaker()
380 {
381  int result;
382  // fprintf(stderr, "shutting down #%d\n", m_socket);
383  if (m_socket != INVALID_SOCKET)
384  {
385  result = shutdown(m_socket, SD_BOTH);
386 
387  // this assertion will fail if someone has already closed the
388  // socket; eg. if you give the socket to a WvFDStream and then let
389  // him close it. But you shouldn't do that, because nobody is
390  // supposed to close stdin/stdout/stderr!
391  if (result != 0)
392  {
393  int e = GetLastError();
394  if (e == WSASYSNOTREADY || e == WSANOTINITIALISED)
395  {
396  fprintf(stderr, "Abnormal termination. Skipping cleanup.\n");
397  _exit(42);
398  }
399  else
400  {
401  fprintf(stderr,
402  "ERROR! Socket #%d was already shut down! (%d)\n",
403  m_socket, e);
404  assert(result == 0);
405  }
406  }
407 
408  if (m_wait) // wait for socket->fd copier
409  {
410  // wait for thread to terminate. Since it's reading from a
411  // socket (m_wait==true), this will be safe, because we know
412  // it'll die politely when it should.
413  WaitForSingleObject(m_hThread, INFINITE);
414  }
415  else
416  {
417  // FIXME: fd->socket copier will never die politely. It gets
418  // stuck in _read(), which enters a critical section and
419  // then blocks until input is available. Unfortunately, it's
420  // impossible to make input *not* available.
421  //
422  // TerminateThread() is generally evil, and doesn't help here
423  // anyway: it just leaves that critical section locked forever, so
424  // no operation on that fd will *ever* finish.
425  //
426  // Answer: just do nothing. Someone will clean up the broken
427  // thread eventually, I guess. (ExitProcess() claims to do
428  // this, but I hope it doesn't get stuck in a critical section...)
429  }
430 
431  close(m_socket);
432  }
433  CloseHandle(m_hThread);
434 }
socket_fd_pair
Definition: streams.h:24
FixLibcIoBuffers
Definition: streams.cc:26