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 0 : scan_unix_sockets(Mapi mid)
29 : {
30 0 : struct {
31 : int port;
32 : int priority;
33 : } candidates[MAX_SCAN];
34 0 : int ncandidates = 0;
35 0 : DIR *dir = NULL;
36 0 : struct dirent *entry;
37 :
38 0 : const char *sockdir = msetting_string(mid->settings, MP_SOCKDIR);
39 0 : size_t len = strlen(sockdir);
40 0 : char *namebuf = malloc(len + 50);
41 0 : if (namebuf == NULL)
42 0 : return mapi_setError(mid, "malloc failed", __func__, MERROR);
43 0 : strcpy(namebuf, sockdir);
44 0 : strcpy(namebuf + len, "/.s.monetdb.PORTXXXXX");
45 0 : char *put_port_here = strrchr(namebuf, 'P');
46 :
47 0 : msettings *original = mid->settings;
48 0 : mid->settings = NULL; // invalid state, will fix it before use and on return
49 :
50 0 : mapi_log_record(mid, "CONN", "Scanning %s for Unix domain sockets", sockdir);
51 :
52 : // Make a list of Unix domain sockets in /tmp
53 0 : uid_t me = getuid();
54 0 : dir = opendir(sockdir);
55 0 : if (dir) {
56 0 : while (ncandidates < MAX_SCAN && (entry = readdir(dir)) != NULL) {
57 0 : const char *basename = entry->d_name;
58 0 : if (strncmp(basename, ".s.monetdb.", 11) != 0 || basename[11] == '\0' || strlen(basename) > 20)
59 0 : 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 0 : closedir(dir);
75 : }
76 :
77 0 : mapi_log_record(mid, "CONN", "Found %d Unix domain sockets", ncandidates);
78 :
79 : // Try those owned by us first, then all others
80 0 : for (int round = 0; round < 2; round++) {
81 0 : 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 0 : free(namebuf);
121 0 : assert(mid->settings == NULL);
122 0 : mid->settings = original;
123 0 : 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 65 : connect_socket_unix(Mapi mid)
130 : {
131 65 : const char *sockname = msettings_connect_unix(mid->settings);
132 65 : assert (*sockname != '\0');
133 :
134 65 : mapi_log_record(mid, "CONN", "Connecting to Unix domain socket %s", sockname);
135 :
136 65 : struct sockaddr_un userver;
137 65 : if (strlen(sockname) >= sizeof(userver.sun_path)) {
138 0 : return mapi_printError(mid, __func__, MERROR, "path name '%s' too long", sockname);
139 : }
140 :
141 : // Create the socket, taking care of CLOEXEC
142 :
143 : #ifdef SOCK_CLOEXEC
144 65 : int s = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
145 : #else
146 : int s = socket(PF_UNIX, SOCK_STREAM, 0);
147 : #endif
148 65 : if (s == INVALID_SOCKET) {
149 0 : return mapi_printError(
150 : mid, __func__, MERROR,
151 0 : "could not create Unix domain socket '%s': %s", sockname, strerror(errno));
152 : }
153 : #if !defined(SOCK_CLOEXEC) && defined(HAVE_FCNTL)
154 : (void) fcntl(s, F_SETFD, FD_CLOEXEC);
155 : #endif
156 :
157 : // Attempt to connect
158 :
159 65 : userver = (struct sockaddr_un) {
160 : .sun_family = AF_UNIX,
161 : };
162 65 : strcpy_len(userver.sun_path, sockname, sizeof(userver.sun_path));
163 :
164 65 : if (connect(s, (struct sockaddr *) &userver, sizeof(struct sockaddr_un)) == SOCKET_ERROR) {
165 2 : closesocket(s);
166 2 : return mapi_printError(
167 : mid, __func__, MERROR,
168 2 : "connect to Unix domain socket '%s' failed: %s", sockname, strerror(errno));
169 : }
170 :
171 : // Send an initial zero (not NUL) to let the server know we're not passing a file
172 : // descriptor.
173 :
174 63 : ssize_t n = send(s, "0", 1, 0);
175 63 : if (n < 1) {
176 : // used to be if n < 0 but this makes more sense
177 0 : closesocket(s);
178 0 : return mapi_printError(
179 : mid, __func__, MERROR,
180 0 : "could not send initial '0' on Unix domain socket: %s", strerror(errno));
181 : }
182 :
183 63 : return wrap_socket(mid, s);
184 : }
|