tools: added FIXME notes for future library maintainers
[barry.git] / tools / brawchannel_win32.cc
blob50c662b9dbe3f62265076acef6f4bb8efd01dd9f
1 ///
2 /// \file brawchannel_win32.cc
3 /// Implements OS support for STDIN/STDOUT and TCP
4 ///
6 /*
7 Copyright (C) 2010-2012, RealVNC Ltd.
9 Some parts are inspired from bjavaloader.cc
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 See the GNU General Public License in the COPYING file at the
21 root directory of this project for more details.
24 #include "brawchannel.h"
25 #include "i18n.h"
26 #include <barry/barry.h>
28 #include <iostream>
29 #include <winsock2.h>
30 #include <ws2tcpip.h>
32 #define INVALID_HANDLE ((HANDLE)NULL)
33 // This is a name of a public semaphore to signal when the listen socket is opened
34 #define LISTEN_SEMAPHORE_NAME _T("Barry_brawchannel_%s_%d_startup_rendezvous")
35 #define LISTEN_SEMAPHORE_MAX_LEN 255
36 #define LISTEN_ADDRESS_MAX 128
38 using namespace std;
39 using namespace Barry;
41 struct TcpStreamImpl
43 in_addr mHostAddress;
44 // FIXME - this should be a std::string if ever moved to the library
45 const char * mListenAddress;
46 long mPort;
47 SOCKET mListenSocket;
48 SOCKET mSocket;
49 HANDLE mEvent;
50 DWORD mLastError;
53 ssize_t StdOutStream::write(const unsigned char* ptr, size_t size)
55 size_t written = fwrite(ptr, 1, size, stderr);
56 if( written == 0 &&
57 ( ferror(stderr) != 0 || feof(stderr) != 0 ) ) {
58 return -1;
60 return static_cast<ssize_t>(written);
63 /* Windows terminal input class implementation */
64 ssize_t StdInStream::read(unsigned char* ptr, size_t size, int timeout)
66 /* Windows CE can't do non-blocking IO, so just always fail to read anything*/
67 Sleep(timeout * 1000);
68 return 0;
71 TcpStream::TcpStream(const char * addr, long port)
73 mImpl.reset(new TcpStreamImpl);
74 mImpl->mListenAddress = addr;
75 mImpl->mPort = port;
76 mImpl->mListenSocket = INVALID_SOCKET;
77 mImpl->mSocket = INVALID_SOCKET;
78 mImpl->mEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
79 WSADATA wsaData;
80 mImpl->mLastError = WSAStartup(MAKEWORD(2, 2), &wsaData);
81 if( mImpl->mLastError != 0 ) {
82 cerr << _("Failed to startup WSA: ") << mImpl->mLastError << endl;
84 mImpl->mListenSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
85 if( mImpl->mListenSocket == INVALID_SOCKET ) {
86 cerr << _("Failed to create listening socket: ") <<
87 WSAGetLastError() << endl;
89 if( mImpl->mListenAddress == NULL ) {
90 mImpl->mHostAddress.s_addr = INADDR_ANY;
91 } else {
92 mImpl->mHostAddress.s_addr = inet_addr(mImpl->mListenAddress);
96 TcpStream::~TcpStream()
98 if( mImpl->mSocket != INVALID_SOCKET ) {
99 shutdown(mImpl->mSocket, SD_SEND);
100 closesocket(mImpl->mSocket);
101 mImpl->mSocket = INVALID_SOCKET;
103 if( mImpl->mListenSocket != INVALID_SOCKET ) {
104 shutdown(mImpl->mListenSocket, SD_SEND);
105 closesocket(mImpl->mListenSocket);
106 mImpl->mListenSocket = INVALID_SOCKET;
108 if( mImpl->mEvent != INVALID_HANDLE ) {
109 CloseHandle(mImpl->mEvent);
110 mImpl->mEvent = INVALID_HANDLE;
112 WSACleanup();
115 bool TcpStream::accept()
117 if( mImpl->mListenSocket == INVALID_SOCKET ||
118 mImpl->mLastError != 0 ||
119 mImpl->mHostAddress.s_addr == INADDR_NONE ) {
120 return false;
122 struct sockaddr_in serverAddr;
123 memset(&serverAddr, 0, sizeof(serverAddr));
124 serverAddr.sin_family = AF_INET;
125 serverAddr.sin_addr = mImpl->mHostAddress;
126 serverAddr.sin_port = htons(static_cast<u_short>(mImpl->mPort));
127 if( ::bind(mImpl->mListenSocket, (sockaddr*) & serverAddr, sizeof(serverAddr)) < 0 ) {
128 cerr << _("Failed to bind to listening address") << endl;
129 return false;
132 // Set the socket options
133 int one = 1;
134 if( setsockopt(mImpl->mListenSocket, SOL_SOCKET, SO_REUSEADDR,
135 reinterpret_cast<const char *> (&one), sizeof(one)) < 0 ) {
136 cerr << _("Failed to enable reuse of address") << endl;
137 return false;
140 ULONG longOne = 1;
141 if( ioctlsocket(mImpl->mListenSocket, FIONBIO, &longOne) == INVALID_SOCKET ) {
142 cerr << _("Failed to set non-blocking listening socket") << endl;
143 return false;
146 if( ::listen(mImpl->mListenSocket, 5) == INVALID_SOCKET ) {
147 cerr << _("Failed to listen to listening address") << endl;
148 return false;
151 struct sockaddr_in clientAddr;
152 socklen_t len = sizeof(clientAddr);
153 cout << string_vprintf(_("Listening for connection on %s:%d"),
154 ( mImpl->mListenAddress == NULL ? "*" : mImpl->mListenAddress ),
155 mImpl->mPort) << endl;
157 /* Signal to a public semaphore that the listen socket is up */
158 TCHAR wListenAddress[LISTEN_ADDRESS_MAX];
159 if( MultiByteToWideChar(CP_ACP, 0,
160 (mImpl->mListenAddress == NULL ? "*" : mImpl->mListenAddress), -1,
161 wListenAddress, LISTEN_ADDRESS_MAX) > 0 ) {
162 TCHAR semName[LISTEN_SEMAPHORE_MAX_LEN];
163 _snwprintf(semName, LISTEN_SEMAPHORE_MAX_LEN, LISTEN_SEMAPHORE_NAME, wListenAddress, mImpl->mPort);
164 semName[LISTEN_SEMAPHORE_MAX_LEN - 1] = 0;
165 HANDLE sem = CreateSemaphore(NULL, 0, 1, semName);
166 if( sem != NULL ) {
167 ReleaseSemaphore(sem, 1, NULL);
168 CloseHandle(sem);
172 int ret = WSAEventSelect(mImpl->mListenSocket, mImpl->mEvent, FD_ACCEPT);
173 if( ret != 0 ) {
174 cerr << _("WSAEventSelect failed with error: ") << ret << endl;
175 return false;
177 DWORD signalledObj = WaitForSingleObject(mImpl->mEvent, INFINITE);
178 if( signalledObj != WAIT_OBJECT_0 ) {
179 cerr << _("Failed to wait for new connection: ") << signalledObj << endl;
180 return false;
183 mImpl->mSocket = ::accept(mImpl->mListenSocket, (struct sockaddr*) &clientAddr, &len);
184 shutdown(mImpl->mListenSocket, SD_SEND);
185 closesocket(mImpl->mListenSocket);
186 mImpl->mListenSocket = INVALID_SOCKET;
187 if( mImpl->mSocket == INVALID_SOCKET ) {
188 cerr << _("Failed to accept on listening socket") << endl;
189 return false;
192 if( setsockopt(mImpl->mSocket, IPPROTO_TCP, TCP_NODELAY,
193 reinterpret_cast<const char *> (&one), sizeof(one)) < 0 ) {
194 cerr << _("Failed to set no delay") << endl;
195 return false;
198 return true;
201 ssize_t TcpInStream::read(unsigned char* ptr, size_t size, int timeout)
203 int ret = WSAEventSelect(mStream.mImpl->mSocket, mStream.mImpl->mEvent, FD_READ);
204 if( ret != 0 ) {
205 cerr << _("WSAEventSelect failed with error: ") << ret << endl;
206 return -1;
208 switch( WaitForSingleObject(mStream.mImpl->mEvent, timeout * 1000) ) {
209 case WAIT_ABANDONED:
210 case WAIT_TIMEOUT:
211 return 0;
212 case WAIT_OBJECT_0:
213 ResetEvent(mStream.mImpl->mEvent);
214 ret = ::recv(mStream.mImpl->mSocket, reinterpret_cast<char *>(ptr), size, 0);
215 if( ret == SOCKET_ERROR ) {
216 int wsaErr = WSAGetLastError();
217 switch( wsaErr ) {
218 case WSAEWOULDBLOCK:
219 return 0;
220 default:
221 return -1;
223 } else {
224 return ret;
226 case WAIT_FAILED:
227 default:
228 cerr << _("WaitForSingleObject failed with error: ") << GetLastError() << endl;
229 return -1;
234 ssize_t TcpOutStream::write(const unsigned char* ptr, size_t size)
236 return ::send(mStream.mImpl->mSocket, reinterpret_cast<const char*>(ptr), size, 0);