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