1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 *
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V.
7 */
8
9#include "monetdb_config.h"
10#include <sys/types.h>
11#include <sys/stat.h> /* stat */
12#include <sys/wait.h> /* wait */
13#include <sys/socket.h>
14#include <sys/un.h>
15#include <netdb.h>
16#include <netinet/in.h>
17#include <fcntl.h>
18#include <string.h> /* strerror */
19
20#include "mstring.h"
21#include "stream.h"
22#include "stream_socket.h"
23
24#include "merovingian.h"
25#include "connections.h"
26
27err
28openConnectionTCP(int *ret, bool bind_ipv6, const char *bindaddr, unsigned short port, FILE *log)
29{
30 struct sockaddr *server;
31 struct sockaddr_in server_ipv4;
32 struct sockaddr_in6 server_ipv6;
33 struct addrinfo *result = NULL, *rp = NULL;
34 int sock = -1, check = 0;
35 socklen_t length = 0;
36 int on = 1;
37 int i = 0;
38 char sport[16];
39 char host[512];
40
41 snprintf(sport, 16, "%hu", port);
42 if (bindaddr) {
43 struct addrinfo hints = (struct addrinfo) {
44 .ai_family = bind_ipv6 ? AF_INET6 : AF_INET,
45 .ai_socktype = SOCK_STREAM,
46 .ai_flags = AI_PASSIVE,
47 .ai_protocol = IPPROTO_TCP,
48 };
49
50 check = getaddrinfo(bindaddr, sport, &hints, &result);
51 if (check != 0)
52 return newErr("cannot find host %s with error: %s", bindaddr, gai_strerror(check));
53
54 for (rp = result; rp != NULL; rp = rp->ai_next) {
55 sock = socket(rp->ai_family, rp->ai_socktype
56#ifdef SOCK_CLOEXEC
57 | SOCK_CLOEXEC
58#endif
59 , rp->ai_protocol);
60 if (sock == -1)
61 continue;
62#if !defined(SOCK_CLOEXEC) && defined(HAVE_FCNTL)
63 (void) fcntl(sock, F_SETFD, FD_CLOEXEC);
64#endif
65
66 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof on) < 0) {
67 closesocket(sock);
68 continue;
69 }
70
71 if (bind(sock, rp->ai_addr, rp->ai_addrlen) != -1)
72 break; /* working */
73 }
74 if (rp == NULL) {
75 int e = errno;
76 if (result)
77 freeaddrinfo(result);
78 if (sock != -1)
79 closesocket(sock);
80 if (result) { /* results found, tried socket, setsockopt and bind calls */
81 errno = e;
82 return newErr("binding to stream socket port %hu failed: %s", port, strerror(errno));
83 } else { /* no results found, could not translate address */
84 return newErr("cannot translate host %s", bindaddr);
85 }
86 }
87 server = rp->ai_addr;
88 length = rp->ai_addrlen;
89 } else {
90 sock = socket(bind_ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM
91#ifdef SOCK_CLOEXEC
92 | SOCK_CLOEXEC
93#endif
94 , 0);
95 if (sock == -1)
96 return(newErr("creation of stream socket failed: %s", strerror(errno)));
97
98 if (bind_ipv6) {
99 memset(&server_ipv6, 0, sizeof(server_ipv6));
100 server_ipv6.sin6_family = AF_INET6;
101 length = (socklen_t) sizeof(server_ipv6);
102 server_ipv6.sin6_port = htons((unsigned short) ((port) & 0xFFFF));
103 server_ipv6.sin6_addr = ipv6_any_addr;
104 } else {
105 server_ipv4.sin_family = AF_INET;
106 for (i = 0; i < 8; i++)
107 server_ipv4.sin_zero[i] = 0;
108 length = (socklen_t) sizeof(server_ipv4);
109 server_ipv4.sin_port = htons((unsigned short) ((port) & 0xFFFF));
110 server_ipv4.sin_addr.s_addr = htonl(INADDR_ANY);
111 }
112 server = bind_ipv6 ? (struct sockaddr*) &server_ipv6 : (struct sockaddr*) &server_ipv4;
113
114#if !defined(SOCK_CLOEXEC) && defined(HAVE_FCNTL)
115 (void) fcntl(sock, F_SETFD, FD_CLOEXEC);
116#endif
117 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof on) < 0) {
118 int e = errno;
119 closesocket(sock);
120 errno = e;
121 return newErr("setsockopt unexpectedly failed: %s", strerror(errno));
122 }
123
124 if (bind(sock, server, length) == -1) {
125 int e = errno;
126 closesocket(sock);
127 errno = e;
128 return(newErr("binding to stream socket port %hu failed: %s", port, strerror(errno)));
129 }
130
131 if (getsockname(sock, server, &length) == -1) {
132 int e = errno;
133 closesocket(sock);
134 errno = e;
135 return(newErr("failed getting socket name: %s", strerror(errno)));
136 }
137 }
138
139 check = getnameinfo(server, length, host, sizeof(host), sport, sizeof(sport), NI_NUMERICSERV);
140 if (result)
141 freeaddrinfo(result);
142 if (check != 0) {
143 closesocket(sock);
144 return(newErr("failed getting socket name: %s", gai_strerror(check)));
145 }
146
147 /* keep queue of 5 */
148 if (listen(sock, 5) == -1) {
149 int e = errno;
150 closesocket(sock);
151 errno = e;
152 return(newErr("failed setting socket to listen: %s", strerror(errno)));
153 }
154
155 Mfprintf(log, "accepting connections on TCP socket %s:%s\n", host, sport);
156
157 *ret = sock;
158 return(NO_ERR);
159}
160
161err
162openConnectionUDP(int *ret, bool bind_ipv6, const char *bindaddr, unsigned short port)
163{
164 struct addrinfo hints;
165 struct addrinfo *result, *rp;
166 int sock = -1;
167
168 char sport[10];
169 char host[512];
170
171 hints = (struct addrinfo) {
172 .ai_family = bind_ipv6 ? AF_INET6 : AF_INET,
173 .ai_socktype = SOCK_DGRAM, /* Datagram socket */
174 .ai_flags = AI_PASSIVE, /* For wildcard IP address */
175 .ai_protocol = 0, /* Any protocol */
176 .ai_canonname = NULL,
177 .ai_addr = NULL,
178 .ai_next = NULL,
179 };
180
181 snprintf(sport, 10, "%hu", port);
182 sock = getaddrinfo(bindaddr, sport, &hints, &result);
183 if (sock != 0)
184 return(newErr("failed getting address info: %s", gai_strerror(sock)));
185
186 for (rp = result; rp != NULL; rp = rp->ai_next) {
187 sock = socket(rp->ai_family, rp->ai_socktype
188#ifdef SOCK_CLOEXEC
189 | SOCK_CLOEXEC
190#endif
191 , rp->ai_protocol);
192 if (sock == -1)
193 continue;
194#if !defined(SOCK_CLOEXEC) && defined(HAVE_FCNTL)
195 (void) fcntl(sock, F_SETFD, FD_CLOEXEC);
196#endif
197
198 if (bind(sock, rp->ai_addr, rp->ai_addrlen) != -1)
199 break; /* working */
200
201 closesocket(sock);
202 }
203
204 if (rp == NULL) {
205 freeaddrinfo(result);
206 return(newErr("binding to datagram socket port %hu failed: "
207 "no available address", port));
208 }
209
210 /* retrieve information from the socket */
211 if(getnameinfo(rp->ai_addr, rp->ai_addrlen,
212 host, sizeof(host),
213 sport, sizeof(sport),
214 NI_NUMERICSERV | NI_DGRAM) == 0) {
215 Mfprintf(_mero_discout, "listening for UDP messages on %s:%s\n", host, sport);
216 } else {
217 Mfprintf(_mero_discout, "listening for UDP messages\n");
218 }
219
220 freeaddrinfo(result);
221
222 *ret = sock;
223 return(NO_ERR);
224}
225
226err
227openConnectionUNIX(int *ret, const char *path, int mode, FILE *log)
228{
229 struct sockaddr_un server;
230 int sock;
231 int omask;
232
233 if (strlen(path) >= sizeof(server.sun_path))
234 return newErr("pathname for UNIX stream socket too long");
235
236 sock = socket(AF_UNIX, SOCK_STREAM
237#ifdef SOCK_CLOEXEC
238 | SOCK_CLOEXEC
239#endif
240 , 0);
241 if (sock == -1)
242 return(newErr("creation of UNIX stream socket failed: %s",
243 strerror(errno)));
244#if !defined(SOCK_CLOEXEC) && defined(HAVE_FCNTL)
245 (void) fcntl(sock, F_SETFD, FD_CLOEXEC);
246#endif
247
248 server = (struct sockaddr_un) {
249 .sun_family = AF_UNIX,
250 };
251 strcpy_len(server.sun_path, path, sizeof(server.sun_path));
252
253 /* have to use umask to restrict permissions to avoid a race
254 * condition */
255 omask = umask(mode);
256 if (bind(sock, (SOCKPTR) &server, sizeof(struct sockaddr_un)) == -1) {
257 umask(omask);
258 closesocket(sock);
259 return(newErr("binding to UNIX stream socket at %s failed: %s",
260 path, strerror(errno)));
261 }
262 umask(omask);
263
264 /* keep queue of 5 */
265 if(listen(sock, 5) == -1) {
266 closesocket(sock);
267 return(newErr("setting UNIX stream socket at %s to listen failed: %s",
268 path, strerror(errno)));
269 }
270
271 Mfprintf(log, "accepting connections on UNIX domain socket %s\n", path);
272
273 *ret = sock;
274 return(NO_ERR);
275}
276
277/* vim:set ts=4 sw=4 noexpandtab: */
278