2 /// \file brawchannel_unix.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>
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <sys/select.h>
35 #include <netinet/in.h>
36 #include <netinet/tcp.h>
37 #include <arpa/inet.h>
42 #define SD_SEND SHUT_WR
43 #define INVALID_SOCKET -1
44 #define INVALID_HANDLE -1
47 using namespace Barry
;
52 std::string mListenAddress
;
59 ssize_t
StdOutStream::write(const unsigned char* ptr
, size_t size
)
61 size_t written
= ::write(STDOUT_FILENO
, ptr
, size
);
63 ( ferror(stdout
) != 0 || feof(stdout
) != 0 ) ) {
66 return static_cast<ssize_t
>(written
);
69 ssize_t
StdInStream::read(unsigned char* ptr
, size_t size
, int timeout
)
74 FD_SET(STDIN_FILENO
, &rfds
);
75 tv
.tv_sec
= READ_TIMEOUT_SECONDS
;
77 int ret
= select(STDIN_FILENO
+ 1, &rfds
, NULL
, NULL
, &tv
);
79 cerr
<< _("Select failed with errno: ") << errno
<< endl
;
81 } else if ( ret
&& FD_ISSET(STDIN_FILENO
, &rfds
) ) {
82 return ::read(STDIN_FILENO
, ptr
, size
);
88 TcpStream::TcpStream(const std::string
& addr
, long port
)
90 mImpl
.reset(new TcpStreamImpl
);
91 mImpl
->mListenAddress
= addr
;
93 mImpl
->mSocket
= INVALID_SOCKET
;
94 mImpl
->mListenSocket
= socket(PF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
95 if( mImpl
->mListenSocket
== INVALID_SOCKET
) {
96 cerr
<< _("Failed to create listening socket: ") <<
99 if( mImpl
->mListenAddress
.length() == 0 ) {
100 mImpl
->mHostAddress
.s_addr
= INADDR_ANY
;
101 mImpl
->mListenAddress
= "*";
103 mImpl
->mHostAddress
.s_addr
= inet_addr(mImpl
->mListenAddress
.c_str());
107 TcpStream::~TcpStream()
109 if( mImpl
->mSocket
!= INVALID_SOCKET
) {
110 shutdown(mImpl
->mSocket
, SD_SEND
);
111 close(mImpl
->mSocket
);
112 mImpl
->mSocket
= INVALID_SOCKET
;
114 if( mImpl
->mListenSocket
!= INVALID_SOCKET
) {
115 shutdown(mImpl
->mListenSocket
, SD_SEND
);
116 close(mImpl
->mListenSocket
);
117 mImpl
->mListenSocket
= INVALID_SOCKET
;
121 bool TcpStream::accept()
123 if( mImpl
->mListenSocket
== INVALID_SOCKET
||
124 mImpl
->mHostAddress
.s_addr
== INADDR_NONE
) {
127 struct sockaddr_in serverAddr
;
128 memset(&serverAddr
, 0, sizeof(serverAddr
));
129 serverAddr
.sin_family
= AF_INET
;
130 serverAddr
.sin_addr
= mImpl
->mHostAddress
;
131 serverAddr
.sin_port
= htons(static_cast<u_short
>(mImpl
->mPort
));
132 if( ::bind(mImpl
->mListenSocket
, (sockaddr
*) & serverAddr
, sizeof(serverAddr
)) < 0 ) {
133 cerr
<< _("Failed to bind to listening address") << endl
;
137 // Set the socket options
139 if( setsockopt(mImpl
->mListenSocket
, SOL_SOCKET
, SO_REUSEADDR
,
140 reinterpret_cast<const char *> (&one
), sizeof(one
)) < 0 ) {
141 cerr
<< _("Failed to enable reuse of address") << endl
;
145 if( ::listen(mImpl
->mListenSocket
, 5) == INVALID_SOCKET
) {
146 cerr
<< _("Failed to listen to listening address") << endl
;
150 struct sockaddr_in clientAddr
;
151 socklen_t len
= sizeof(clientAddr
);
152 cout
<< string_vprintf(_("Listening for connection on %s:%ld"),
153 mImpl
->mListenAddress
.c_str(),
154 mImpl
->mPort
) << endl
;
156 mImpl
->mSocket
= ::accept(mImpl
->mListenSocket
, (struct sockaddr
*) &clientAddr
, &len
);
157 shutdown(mImpl
->mListenSocket
, SD_SEND
);
158 close(mImpl
->mListenSocket
);
159 mImpl
->mListenSocket
= INVALID_SOCKET
;
160 if( mImpl
->mSocket
== INVALID_SOCKET
) {
161 cerr
<< _("Failed to accept on listening socket") << endl
;
165 if( setsockopt(mImpl
->mSocket
, IPPROTO_TCP
, TCP_NODELAY
,
166 reinterpret_cast<const char *> (&one
), sizeof(one
)) < 0 ) {
167 cerr
<< _("Failed to set no delay") << endl
;
171 if( fcntl(mImpl
->mSocket
, F_SETFL
,
172 fcntl(mImpl
->mSocket
, F_GETFL
, 0) | O_NONBLOCK
) < 0 ) {
173 cerr
<< _("Failed to set non-blocking on socket") << endl
;
180 ssize_t
TcpInStream::read(unsigned char* ptr
, size_t size
, int timeout
)
185 FD_SET(mStream
.mImpl
->mSocket
, &rfds
);
186 tv
.tv_sec
= READ_TIMEOUT_SECONDS
;
188 int ret
= select(mStream
.mImpl
->mSocket
+ 1, &rfds
, NULL
, NULL
, &tv
);
190 cerr
<< _("Select failed with errno: ") << errno
<< endl
;
192 } else if ( ret
&& FD_ISSET(mStream
.mImpl
->mSocket
, &rfds
) ) {
193 return ::recv(mStream
.mImpl
->mSocket
, reinterpret_cast<char *>(ptr
), size
, 0);
199 ssize_t
TcpOutStream::write(const unsigned char* ptr
, size_t size
)
201 return ::send(mStream
.mImpl
->mSocket
, reinterpret_cast<const char*>(ptr
), size
, 0);