tools: Updating TcpStream class in brawchannel to use std::string instead of char*.
[barry.git] / tools / brawchannel_win32.cc
blobd43510b7413f22a1e1bab4e0e03bfc2a86538c92
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>
31 #include <string>
33 #define INVALID_HANDLE ((HANDLE)NULL)
34 // This is a name of a public semaphore to signal when the listen socket is opened
35 #define LISTEN_SEMAPHORE_NAME _T("Barry_brawchannel_%s_%d_startup_rendezvous")
36 #define LISTEN_SEMAPHORE_MAX_LEN 255
37 #define LISTEN_ADDRESS_MAX 128
39 using namespace std;
40 using namespace Barry;
42 struct TcpStreamImpl
44 in_addr mHostAddress;
45 std::string 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 std::string& 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.length() == 0 ) {
90 mImpl->mHostAddress.s_addr = INADDR_ANY;
91 mImpl->mListenAddress = "*";
92 } else {
93 mImpl->mHostAddress.s_addr = inet_addr(mImpl->mListenAddress.c_str());
97 TcpStream::~TcpStream()
99 if( mImpl->mSocket != INVALID_SOCKET ) {
100 shutdown(mImpl->mSocket, SD_SEND);
101 closesocket(mImpl->mSocket);
102 mImpl->mSocket = INVALID_SOCKET;
104 if( mImpl->mListenSocket != INVALID_SOCKET ) {
105 shutdown(mImpl->mListenSocket, SD_SEND);
106 closesocket(mImpl->mListenSocket);
107 mImpl->mListenSocket = INVALID_SOCKET;
109 if( mImpl->mEvent != INVALID_HANDLE ) {
110 CloseHandle(mImpl->mEvent);
111 mImpl->mEvent = INVALID_HANDLE;
113 WSACleanup();
116 bool TcpStream::accept()
118 if( mImpl->mListenSocket == INVALID_SOCKET ||
119 mImpl->mLastError != 0 ||
120 mImpl->mHostAddress.s_addr == INADDR_NONE ) {
121 return false;
123 struct sockaddr_in serverAddr;
124 memset(&serverAddr, 0, sizeof(serverAddr));
125 serverAddr.sin_family = AF_INET;
126 serverAddr.sin_addr = mImpl->mHostAddress;
127 serverAddr.sin_port = htons(static_cast<u_short>(mImpl->mPort));
128 if( ::bind(mImpl->mListenSocket, (sockaddr*) & serverAddr, sizeof(serverAddr)) < 0 ) {
129 cerr << _("Failed to bind to listening address") << endl;
130 return false;
133 // Set the socket options
134 int one = 1;
135 if( setsockopt(mImpl->mListenSocket, SOL_SOCKET, SO_REUSEADDR,
136 reinterpret_cast<const char *> (&one), sizeof(one)) < 0 ) {
137 cerr << _("Failed to enable reuse of address") << endl;
138 return false;
141 ULONG longOne = 1;
142 if( ioctlsocket(mImpl->mListenSocket, FIONBIO, &longOne) == INVALID_SOCKET ) {
143 cerr << _("Failed to set non-blocking listening socket") << endl;
144 return false;
147 if( ::listen(mImpl->mListenSocket, 5) == INVALID_SOCKET ) {
148 cerr << _("Failed to listen to listening address") << endl;
149 return false;
152 struct sockaddr_in clientAddr;
153 socklen_t len = sizeof(clientAddr);
154 cout << string_vprintf(_("Listening for connection on %s:%ld"),
155 mImpl->mListenAddress.c_str(),
156 mImpl->mPort) << endl;
158 /* Signal to a public semaphore that the listen socket is up */
159 TCHAR wListenAddress[LISTEN_ADDRESS_MAX];
160 if( MultiByteToWideChar(CP_ACP, 0,
161 mImpl->mListenAddress.c_str(), -1,
162 wListenAddress, LISTEN_ADDRESS_MAX) > 0 ) {
163 TCHAR semName[LISTEN_SEMAPHORE_MAX_LEN];
164 _snwprintf(semName, LISTEN_SEMAPHORE_MAX_LEN, LISTEN_SEMAPHORE_NAME, wListenAddress, mImpl->mPort);
165 semName[LISTEN_SEMAPHORE_MAX_LEN - 1] = 0;
166 HANDLE sem = CreateSemaphore(NULL, 0, 1, semName);
167 if( sem != NULL ) {
168 ReleaseSemaphore(sem, 1, NULL);
169 CloseHandle(sem);
173 int ret = WSAEventSelect(mImpl->mListenSocket, mImpl->mEvent, FD_ACCEPT);
174 if( ret != 0 ) {
175 cerr << _("WSAEventSelect failed with error: ") << ret << endl;
176 return false;
178 DWORD signalledObj = WaitForSingleObject(mImpl->mEvent, INFINITE);
179 if( signalledObj != WAIT_OBJECT_0 ) {
180 cerr << _("Failed to wait for new connection: ") << signalledObj << endl;
181 return false;
184 mImpl->mSocket = ::accept(mImpl->mListenSocket, (struct sockaddr*) &clientAddr, &len);
185 shutdown(mImpl->mListenSocket, SD_SEND);
186 closesocket(mImpl->mListenSocket);
187 mImpl->mListenSocket = INVALID_SOCKET;
188 if( mImpl->mSocket == INVALID_SOCKET ) {
189 cerr << _("Failed to accept on listening socket") << endl;
190 return false;
193 if( setsockopt(mImpl->mSocket, IPPROTO_TCP, TCP_NODELAY,
194 reinterpret_cast<const char *> (&one), sizeof(one)) < 0 ) {
195 cerr << _("Failed to set no delay") << endl;
196 return false;
199 return true;
202 ssize_t TcpInStream::read(unsigned char* ptr, size_t size, int timeout)
204 int ret = WSAEventSelect(mStream.mImpl->mSocket, mStream.mImpl->mEvent, FD_READ);
205 if( ret != 0 ) {
206 cerr << _("WSAEventSelect failed with error: ") << ret << endl;
207 return -1;
209 switch( WaitForSingleObject(mStream.mImpl->mEvent, timeout * 1000) ) {
210 case WAIT_ABANDONED:
211 case WAIT_TIMEOUT:
212 return 0;
213 case WAIT_OBJECT_0:
214 ResetEvent(mStream.mImpl->mEvent);
215 ret = ::recv(mStream.mImpl->mSocket, reinterpret_cast<char *>(ptr), size, 0);
216 if( ret == SOCKET_ERROR ) {
217 int wsaErr = WSAGetLastError();
218 switch( wsaErr ) {
219 case WSAEWOULDBLOCK:
220 return 0;
221 default:
222 return -1;
224 } else {
225 return ret;
227 case WAIT_FAILED:
228 default:
229 cerr << _("WaitForSingleObject failed with error: ") << GetLastError() << endl;
230 return -1;
235 ssize_t TcpOutStream::write(const unsigned char* ptr, size_t size)
237 return ::send(mStream.mImpl->mSocket, reinterpret_cast<const char*>(ptr), size, 0);