vformat.c: fixed incomplete base64_init()
[barry.git] / tools / brawchannel_unix.cc
blob18d62c21101157d21bf00cd136707c6fd509bc1d
1 ///
2 /// \file brawchannel_unix.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 <stdio.h>
29 #include <errno.h>
30 #include <iostream>
31 #include <netdb.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>
38 #include <fcntl.h>
39 #include <string>
41 #define SD_SEND SHUT_WR
42 #define INVALID_SOCKET -1
43 #define INVALID_HANDLE -1
45 using namespace std;
46 using namespace Barry;
48 struct TcpStreamImpl
50 in_addr mHostAddress;
51 std::string mListenAddress;
52 long mPort;
53 int mListenSocket;
54 int mSocket;
58 ssize_t StdOutStream::write(const unsigned char* ptr, size_t size)
60 size_t written = ::write(STDOUT_FILENO, ptr, size);
61 if( written == 0 &&
62 ( ferror(stdout) != 0 || feof(stdout) != 0 ) ) {
63 return -1;
65 return static_cast<ssize_t>(written);
68 ssize_t StdInStream::read(unsigned char* ptr, size_t size, int timeout)
70 fd_set rfds;
71 struct timeval tv;
72 FD_ZERO(&rfds);
73 FD_SET(STDIN_FILENO, &rfds);
74 tv.tv_sec = READ_TIMEOUT_SECONDS;
75 tv.tv_usec = 0;
76 int ret = select(STDIN_FILENO + 1, &rfds, NULL, NULL, &tv);
77 if( ret < 0 ) {
78 cerr << _("Select failed with errno: ") << errno << endl;
79 return -1;
80 } else if ( ret && FD_ISSET(STDIN_FILENO, &rfds) ) {
81 return ::read(STDIN_FILENO, ptr, size);
82 } else {
83 return 0;
87 TcpStream::TcpStream(const std::string& addr, long port)
89 mImpl.reset(new TcpStreamImpl);
90 mImpl->mListenAddress = addr;
91 mImpl->mPort = port;
92 mImpl->mSocket = INVALID_SOCKET;
93 mImpl->mListenSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
94 if( mImpl->mListenSocket == INVALID_SOCKET ) {
95 cerr << _("Failed to create listening socket: ") <<
96 errno << endl;
98 if( mImpl->mListenAddress.length() == 0 ) {
99 mImpl->mHostAddress.s_addr = INADDR_ANY;
100 mImpl->mListenAddress = "*";
101 } else {
102 mImpl->mHostAddress.s_addr = inet_addr(mImpl->mListenAddress.c_str());
106 TcpStream::~TcpStream()
108 if( mImpl->mSocket != INVALID_SOCKET ) {
109 shutdown(mImpl->mSocket, SD_SEND);
110 close(mImpl->mSocket);
111 mImpl->mSocket = INVALID_SOCKET;
113 if( mImpl->mListenSocket != INVALID_SOCKET ) {
114 shutdown(mImpl->mListenSocket, SD_SEND);
115 close(mImpl->mListenSocket);
116 mImpl->mListenSocket = INVALID_SOCKET;
120 bool TcpStream::accept()
122 if( mImpl->mListenSocket == INVALID_SOCKET ||
123 mImpl->mHostAddress.s_addr == INADDR_NONE ) {
124 return false;
126 struct sockaddr_in serverAddr;
127 memset(&serverAddr, 0, sizeof(serverAddr));
128 serverAddr.sin_family = AF_INET;
129 serverAddr.sin_addr = mImpl->mHostAddress;
130 serverAddr.sin_port = htons(static_cast<u_short>(mImpl->mPort));
131 if( ::bind(mImpl->mListenSocket, (sockaddr*) & serverAddr, sizeof(serverAddr)) < 0 ) {
132 cerr << _("Failed to bind to listening address") << endl;
133 return false;
136 // Set the socket options
137 int one = 1;
138 if( setsockopt(mImpl->mListenSocket, SOL_SOCKET, SO_REUSEADDR,
139 reinterpret_cast<const char *> (&one), sizeof(one)) < 0 ) {
140 cerr << _("Failed to enable reuse of address") << endl;
141 return false;
144 if( ::listen(mImpl->mListenSocket, 5) == INVALID_SOCKET ) {
145 cerr << _("Failed to listen to listening address") << endl;
146 return false;
149 struct sockaddr_in clientAddr;
150 socklen_t len = sizeof(clientAddr);
151 cout << string_vprintf(_("Listening for connection on %s:%ld"),
152 mImpl->mListenAddress.c_str(),
153 mImpl->mPort) << endl;
155 mImpl->mSocket = ::accept(mImpl->mListenSocket, (struct sockaddr*) &clientAddr, &len);
156 shutdown(mImpl->mListenSocket, SD_SEND);
157 close(mImpl->mListenSocket);
158 mImpl->mListenSocket = INVALID_SOCKET;
159 if( mImpl->mSocket == INVALID_SOCKET ) {
160 cerr << _("Failed to accept on listening socket") << endl;
161 return false;
164 if( setsockopt(mImpl->mSocket, IPPROTO_TCP, TCP_NODELAY,
165 reinterpret_cast<const char *> (&one), sizeof(one)) < 0 ) {
166 cerr << _("Failed to set no delay") << endl;
167 return false;
170 if( fcntl(mImpl->mSocket, F_SETFL,
171 fcntl(mImpl->mSocket, F_GETFL, 0) | O_NONBLOCK) < 0 ) {
172 cerr << _("Failed to set non-blocking on socket") << endl;
173 return false;
176 return true;
179 ssize_t TcpInStream::read(unsigned char* ptr, size_t size, int timeout)
181 fd_set rfds;
182 struct timeval tv;
183 FD_ZERO(&rfds);
184 FD_SET(mStream.mImpl->mSocket, &rfds);
185 tv.tv_sec = READ_TIMEOUT_SECONDS;
186 tv.tv_usec = 0;
187 int ret = select(mStream.mImpl->mSocket + 1, &rfds, NULL, NULL, &tv);
188 if( ret < 0 ) {
189 cerr << _("Select failed with errno: ") << errno << endl;
190 return -1;
191 } else if ( ret && FD_ISSET(mStream.mImpl->mSocket, &rfds) ) {
192 return ::recv(mStream.mImpl->mSocket, reinterpret_cast<char *>(ptr), size, 0);
193 } else {
194 return 0;
198 ssize_t TcpOutStream::write(const unsigned char* ptr, size_t size)
200 return ::send(mStream.mImpl->mSocket, reinterpret_cast<const char*>(ptr), size, 0);