WvStreams
streams.cc
00001 #include "streams.h"
00002 #include "wvstring.h"
00003 #include <assert.h>
00004 #include <errno.h>
00005 #include <conio.h>
00006 #include <stdio.h>
00007 #include <io.h>
00008 
00009 #if _MSC_VER
00010 // MS Visual C++ doesn't support varags preproc macros
00011 # define DPRINTF
00012 #else
00013 #if 0
00014 # define DPRINTF(x, args...) do { \
00015         printf(x, ## args); fflush(stdout); \
00016     } while (0)
00017 #else
00018 # define DPRINTF(x, args...) do { } while(0)
00019 #endif
00020 #endif
00021 
00022 
00023 // this class changes the default libc stdout buffering to "line buffered"
00024 // and stderr to "unbuffered", like they should be in any sane system.
00025 // Apparently they start off as "fully buffered" in most Windows systems.
00026 class FixLibcIoBuffers
00027 {
00028 public:
00029     FixLibcIoBuffers()
00030     {
00031         setvbuf(stdout, NULL, _IOLBF, 0);
00032         setvbuf(stderr, NULL, _IONBF, 0);
00033     }
00034 };
00035 static FixLibcIoBuffers fixbufs;
00036 
00037 
00038 // these versions of close/read/write try to work with both sockets and
00039 // msvcrt file descriptors! (I hope we never get a socket with the same
00040 // VALUE as a file descriptor!)
00041  
00042 
00043 static void errcode(int err)
00044 {
00045     if (err == EIO)
00046         err = EBADF; // sometimes we get EIO when Unix would be EBADF
00047     if (err == WSAENOTSOCK)
00048         err = EBADF; // if it's not a socket, it's also not a fd
00049     SetLastError(err);
00050     errno = err;
00051 }
00052 
00053 
00054 static bool is_socket(int fd)
00055 {
00056     // if _get_osfhandle doesn't work, it must not be a fd, so assume it's
00057     // a socket.
00058     return (HANDLE)_get_osfhandle(fd) == INVALID_HANDLE_VALUE;
00059 }
00060 
00061 
00062 int close(int fd)
00063 {
00064     int ret;
00065     if (is_socket(fd))
00066     {
00067         ret = closesocket(fd);
00068         errcode(GetLastError());
00069     }
00070     else
00071     {
00072         ret = _close(fd);
00073         errcode(errno);
00074     }
00075     return ret;
00076 }
00077 
00078 
00079 int read(int fd, void *buf, size_t count)
00080 {
00081     int ret;
00082     if (is_socket(fd))
00083     {
00084         ret = recv(fd, (char *)buf, count, 0);
00085         errcode(GetLastError());
00086     }
00087     else
00088     {
00089         ret = _read(fd, buf, count);
00090         errcode(errno);
00091     }
00092     return ret;
00093 }
00094 
00095 
00096 int write(int fd, const void *buf, size_t count)
00097 {
00098     int ret;
00099     if (is_socket(fd))
00100     {
00101         ret = send(fd, (char *)buf, count, 0);
00102         errcode(GetLastError());
00103     }
00104     else
00105     {
00106         ret = _write(fd, buf, count);
00107         errcode(errno);
00108     }
00109     return ret;
00110 }
00111 
00112 
00113 int socketpair(int family, int type, int protocol, int *sb)
00114 {
00115     SOCKET insock, outsock, newsock;
00116     struct sockaddr_in sock_in;
00117 
00118     if (type != SOCK_STREAM)
00119         return -1;
00120 
00121     newsock = socket(AF_INET, type, 0);
00122     if (newsock == INVALID_SOCKET)
00123         return -1;
00124 
00125     sock_in.sin_family = AF_INET;
00126     sock_in.sin_port = 0;
00127     sock_in.sin_addr.s_addr = INADDR_ANY;
00128     if (bind(newsock, (struct sockaddr *) &sock_in, sizeof (sock_in)) < 0)
00129         return -1;
00130 
00131     int len = sizeof (sock_in);
00132     if (getsockname(newsock, (struct sockaddr *)&sock_in, &len) < 0)
00133         return -1;
00134 
00135     if (listen(newsock, 2) < 0)
00136         return -1;
00137 
00138     outsock = socket(AF_INET, type, 0);
00139     if (outsock == INVALID_SOCKET)
00140         return -1;
00141 
00142     sock_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
00143     if (connect(outsock, (struct sockaddr *)&sock_in, sizeof(sock_in)) < 0)
00144         return -1;
00145 
00146     /* For stream sockets, accept the connection and close the listener */
00147     len = sizeof(sock_in);
00148     insock = accept(newsock, (struct sockaddr *)&sock_in, &len);
00149     if (insock == INVALID_SOCKET)
00150         return -1;
00151 
00152     if (closesocket(newsock) < 0)
00153         return -1;
00154 
00155     sb[0] = insock;
00156     sb[1] = outsock;
00157     return 0;
00158 }
00159 
00160 
00161 static void CALLBACK completion(DWORD error, DWORD nread, LPOVERLAPPED ov)
00162 {
00163 }
00164 
00165 
00166 static size_t fake_read(int fd, void *buf, size_t len)
00167 {
00168     HANDLE h = (HANDLE)_get_osfhandle(fd);
00169     INPUT_RECORD p;
00170     DPRINTF("fake_read(%d/%d,%p,%d) = ", fd, (int)h, buf, (int)len);
00171     
00172     DWORD ret = 0;
00173     OVERLAPPED ov;
00174     memset(&ov, 0, sizeof(ov));
00175     ov.Offset = SetFilePointer(h, 0, NULL, FILE_CURRENT);
00176     
00177     if (PeekNamedPipe(h, NULL, 0, NULL, &ret, NULL))
00178     {
00179         // cygwin sshd/telnetd uses named pipes for stdin.  We have to
00180         // support these separately.  Getting stuck in ReadFile on a named
00181         // pipe appears to freeze up gethostbyname() for some reason on win2k!
00182         DPRINTF("(stdin is a pipe)\n");
00183         while (PeekNamedPipe(h, NULL, 0, NULL, &ret, NULL) && !ret)
00184         {
00185             DPRINTF(".");
00186             Sleep(100);
00187         }
00188         ReadFile(h, buf, len, &ret, NULL);
00189     }
00190     else if (PeekConsoleInput(h, &p, 1, &ret))
00191     {
00192         // a typical stdin/out pair refers to a console.  Unfortunately,
00193         // console I/O is stupid: you can poll it to see if it's ready, but
00194         // if you have it in line mode, then it's not *really* ready.
00195         // ReadConsole/ReadFile will only return after the user hits enter.
00196         // Unfortunately, it seems the only way around this is to disable
00197         // line/echo mode and fake it ourselves.  Hopefully this isn't too
00198         // ugly...
00199         DPRINTF("(stdin is a console)\n");
00200         
00201         size_t used = 0;
00202         char *xbuf = (char *)buf;
00203         HANDLE hout = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE,
00204                                  FILE_SHARE_READ | FILE_SHARE_WRITE,
00205                                  NULL, OPEN_EXISTING, 0, 0);
00206         
00207         DWORD conmode = 0;
00208         GetConsoleMode(h, &conmode);
00209         SetConsoleMode(h, conmode & 
00210                ~(ENABLE_LINE_INPUT | ENABLE_MOUSE_INPUT | ENABLE_ECHO_INPUT));
00211         
00212         while (PeekConsoleInput(h, &p, 1, &ret))
00213         {
00214             DWORD tmp;
00215             if (ret)
00216             {
00217                 ReadConsoleInput(h, &p, 1, &ret);
00218                 assert(ret);
00219                 if (p.EventType == KEY_EVENT && p.Event.KeyEvent.bKeyDown)
00220                 {
00221                     int key = p.Event.KeyEvent.uChar.AsciiChar;
00222                     if (key == '\r') // end of line
00223                     {
00224                         xbuf[used++] = '\n';
00225                         WriteConsole(hout, "\r\n", 2, &tmp, NULL);
00226                         ret = used;
00227                         break;
00228                     }
00229                     else if (key == '\b' && used > 0)
00230                     {
00231                         used--;
00232                         WriteConsole(hout, "\b \b", 3, &tmp, NULL);
00233                     }
00234                     else if (key && used < len-1)
00235                     {
00236                         xbuf[used++] = key;
00237                         WriteConsole(hout, xbuf+used-1, 1, &tmp, NULL);
00238                     }
00239                 }
00240             }
00241             else
00242             {
00243                 DPRINTF(".");
00244                 WaitForSingleObjectEx(h, 1000, true);
00245             }
00246         }
00247         
00248         CloseHandle(hout);
00249     }
00250     else
00251     {
00252         // stdin might be redirected from a file, in which case we can
00253         // probably safely (heh) assume it'll never block.  Still, try
00254         // ReadFileEx with a timeout first and see if that works.
00255         DPRINTF("(stdin is a file)\n");
00256         while (!ret)
00257         {
00258             DPRINTF(".");
00259             int rv = 0;
00260             if (ReadFileEx(h, buf, 0, &ov, &completion))
00261             {
00262                 rv = SleepEx(1000, true);
00263                 CancelIo(h);
00264                 DPRINTF("(rv is %d)\n", rv);
00265                 if (rv == WAIT_IO_COMPLETION)
00266                 {
00267                     ReadFile(h, buf, len, &ret, NULL);
00268                     break;
00269                 }
00270                 else if (!rv) // timed out
00271                     Sleep(1); // ensure lock is released for nonzero time (1ms)
00272                 else
00273                     return 0; // unknown problem: assume EOF
00274             }
00275             else
00276             {
00277                 // can't do ReadFileEx: probably stupid Win9x.
00278                 ReadFile(h, buf, len, &ret, NULL);
00279                 break;
00280             }
00281         }
00282     }
00283     
00284     DPRINTF("[%d]\n", ret);
00285     return ret;
00286 }
00287 
00288 
00289 DWORD WINAPI fd2socket_fwd(LPVOID lpThreadParameter)
00290 {
00291 //    return 0;
00292     DWORD retval = 0;
00293     const int BUFSIZE = 512;
00294     socket_fd_pair *pair = (socket_fd_pair *)lpThreadParameter;
00295     
00296     // fprintf(stderr, "forwarding %d -> %d\n", 
00297     //         pair->fd, pair->socket); fflush(stderr);
00298     
00299     char buf[BUFSIZE];
00300     while (true)
00301     {
00302         char *ptr = buf;
00303         
00304         size_t bytes = fake_read(pair->fd, ptr, BUFSIZE);
00305         if (bytes <= 0) { retval = bytes; break; }
00306         while (bytes > 0)
00307         {
00308             int written = send(pair->socket, ptr, bytes, 0);
00309             if (written < 0) { retval = written; break; }
00310 
00311             bytes -= written;
00312             ptr += written;
00313         }
00314     }
00315 
00316     shutdown(pair->socket, SD_BOTH);
00317     closesocket(pair->socket);
00318     // fprintf(stderr, "TERMINATING-%d\n", pair->fd); fflush(stderr);
00319     return retval;
00320 }
00321 
00322 
00323 DWORD WINAPI socket2fd_fwd(LPVOID lpThreadParameter)
00324 {
00325     DWORD retval = 0;
00326     const int BUFSIZE = 512;
00327     socket_fd_pair *pair = (socket_fd_pair *)lpThreadParameter;
00328 
00329     char buf[BUFSIZE];
00330     while (true)
00331     {
00332         char *ptr = buf;
00333         int bytes = recv(pair->socket, ptr, BUFSIZE, 0);
00334         if (bytes <= 0) { retval = bytes; break; }
00335         while (bytes > 0)
00336         {
00337             int written = _write(pair->fd, ptr, bytes);
00338             if (written < 0) { retval = written; break; }
00339             bytes -= written;
00340             ptr += written;
00341         }
00342     }
00343     shutdown(pair->socket, SD_BOTH);
00344     closesocket(pair->socket);
00345     // fprintf(stderr, "TERMINATING-%d\n", pair->fd); fflush(stderr);
00346     return retval;
00347 }
00348 
00349 
00350 SocketFromFDMaker::SocketFromFDMaker(int fd,
00351                      LPTHREAD_START_ROUTINE lpStartAddress, bool wait)
00352     : m_hThread(0), m_socket(INVALID_SOCKET), m_wait(wait)
00353 {
00354     // might do this twice
00355     WSAData wsaData;
00356     WSAStartup(MAKEWORD(2,0), &wsaData);
00357 
00358     int s[2], result;
00359     result = socketpair(AF_INET, SOCK_STREAM, 0, s);
00360     assert(result == 0);
00361 
00362     m_pair.fd = fd;
00363     m_pair.socket = s[0];
00364     m_socket = s[1];
00365 
00366     DWORD threadid;
00367     m_hThread = CreateThread(
00368         NULL,
00369         0,
00370         lpStartAddress,
00371         &m_pair,
00372         0,
00373         &threadid
00374     );
00375     assert(m_hThread);
00376 }
00377 
00378 
00379 SocketFromFDMaker::~SocketFromFDMaker()
00380 {
00381     int result;
00382     // fprintf(stderr, "shutting down #%d\n", m_socket);
00383     if (m_socket != INVALID_SOCKET)
00384     {
00385         result = shutdown(m_socket, SD_BOTH);
00386         
00387         // this assertion will fail if someone has already closed the
00388         // socket; eg. if you give the socket to a WvFDStream and then let
00389         // him close it.  But you shouldn't do that, because nobody is
00390         // supposed to close stdin/stdout/stderr!
00391         if (result != 0)
00392         {
00393             int e = GetLastError();
00394             if (e == WSASYSNOTREADY || e == WSANOTINITIALISED)
00395             {
00396                 fprintf(stderr, "Abnormal termination.  Skipping cleanup.\n");
00397                 _exit(42);
00398             }
00399             else
00400             {
00401                 fprintf(stderr,
00402                         "ERROR! Socket #%d was already shut down! (%d)\n",
00403                         m_socket, e);
00404                 assert(result == 0);
00405             }
00406         }
00407             
00408         if (m_wait) // wait for socket->fd copier
00409         {
00410             // wait for thread to terminate.  Since it's reading from a
00411             // socket (m_wait==true), this will be safe, because we know
00412             // it'll die politely when it should.
00413             WaitForSingleObject(m_hThread, INFINITE);
00414         }
00415         else
00416         {
00417             // FIXME: fd->socket copier will never die politely.  It gets
00418             // stuck in _read(), which enters a critical section and
00419             // then blocks until input is available.  Unfortunately, it's
00420             // impossible to make input *not* available.
00421             // 
00422             // TerminateThread() is generally evil, and doesn't help here
00423             // anyway: it just leaves that critical section locked forever, so
00424             // no operation on that fd will *ever* finish.
00425             // 
00426             // Answer: just do nothing.  Someone will clean up the broken
00427             // thread eventually, I guess.  (ExitProcess() claims to do
00428             // this, but I hope it doesn't get stuck in a critical section...)
00429         }
00430         
00431         close(m_socket);
00432     }
00433     CloseHandle(m_hThread);
00434 }