1 /*
2 * This file is part of Wireless Display Software for Linux OS
3 *
4 * Copyright (C) 2014 Intel Corporation.
5 *
6 * Contact: Jussi Laako <jussi.laako@linux.intel.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 * 02110-1301 USA
22 */
23
24
25 #include <cstddef>
26 #include <cstdio>
27 #include <cstdlib>
28 #include <cstring>
29 #include <cerrno>
30 #include <algorithm>
31 #include <memory>
32
33 #include <unistd.h>
34 #include <netdb.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <sys/ioctl.h>
38 #include <netinet/in.h>
39 #include <netinet/tcp.h>
40 #include <arpa/inet.h>
41
42 #include "mirac-network.hpp"
43
44
45 #define MIRAC_MAX_NAMELEN 255
46
47
MiracNetwork()48 MiracNetwork::MiracNetwork ()
49 {
50 Init();
51 }
52
53
MiracNetwork(int conn_handle)54 MiracNetwork::MiracNetwork (int conn_handle)
55 {
56 Init();
57 handle = conn_handle;
58 }
59
60
61
~MiracNetwork()62 MiracNetwork::~MiracNetwork ()
63 {
64 Close();
65 }
66
67
Init()68 void MiracNetwork::Init ()
69 {
70 long ps;
71
72 handle = -1;
73
74 ps = sysconf(_SC_PAGESIZE);
75 page_size = (ps <= 0) ? 4096 : static_cast<size_t> (ps);
76
77 conn_ares = NULL;
78 }
79
80
Close()81 void MiracNetwork::Close ()
82 {
83 if (handle >= 0)
84 close(handle);
85 handle = -1;
86
87 if (conn_ares)
88 {
89 freeaddrinfo(reinterpret_cast<struct addrinfo *> (conn_ares));
90 conn_ares = NULL;
91 }
92 }
93
94
Bind(const char * address,const char * service)95 void MiracNetwork::Bind (const char *address, const char *service)
96 {
97 int ec;
98 int reuse = 1;
99 struct addrinfo *addr_res = NULL;
100 struct addrinfo *bind_addr;
101 struct addrinfo addr_hint;
102
103 Close();
104
105 memset(&addr_hint, 0x00, sizeof(addr_hint));
106 addr_hint.ai_flags = AI_PASSIVE;
107 ec = getaddrinfo(address, service, &addr_hint, &addr_res);
108 if (ec)
109 throw MiracException(gai_strerror(ec), __FUNCTION__);
110 bind_addr = addr_res;
111 while (bind_addr)
112 {
113 /* note, the SOCK_NONBLOCK is specific to Linux 2.6.27+,
114 * on other platforms use either fcntl() or ioctl(h, FIONBIO, 1) */
115 handle = socket(addr_res->ai_family,
116 addr_res->ai_socktype | SOCK_NONBLOCK, addr_res->ai_protocol);
117 if (handle < 0)
118 throw MiracException(errno, "socket()", __FUNCTION__);
119 if (setsockopt(handle, SOL_SOCKET, SO_REUSEADDR,
120 &reuse, sizeof(reuse)))
121 throw MiracException(errno, "setsockopt()", __FUNCTION__);
122 if (bind(handle, bind_addr->ai_addr, bind_addr->ai_addrlen) == 0)
123 break;
124 else if (!bind_addr->ai_next)
125 throw MiracException(errno, "bind()", __FUNCTION__);
126 close(handle);
127 bind_addr = bind_addr->ai_next;
128 }
129 freeaddrinfo(addr_res);
130
131 if (listen(handle, 1))
132 throw MiracException(errno, "listen()", __FUNCTION__);
133 }
134
135
Accept()136 MiracNetwork * MiracNetwork::Accept ()
137 {
138 int ch;
139 int nonblock = 1;
140
141 ch = accept(handle, NULL, NULL);
142 if (ch < 0)
143 throw MiracException(errno, "accept()", __FUNCTION__);
144 if (ioctl(ch, FIONBIO, &nonblock))
145 throw MiracException(errno, "ioctl(FIONBIO)", __FUNCTION__);
146 return new MiracNetwork(ch);
147 }
148
149
Connect(const char * address,const char * service)150 bool MiracNetwork::Connect (const char *address, const char *service)
151 {
152 int ec = 0;
153
154 if (address && service)
155 {
156 Close();
157
158 struct addrinfo addr_hint;
159 memset(&addr_hint, 0x00, sizeof(addr_hint));
160 addr_hint.ai_socktype = SOCK_STREAM;
161 ec = getaddrinfo(address, service, &addr_hint,
162 reinterpret_cast<struct addrinfo **> (&conn_ares));
163 if (ec)
164 throw MiracException(gai_strerror(ec), __FUNCTION__);
165 conn_aptr = conn_ares;
166 }
167 else
168 {
169 socklen_t optlen = sizeof(ec);
170 if (getsockopt(handle, SOL_SOCKET, SO_ERROR, &ec, &optlen))
171 throw MiracException(errno, "getsockopt()", __FUNCTION__);
172 if (!ec)
173 return true;
174 conn_aptr = reinterpret_cast<struct addrinfo *> (conn_aptr)->ai_next;
175 }
176
177 struct addrinfo *addr = reinterpret_cast<struct addrinfo *> (conn_aptr);
178 if (!addr)
179 throw MiracException("peer unavailable", __FUNCTION__);
180 /* note, the SOCK_NONBLOCK is specific to Linux 2.6.27+,
181 * on other platforms use either fcntl() or ioctl(h, FIONBIO, 1) */
182 handle = socket(addr->ai_family,
183 addr->ai_socktype | SOCK_NONBLOCK, addr->ai_protocol);
184 if (handle < 0)
185 throw MiracException(errno, "socket()", __FUNCTION__);
186 if (connect(handle, addr->ai_addr, addr->ai_addrlen))
187 {
188 if (errno == EINPROGRESS)
189 return false;
190 throw MiracException(errno, "connect()", __FUNCTION__);
191 }
192 return true;
193 }
194
195
GetPeerAddress()196 std::string MiracNetwork::GetPeerAddress ()
197 {
198 int ec;
199 socklen_t addrsize = std::max(sizeof(sockaddr_in), sizeof(sockaddr_in6));
200 std::unique_ptr<uint8_t []> addrbuf(new uint8_t[addrsize]);
201 std::unique_ptr<char []> namebuf(new char [MIRAC_MAX_NAMELEN + 1]);
202 std::unique_ptr<char []> servbuf(new char [MIRAC_MAX_NAMELEN + 1]);
203
204 if (getpeername(handle,
205 reinterpret_cast<struct sockaddr *> (addrbuf.get()), &addrsize))
206 throw MiracException(errno, "getpeername()", __FUNCTION__);
207 ec = getnameinfo(
208 reinterpret_cast<struct sockaddr *> (addrbuf.get()), addrsize,
209 namebuf.get(), MIRAC_MAX_NAMELEN,
210 servbuf.get(), MIRAC_MAX_NAMELEN,
211 NI_NOFQDN|NI_NUMERICHOST|NI_NUMERICSERV);
212 if (ec)
213 throw MiracException(gai_strerror(ec), __FUNCTION__);
214 return std::string(namebuf.get());
215 }
216
217
GetHostPort()218 unsigned short MiracNetwork::GetHostPort ()
219 {
220 socklen_t addrsize = std::max(sizeof(sockaddr_in), sizeof(sockaddr_in6));
221 std::unique_ptr<uint8_t []> addrbuf(new uint8_t[addrsize]);
222
223 if (getsockname(handle,
224 reinterpret_cast<struct sockaddr *> (addrbuf.get()), &addrsize))
225 throw MiracException(errno, "getsockname()", __FUNCTION__);
226
227 struct sockaddr *saddr =
228 reinterpret_cast<struct sockaddr *> (addrbuf.get());
229 if (saddr->sa_family == AF_INET)
230 {
231 struct sockaddr_in *ip4addr =
232 reinterpret_cast<struct sockaddr_in *> (saddr);
233 return ntohs(ip4addr->sin_port);
234 }
235 else if (saddr->sa_family == AF_INET6)
236 {
237 struct sockaddr_in6 *ip6addr =
238 reinterpret_cast<struct sockaddr_in6 *> (saddr);
239 return ntohs(ip6addr->sin6_port);
240 }
241 return 0;
242 }
243
244
Receive(std::string & message)245 bool MiracNetwork::Receive (std::string &message)
246 {
247 int ec;
248 char nb[page_size];
249
250 do {
251 ec = recv(handle, nb, page_size, 0);
252 if (ec > 0)
253 message.append(nb, ec);
254 else if (ec < 0)
255 {
256 if (errno == EAGAIN || errno == EWOULDBLOCK)
257 break;
258 if (errno == ECONNRESET)
259 throw MiracConnectionLostException( __FUNCTION__);
260 throw MiracException(errno, "recv()", __FUNCTION__);
261 }
262 else // ec == 0
263 throw MiracConnectionLostException( __FUNCTION__);
264 } while (ec > 0);
265
266 return true;
267 }
268
269
Receive(std::string & message,size_t length)270 bool MiracNetwork::Receive (std::string &message, size_t length)
271 {
272 int ec;
273 char nb[page_size];
274
275 do {
276 ec = recv(handle, nb, page_size, 0);
277 if (ec > 0)
278 recv_buf.append(nb, ec);
279 else if (ec < 0)
280 {
281 if (errno == EAGAIN || errno == EWOULDBLOCK)
282 break;
283 if (errno == ECONNRESET)
284 throw MiracConnectionLostException( __FUNCTION__);
285 throw MiracException(errno, "recv()", __FUNCTION__);
286 }
287 else // ec == 0
288 throw MiracConnectionLostException( __FUNCTION__);
289 } while (ec > 0);
290
291 if (recv_buf.size() < length)
292 return false;
293
294 message = recv_buf.substr(0, length);
295 recv_buf.erase(0, length);
296 return true;
297 }
298
299
Send(const std::string & message)300 bool MiracNetwork::Send (const std::string &message)
301 {
302 int ec;
303
304 if (!message.empty())
305 send_buf.append(message);
306 do {
307 ec = send(handle, send_buf.c_str(), send_buf.size(), MSG_NOSIGNAL);
308 if (ec > 0)
309 send_buf.erase(0, ec);
310 else
311 {
312 if (errno == EAGAIN || errno == EWOULDBLOCK)
313 return false;
314 if (errno == EPIPE || errno == ENOTCONN)
315 throw MiracConnectionLostException(__FUNCTION__);
316 throw MiracException(errno, "send()", __FUNCTION__);
317 }
318 } while (send_buf.size() > 0);
319
320 return true;
321 }
322
323