Imported Upstream version 3.2
[platform/upstream/libwebsockets.git] / lib / plat / unix / unix-sockets.c
1 /*
2  * libwebsockets - small server side websockets and web server implementation
3  *
4  * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public
8  *  License as published by the Free Software Foundation:
9  *  version 2.1 of the License.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  *  MA  02110-1301  USA
20  */
21
22 #define _GNU_SOURCE
23 #include "core/private.h"
24
25 #include <pwd.h>
26 #include <grp.h>
27
28
29
30 int
31 lws_send_pipe_choked(struct lws *wsi)
32 {
33         struct lws_pollfd fds;
34         struct lws *wsi_eff;
35
36 #if !defined(LWS_WITHOUT_EXTENSIONS)
37         if (wsi->ws && wsi->ws->tx_draining_ext)
38                 return 1;
39 #endif
40
41 #if defined(LWS_WITH_HTTP2)
42         wsi_eff = lws_get_network_wsi(wsi);
43 #else
44         wsi_eff = wsi;
45 #endif
46
47         /* the fact we checked implies we avoided back-to-back writes */
48         wsi_eff->could_have_pending = 0;
49
50         /* treat the fact we got a truncated send pending as if we're choked */
51         if (lws_has_buffered_out(wsi_eff)
52 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
53             ||wsi->http.comp_ctx.buflist_comp ||
54             wsi->http.comp_ctx.may_have_more
55 #endif
56             )
57                 return 1;
58
59         fds.fd = wsi_eff->desc.sockfd;
60         fds.events = POLLOUT;
61         fds.revents = 0;
62
63         if (poll(&fds, 1, 0) != 1)
64                 return 1;
65
66         if ((fds.revents & POLLOUT) == 0)
67                 return 1;
68
69         /* okay to send another packet without blocking */
70
71         return 0;
72 }
73
74 int
75 lws_plat_set_nonblocking(int fd)
76 {
77         return fcntl(fd, F_SETFL, O_NONBLOCK) < 0;
78 }
79
80 int
81 lws_plat_set_socket_options(struct lws_vhost *vhost, int fd, int unix_skt)
82 {
83         int optval = 1;
84         socklen_t optlen = sizeof(optval);
85
86 #if defined(__APPLE__) || \
87     defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
88     defined(__NetBSD__) || \
89     defined(__OpenBSD__) || \
90     defined(__HAIKU__)
91         struct protoent *tcp_proto;
92 #endif
93
94         (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
95
96         if (!unix_skt && vhost->ka_time) {
97                 /* enable keepalive on this socket */
98                 optval = 1;
99                 if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
100                                (const void *)&optval, optlen) < 0)
101                         return 1;
102
103 #if defined(__APPLE__) || \
104     defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
105     defined(__NetBSD__) || \
106     defined(__CYGWIN__) || defined(__OpenBSD__) || defined (__sun) || \
107     defined(__HAIKU__)
108
109                 /*
110                  * didn't find a way to set these per-socket, need to
111                  * tune kernel systemwide values
112                  */
113 #else
114                 /* set the keepalive conditions we want on it too */
115
116 #if defined(LWS_HAVE_TCP_USER_TIMEOUT)
117                 optval = 1000 * (vhost->ka_time +
118                                  (vhost->ka_interval * vhost->ka_probes));
119                 if (setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT,
120                                (const void *)&optval, optlen) < 0)
121                         return 1;
122 #endif
123                 optval = vhost->ka_time;
124                 if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE,
125                                (const void *)&optval, optlen) < 0)
126                         return 1;
127
128                 optval = vhost->ka_interval;
129                 if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL,
130                                (const void *)&optval, optlen) < 0)
131                         return 1;
132
133                 optval = vhost->ka_probes;
134                 if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT,
135                                (const void *)&optval, optlen) < 0)
136                         return 1;
137 #endif
138         }
139
140 #if defined(SO_BINDTODEVICE)
141         if (!unix_skt && vhost->bind_iface && vhost->iface) {
142                 lwsl_info("binding listen skt to %s using SO_BINDTODEVICE\n", vhost->iface);
143                 if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, vhost->iface,
144                                 strlen(vhost->iface)) < 0) {
145                         lwsl_warn("Failed to bind to device %s\n", vhost->iface);
146                         return 1;
147                 }
148         }
149 #endif
150
151         /* Disable Nagle */
152         optval = 1;
153 #if defined (__sun) || defined(__QNX__)
154         if (!unix_skt && setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0)
155                 return 1;
156 #elif !defined(__APPLE__) && \
157       !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) &&        \
158       !defined(__NetBSD__) && \
159       !defined(__OpenBSD__) && \
160       !defined(__HAIKU__)
161         if (!unix_skt && setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0)
162                 return 1;
163 #else
164         tcp_proto = getprotobyname("TCP");
165         if (!unix_skt && setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, &optval, optlen) < 0)
166                 return 1;
167 #endif
168
169         return lws_plat_set_nonblocking(fd);
170 }
171
172
173 /* cast a struct sockaddr_in6 * into addr for ipv6 */
174
175 int
176 lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
177                     size_t addrlen)
178 {
179         int rc = LWS_ITOSA_NOT_EXIST;
180
181         struct ifaddrs *ifr;
182         struct ifaddrs *ifc;
183 #ifdef LWS_WITH_IPV6
184         struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
185 #endif
186
187         getifaddrs(&ifr);
188         for (ifc = ifr; ifc != NULL && rc; ifc = ifc->ifa_next) {
189                 if (!ifc->ifa_addr)
190                         continue;
191
192                 lwsl_debug(" interface %s vs %s (fam %d) ipv6 %d\n",
193                            ifc->ifa_name, ifname,
194                            ifc->ifa_addr->sa_family, ipv6);
195
196                 if (strcmp(ifc->ifa_name, ifname))
197                         continue;
198
199                 switch (ifc->ifa_addr->sa_family) {
200 #if defined(AF_PACKET)
201                 case AF_PACKET:
202                         /* interface exists but is not usable */
203                         rc = LWS_ITOSA_NOT_USABLE;
204                         continue;
205 #endif
206
207                 case AF_INET:
208 #ifdef LWS_WITH_IPV6
209                         if (ipv6) {
210                                 /* map IPv4 to IPv6 */
211                                 memset((char *)&addr6->sin6_addr, 0,
212                                                 sizeof(struct in6_addr));
213                                 addr6->sin6_addr.s6_addr[10] = 0xff;
214                                 addr6->sin6_addr.s6_addr[11] = 0xff;
215                                 memcpy(&addr6->sin6_addr.s6_addr[12],
216                                        &((struct sockaddr_in *)ifc->ifa_addr)->sin_addr,
217                                                         sizeof(struct in_addr));
218                                 lwsl_debug("%s: uplevelling ipv4 bind to ipv6\n", __func__);
219                         } else
220 #endif
221                                 memcpy(addr,
222                                         (struct sockaddr_in *)ifc->ifa_addr,
223                                                     sizeof(struct sockaddr_in));
224                         break;
225 #ifdef LWS_WITH_IPV6
226                 case AF_INET6:
227                         memcpy(&addr6->sin6_addr,
228                           &((struct sockaddr_in6 *)ifc->ifa_addr)->sin6_addr,
229                                                        sizeof(struct in6_addr));
230                         break;
231 #endif
232                 default:
233                         continue;
234                 }
235                 rc = LWS_ITOSA_USABLE;
236         }
237
238         freeifaddrs(ifr);
239
240         if (rc) {
241                 /* check if bind to IP address */
242 #ifdef LWS_WITH_IPV6
243                 if (inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1)
244                         rc = LWS_ITOSA_USABLE;
245                 else
246 #endif
247                 if (inet_pton(AF_INET, ifname, &addr->sin_addr) == 1)
248                         rc = LWS_ITOSA_USABLE;
249         }
250
251         return rc;
252 }
253
254
255 const char *
256 lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
257 {
258         return inet_ntop(af, src, dst, cnt);
259 }
260
261 int
262 lws_plat_inet_pton(int af, const char *src, void *dst)
263 {
264         return inet_pton(af, src, dst);
265 }