Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : #include "monetdb_config.h"
14 : #include "stream.h" /* include before mapi.h */
15 : #include "stream_socket.h"
16 : #include "mapi.h"
17 : #include "mapi_prompt.h"
18 : #include "mcrypt.h"
19 : #include "matomic.h"
20 : #include "mstring.h"
21 : #include "mutils.h"
22 :
23 : #include "mapi_intern.h"
24 :
25 : #define MAX_SCAN (24)
26 :
27 : MapiMsg
28 18 : scan_unix_sockets(Mapi mid)
29 : {
30 18 : struct {
31 : int port;
32 : int priority;
33 : } candidates[MAX_SCAN];
34 18 : int ncandidates = 0;
35 18 : DIR *dir = NULL;
36 18 : struct dirent *entry;
37 :
38 18 : const char *sockdir = msetting_string(mid->settings, MP_SOCKDIR);
39 18 : size_t len = strlen(sockdir);
40 18 : char *namebuf = malloc(len + 50);
41 18 : if (namebuf == NULL)
42 0 : return mapi_setError(mid, "malloc failed", __func__, MERROR);
43 18 : strcpy(namebuf, sockdir);
44 18 : strcpy(namebuf + len, "/.s.monetdb.PORTXXXXX");
45 18 : char *put_port_here = strrchr(namebuf, 'P');
46 :
47 18 : msettings *original = mid->settings;
48 18 : mid->settings = NULL; // invalid state, will fix it before use and on return
49 :
50 18 : mapi_log_record(mid, "CONN", "Scanning %s for Unix domain sockets", sockdir);
51 :
52 : // Make a list of Unix domain sockets in /tmp
53 18 : uid_t me = getuid();
54 18 : dir = opendir(sockdir);
55 18 : if (dir) {
56 168 : while (ncandidates < MAX_SCAN && (entry = readdir(dir)) != NULL) {
57 150 : const char *basename = entry->d_name;
58 150 : if (strncmp(basename, ".s.monetdb.", 11) != 0 || basename[11] == '\0' || strlen(basename) > 20)
59 150 : continue;
60 :
61 0 : char *end;
62 0 : long port = strtol(basename + 11, &end, 10);
63 0 : if (port < 1 || port > 65535 || *end)
64 0 : continue;
65 :
66 0 : sprintf(put_port_here, "%ld", port);
67 0 : struct stat st;
68 0 : if (stat(namebuf, &st) < 0 || !S_ISSOCK(st.st_mode))
69 0 : continue;
70 :
71 0 : candidates[ncandidates].port = port;
72 0 : candidates[ncandidates++].priority = st.st_uid == me ? 0 : 1;
73 : }
74 18 : closedir(dir);
75 : }
76 :
77 18 : mapi_log_record(mid, "CONN", "Found %d Unix domain sockets", ncandidates);
78 :
79 : // Try those owned by us first, then all others
80 54 : for (int round = 0; round < 2; round++) {
81 36 : for (int i = 0; i < ncandidates; i++) {
82 0 : if (candidates[i].priority != round)
83 0 : continue;
84 :
85 0 : assert(!mid->connected);
86 0 : assert(mid->settings == NULL);
87 0 : mid->settings = msettings_clone(original);
88 0 : if (!mid->settings) {
89 0 : mid->settings = original;
90 0 : free(namebuf);
91 0 : return mapi_setError(mid, "malloc failed", __func__, MERROR);
92 : }
93 0 : msettings_error errmsg = msetting_set_long(mid->settings, MP_PORT, candidates[i].port);
94 0 : char *allocated_errmsg = NULL;
95 0 : if (!errmsg && !msettings_validate(mid->settings, &allocated_errmsg)) {
96 0 : errmsg = allocated_errmsg;
97 : }
98 0 : if (errmsg) {
99 0 : mapi_setError(mid, errmsg, __func__, MERROR);
100 0 : free(allocated_errmsg);
101 0 : free(namebuf);
102 0 : msettings_destroy(mid->settings);
103 0 : mid->settings = original;
104 0 : return MERROR;
105 : }
106 0 : MapiMsg msg = establish_connection(mid);
107 0 : if (msg == MOK) {
108 : // do not restore original
109 0 : msettings_destroy(original);
110 0 : free(namebuf);
111 0 : return MOK;
112 : } else {
113 0 : msettings_destroy(mid->settings);
114 0 : mid->settings = NULL;
115 : // now we're ready to try another one
116 : }
117 : }
118 : }
119 :
120 18 : free(namebuf);
121 18 : assert(mid->settings == NULL);
122 18 : mid->settings = original;
123 18 : mapi_log_record(mid, "CONN", "All %d Unix domain sockets failed. Falling back to TCP", ncandidates);
124 : return MERROR;
125 : }
126 :
127 :
128 : MapiMsg
129 50 : connect_socket_unix(Mapi mid)
130 : {
131 50 : const char *sockname = msettings_connect_unix(mid->settings);
132 50 : assert (*sockname != '\0');
133 50 : long timeout = msetting_long(mid->settings, MP_CONNECT_TIMEOUT);
134 :
135 50 : mapi_log_record(mid, "CONN", "Connecting to Unix domain socket %s with timeout %ld", sockname, timeout);
136 :
137 50 : struct sockaddr_un userver;
138 50 : if (strlen(sockname) >= sizeof(userver.sun_path)) {
139 0 : return mapi_printError(mid, __func__, MERROR, "path name '%s' too long", sockname);
140 : }
141 :
142 : // Create the socket, taking care of CLOEXEC and SNDTIMEO
143 :
144 : #ifdef SOCK_CLOEXEC
145 50 : int s = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
146 : #else
147 : int s = socket(PF_UNIX, SOCK_STREAM, 0);
148 : #endif
149 50 : if (s == INVALID_SOCKET) {
150 0 : return mapi_printError(
151 : mid, __func__, MERROR,
152 0 : "could not create Unix domain socket '%s': %s", sockname, strerror(errno));
153 : }
154 : #if !defined(SOCK_CLOEXEC) && defined(HAVE_FCNTL)
155 : (void) fcntl(s, F_SETFD, FD_CLOEXEC);
156 : #endif
157 :
158 50 : if (timeout > 0) {
159 0 : struct timeval tv = {
160 0 : .tv_sec = timeout / 1000,
161 0 : .tv_usec = timeout % 1000,
162 : };
163 0 : if (
164 0 : setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == SOCKET_ERROR
165 0 : || setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == SOCKET_ERROR
166 : ) {
167 0 : closesocket(s);
168 0 : return mapi_printError(
169 : mid, __func__, MERROR,
170 0 : "could not set connect timeout: %s", strerror(errno));
171 : }
172 : }
173 :
174 : // Attempt to connect
175 :
176 50 : userver = (struct sockaddr_un) {
177 : .sun_family = AF_UNIX,
178 : };
179 50 : strcpy_len(userver.sun_path, sockname, sizeof(userver.sun_path));
180 :
181 50 : if (connect(s, (struct sockaddr *) &userver, sizeof(struct sockaddr_un)) == SOCKET_ERROR) {
182 12 : closesocket(s);
183 12 : return mapi_printError(
184 : mid, __func__, MERROR,
185 12 : "connect to Unix domain socket '%s' failed: %s", sockname, strerror(errno));
186 : }
187 :
188 : // Send an initial zero (not NUL) to let the server know we're not passing a file
189 : // descriptor.
190 :
191 38 : ssize_t n = send(s, "0", 1, 0);
192 38 : if (n < 1) {
193 : // used to be if n < 0 but this makes more sense
194 0 : closesocket(s);
195 0 : return mapi_printError(
196 : mid, __func__, MERROR,
197 0 : "could not send initial '0' on Unix domain socket: %s", strerror(errno));
198 : }
199 :
200 38 : return wrap_socket(mid, s);
201 : }
|