blob: 3b7e16e865d30cae96bf29c4318496bcabd1aece [file] [log] [blame]
Brian Silvermanf7bd1c22015-12-24 16:07:11 -08001/*
2 TCPAcceptor.cpp
3
4 TCPAcceptor class definition. TCPAcceptor provides methods to passively
5 establish TCP/IP connections with clients.
6
7 ------------------------------------------
8
9 Copyright © 2013 [Vic Hargrave - http://vichargrave.com]
10
11 Licensed under the Apache License, Version 2.0 (the "License");
12 you may not use this file except in compliance with the License.
13 You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22*/
23
24#include "TCPAcceptor.h"
25
26#include <cstdio>
27#include <cstring>
28#ifdef _WIN32
29#include <WinSock2.h>
30#pragma comment(lib, "Ws2_32.lib")
31#else
32#include <arpa/inet.h>
33#include <netinet/in.h>
34#include <unistd.h>
35#include <fcntl.h>
36#endif
37
38#include "llvm/SmallString.h"
39#include "../Log.h"
40#include "SocketError.h"
41
42using namespace tcpsockets;
43
44TCPAcceptor::TCPAcceptor(int port, const char* address)
45 : m_lsd(0),
46 m_port(port),
47 m_address(address),
48 m_listening(false) {
49 m_shutdown = false;
50#ifdef _WIN32
51 WSAData wsaData;
52 WORD wVersionRequested = MAKEWORD(2, 2);
53 WSAStartup(wVersionRequested, &wsaData);
54#endif
55}
56
57TCPAcceptor::~TCPAcceptor() {
58 if (m_lsd > 0) {
59 shutdown();
60#ifdef _WIN32
61 closesocket(m_lsd);
62#else
63 close(m_lsd);
64#endif
65 }
66#ifdef _WIN32
67 WSACleanup();
68#endif
69}
70
71int TCPAcceptor::start() {
72 if (m_listening) return 0;
73
74 m_lsd = socket(PF_INET, SOCK_STREAM, 0);
75 struct sockaddr_in address;
76
77 std::memset(&address, 0, sizeof(address));
78 address.sin_family = PF_INET;
79 if (m_address.size() > 0) {
80#ifdef _WIN32
81 llvm::SmallString<128> addr_copy(m_address);
82 addr_copy.push_back('\0');
83 int size = sizeof(address);
84 WSAStringToAddress(addr_copy.data(), PF_INET, nullptr, (struct sockaddr*)&address, &size);
85#else
86 inet_pton(PF_INET, m_address.c_str(), &(address.sin_addr));
87#endif
88 } else {
89 address.sin_addr.s_addr = INADDR_ANY;
90 }
91 address.sin_port = htons(m_port);
92
93 int optval = 1;
94 setsockopt(m_lsd, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, sizeof optval);
95
96 int result = bind(m_lsd, (struct sockaddr*)&address, sizeof(address));
97 if (result != 0) {
98 ERROR("bind() failed: " << SocketStrerror());
99 return result;
100 }
101
102 result = listen(m_lsd, 5);
103 if (result != 0) {
104 ERROR("listen() failed: " << SocketStrerror());
105 return result;
106 }
107 m_listening = true;
108 return result;
109}
110
111void TCPAcceptor::shutdown() {
112 m_shutdown = true;
113#ifdef _WIN32
114 ::shutdown(m_lsd, SD_BOTH);
115
116 // this is ugly, but the easiest way to do this
117 // force wakeup of accept() with a non-blocking connect to ourselves
118 struct sockaddr_in address;
119
120 std::memset(&address, 0, sizeof(address));
121 address.sin_family = PF_INET;
122 llvm::SmallString<128> addr_copy;
123 if (m_address.size() > 0)
124 addr_copy = m_address;
125 else
126 addr_copy = "127.0.0.1";
127 addr_copy.push_back('\0');
128 int size = sizeof(address);
129 if (WSAStringToAddress(addr_copy.data(), PF_INET, nullptr,
130 (struct sockaddr*)&address, &size) != 0)
131 return;
132 address.sin_port = htons(m_port);
133
134 fd_set sdset;
135 struct timeval tv;
136 int result = -1, valopt, sd = socket(AF_INET, SOCK_STREAM, 0);
137
138 // Set socket to non-blocking
139 u_long mode = 1;
140 ioctlsocket(sd, FIONBIO, &mode);
141
142 // Try to connect
143 ::connect(sd, (struct sockaddr*)&address, sizeof(address));
144
145 // Close
146 ::closesocket(sd);
147
148#else
149 ::shutdown(m_lsd, SHUT_RDWR);
150 int nullfd = ::open("/dev/null", O_RDONLY);
151 if (nullfd >= 0) {
152 ::dup2(nullfd, m_lsd);
153 ::close(nullfd);
154 }
155#endif
156}
157
158std::unique_ptr<NetworkStream> TCPAcceptor::accept() {
159 if (!m_listening || m_shutdown) return nullptr;
160
161 struct sockaddr_in address;
162#ifdef _WIN32
163 int len = sizeof(address);
164#else
165 socklen_t len = sizeof(address);
166#endif
167 std::memset(&address, 0, sizeof(address));
168 int sd = ::accept(m_lsd, (struct sockaddr*)&address, &len);
169 if (sd < 0) {
170 if (!m_shutdown) ERROR("accept() failed: " << SocketStrerror());
171 return nullptr;
172 }
173 if (m_shutdown) {
174#ifdef _WIN32
175 closesocket(sd);
176#else
177 close(sd);
178#endif
179 return nullptr;
180 }
181 return std::unique_ptr<NetworkStream>(new TCPStream(sd, &address));
182}