2 /// \file brawchannel_win32.cc
3 /// Implements OS support for STDIN/STDOUT and TCP
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"
26 #include <barry/barry.h>
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
40 using namespace Barry
;
45 std::string mListenAddress
;
53 ssize_t
StdOutStream::write(const unsigned char* ptr
, size_t size
)
55 size_t written
= fwrite(ptr
, 1, size
, stderr
);
57 ( ferror(stderr
) != 0 || feof(stderr
) != 0 ) ) {
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);
71 TcpStream::TcpStream(const std::string
& addr
, long port
)
73 mImpl
.reset(new TcpStreamImpl
);
74 mImpl
->mListenAddress
= addr
;
76 mImpl
->mListenSocket
= INVALID_SOCKET
;
77 mImpl
->mSocket
= INVALID_SOCKET
;
78 mImpl
->mEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
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
= "*";
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
;
116 bool TcpStream::accept()
118 if( mImpl
->mListenSocket
== INVALID_SOCKET
||
119 mImpl
->mLastError
!= 0 ||
120 mImpl
->mHostAddress
.s_addr
== INADDR_NONE
) {
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
;
133 // Set the socket options
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
;
142 if( ioctlsocket(mImpl
->mListenSocket
, FIONBIO
, &longOne
) == INVALID_SOCKET
) {
143 cerr
<< _("Failed to set non-blocking listening socket") << endl
;
147 if( ::listen(mImpl
->mListenSocket
, 5) == INVALID_SOCKET
) {
148 cerr
<< _("Failed to listen to listening address") << endl
;
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
);
168 ReleaseSemaphore(sem
, 1, NULL
);
173 int ret
= WSAEventSelect(mImpl
->mListenSocket
, mImpl
->mEvent
, FD_ACCEPT
);
175 cerr
<< _("WSAEventSelect failed with error: ") << ret
<< endl
;
178 DWORD signalledObj
= WaitForSingleObject(mImpl
->mEvent
, INFINITE
);
179 if( signalledObj
!= WAIT_OBJECT_0
) {
180 cerr
<< _("Failed to wait for new connection: ") << signalledObj
<< endl
;
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
;
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
;
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
);
206 cerr
<< _("WSAEventSelect failed with error: ") << ret
<< endl
;
209 switch( WaitForSingleObject(mStream
.mImpl
->mEvent
, timeout
* 1000) ) {
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();
229 cerr
<< _("WaitForSingleObject failed with error: ") << GetLastError() << endl
;
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);