socket.c (11241B)
1 #include <assert.h> 2 #include <inttypes.h> 3 #include <stdbool.h> 4 #include <stdio.h> 5 #include <string.h> 6 #include <uv.h> 7 8 #include "nvim/ascii_defs.h" 9 #include "nvim/charset.h" 10 #include "nvim/event/defs.h" 11 #include "nvim/event/loop.h" 12 #include "nvim/event/multiqueue.h" 13 #include "nvim/event/socket.h" 14 #include "nvim/event/stream.h" 15 #include "nvim/gettext_defs.h" 16 #include "nvim/log.h" 17 #include "nvim/main.h" 18 #include "nvim/memory.h" 19 #include "nvim/os/fs.h" 20 #include "nvim/os/os_defs.h" 21 #include "nvim/path.h" 22 #include "nvim/types_defs.h" 23 24 #include "event/socket.c.generated.h" 25 26 /// Checks if an address string looks like a TCP endpoint, and returns the end of the host part. 27 /// 28 /// @param address Address string 29 /// @return pointer to the end of the host part of the address, or NULL if it is not a TCP address 30 char *socket_address_tcp_host_end(const char *address) 31 { 32 if (address == NULL) { 33 return NULL; 34 } 35 36 // Windows drive letter path: "X:\..." or "X:/..." is a local path, not TCP. 37 if (ASCII_ISALPHA((uint8_t)address[0]) && address[1] == ':' 38 && (address[2] == '\\' || address[2] == '/')) { 39 return NULL; 40 } 41 42 char *colon = strrchr(address, ':'); 43 return colon != NULL && colon != address ? colon : NULL; 44 } 45 46 int socket_watcher_init(Loop *loop, SocketWatcher *watcher, const char *endpoint) 47 FUNC_ATTR_NONNULL_ALL 48 { 49 xstrlcpy(watcher->addr, endpoint, sizeof(watcher->addr)); 50 char *addr = watcher->addr; 51 char *host_end = socket_address_tcp_host_end(addr); 52 53 if (host_end) { 54 // Split user specified address into two strings, addr (hostname) and port. 55 // The port part in watcher->addr will be updated later. 56 *host_end = NUL; 57 char *port = host_end + 1; 58 intmax_t iport; 59 60 int ok = try_getdigits(&(char *){ port }, &iport); 61 if (!ok || iport < 0 || iport > UINT16_MAX) { 62 ELOG("Invalid port: %s", port); 63 return UV_EINVAL; 64 } 65 66 if (*port == NUL) { 67 // When no port is given, (uv_)getaddrinfo expects NULL otherwise the 68 // implementation may attempt to lookup the service by name (and fail) 69 port = NULL; 70 } 71 72 uv_getaddrinfo_t request; 73 74 int retval = uv_getaddrinfo(&loop->uv, &request, NULL, addr, port, 75 &(struct addrinfo){ .ai_family = AF_UNSPEC, 76 .ai_socktype = SOCK_STREAM, }); 77 if (retval != 0) { 78 ELOG("Host lookup failed: %s", endpoint); 79 return retval; 80 } 81 watcher->uv.tcp.addrinfo = request.addrinfo; 82 83 uv_tcp_init(&loop->uv, &watcher->uv.tcp.handle); 84 uv_tcp_nodelay(&watcher->uv.tcp.handle, true); 85 watcher->stream = (uv_stream_t *)(&watcher->uv.tcp.handle); 86 } else { 87 uv_pipe_init(&loop->uv, &watcher->uv.pipe.handle, 0); 88 watcher->stream = (uv_stream_t *)(&watcher->uv.pipe.handle); 89 } 90 91 watcher->stream->data = watcher; 92 watcher->cb = NULL; 93 watcher->close_cb = NULL; 94 watcher->events = NULL; 95 watcher->data = NULL; 96 97 return 0; 98 } 99 100 /// Callback for closing a handle initialized by socket_connect(). 101 static void connect_close_cb(uv_handle_t *handle) 102 { 103 bool *closed = handle->data; 104 *closed = true; 105 } 106 107 /// Check if a socket is alive by attempting to connect to it. 108 /// @param loop Event loop 109 /// @param addr Socket address to probe 110 /// @return true if socket is alive (connection succeeded), false otherwise 111 static bool socket_alive(Loop *loop, const char *addr) 112 { 113 RStream stream; 114 const char *error = NULL; 115 116 // Try to connect with a 500ms timeout (fast failure for dead sockets) 117 bool connected = socket_connect(loop, &stream, false, addr, 500, &error); 118 if (!connected) { 119 return false; 120 } 121 122 // Connection succeeded - socket is alive. Close the probe connection properly. 123 bool closed = false; 124 stream.s.uv.pipe.data = &closed; 125 uv_close((uv_handle_t *)&stream.s.uv.pipe, connect_close_cb); 126 LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, -1, closed); 127 128 return true; 129 } 130 131 int socket_watcher_start(SocketWatcher *watcher, int backlog, socket_cb cb) 132 FUNC_ATTR_NONNULL_ALL 133 { 134 watcher->cb = cb; 135 int result = UV_EINVAL; 136 137 if (watcher->stream->type == UV_TCP) { 138 struct addrinfo *ai = watcher->uv.tcp.addrinfo; 139 140 for (; ai; ai = ai->ai_next) { 141 result = uv_tcp_bind(&watcher->uv.tcp.handle, ai->ai_addr, 0); 142 if (result != 0) { 143 continue; 144 } 145 result = uv_listen(watcher->stream, backlog, connection_cb); 146 if (result == 0) { 147 struct sockaddr_storage sas; 148 149 // When the endpoint in socket_watcher_init() didn't specify a port 150 // number, a free random port number will be assigned. sin_port will 151 // contain 0 in this case, unless uv_tcp_getsockname() is used first. 152 uv_tcp_getsockname(&watcher->uv.tcp.handle, (struct sockaddr *)&sas, 153 &(int){ sizeof(sas) }); 154 uint16_t port = (sas.ss_family == AF_INET) ? ((struct sockaddr_in *)(&sas))->sin_port 155 : ((struct sockaddr_in6 *)(&sas))->sin6_port; 156 // v:servername uses the string from watcher->addr 157 size_t len = strlen(watcher->addr); 158 snprintf(watcher->addr + len, sizeof(watcher->addr) - len, ":%" PRIu16, 159 ntohs(port)); 160 break; 161 } 162 } 163 uv_freeaddrinfo(watcher->uv.tcp.addrinfo); 164 } else { 165 result = uv_pipe_bind(&watcher->uv.pipe.handle, watcher->addr); 166 167 // If bind failed with EACCES/EADDRINUSE, check if socket is stale 168 if (result == UV_EACCES || result == UV_EADDRINUSE) { 169 Loop *loop = watcher->stream->loop->data; 170 171 if (!socket_alive(loop, watcher->addr)) { 172 // Socket exists but is dead - remove it 173 ILOG("Removing stale socket: %s", watcher->addr); 174 int rm_result = os_remove(watcher->addr); 175 176 if (rm_result != 0) { 177 WLOG("Failed to remove stale socket %s: %s", 178 watcher->addr, uv_strerror(rm_result)); 179 } else { 180 // Close and reinit the pipe handle before retrying bind 181 uv_loop_t *uv_loop = watcher->uv.pipe.handle.loop; 182 bool closed = false; 183 watcher->uv.pipe.handle.data = &closed; 184 uv_close((uv_handle_t *)&watcher->uv.pipe.handle, connect_close_cb); 185 LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, -1, closed); 186 187 uv_pipe_init(uv_loop, &watcher->uv.pipe.handle, 0); 188 watcher->stream = (uv_stream_t *)(&watcher->uv.pipe.handle); 189 watcher->stream->data = watcher; 190 191 // Retry bind with fresh handle 192 result = uv_pipe_bind(&watcher->uv.pipe.handle, watcher->addr); 193 } 194 } else { 195 // Socket is alive - this is a real error 196 ELOG("Socket already in use by another Nvim instance: %s", watcher->addr); 197 } 198 } 199 200 if (result == 0) { 201 result = uv_listen(watcher->stream, backlog, connection_cb); 202 } 203 } 204 205 assert(result <= 0); // libuv should return negative error code or zero. 206 if (result < 0) { 207 if (result == UV_EACCES) { 208 // Libuv converts ENOENT to EACCES for Windows compatibility, but if 209 // the parent directory does not exist, ENOENT would be more accurate. 210 *path_tail(watcher->addr) = NUL; 211 if (!os_path_exists(watcher->addr)) { 212 result = UV_ENOENT; 213 } 214 } 215 return result; 216 } 217 218 return 0; 219 } 220 221 int socket_watcher_accept(SocketWatcher *watcher, RStream *stream) 222 FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_ARG(2) 223 { 224 uv_stream_t *client; 225 226 if (watcher->stream->type == UV_TCP) { 227 client = (uv_stream_t *)(&stream->s.uv.tcp); 228 uv_tcp_init(watcher->uv.tcp.handle.loop, (uv_tcp_t *)client); 229 uv_tcp_nodelay((uv_tcp_t *)client, true); 230 } else { 231 client = (uv_stream_t *)&stream->s.uv.pipe; 232 uv_pipe_init(watcher->uv.pipe.handle.loop, (uv_pipe_t *)client, 0); 233 } 234 235 int result = uv_accept(watcher->stream, client); 236 237 if (result) { 238 uv_close((uv_handle_t *)client, NULL); 239 return result; 240 } 241 242 stream_init(NULL, &stream->s, -1, false, client); 243 return 0; 244 } 245 246 void socket_watcher_close(SocketWatcher *watcher, socket_close_cb cb) 247 FUNC_ATTR_NONNULL_ARG(1) 248 { 249 watcher->close_cb = cb; 250 uv_close((uv_handle_t *)watcher->stream, close_cb); 251 } 252 253 static void connection_event(void **argv) 254 { 255 SocketWatcher *watcher = argv[0]; 256 int status = (int)(uintptr_t)(argv[1]); 257 watcher->cb(watcher, status, watcher->data); 258 } 259 260 static void connection_cb(uv_stream_t *handle, int status) 261 { 262 SocketWatcher *watcher = handle->data; 263 CREATE_EVENT(watcher->events, connection_event, watcher, (void *)(uintptr_t)status); 264 } 265 266 static void close_cb(uv_handle_t *handle) 267 { 268 SocketWatcher *watcher = handle->data; 269 if (watcher->close_cb) { 270 watcher->close_cb(watcher, watcher->data); 271 } 272 } 273 274 static void connect_cb(uv_connect_t *req, int status) 275 { 276 int *ret_status = req->data; 277 *ret_status = status; 278 uv_handle_t *handle = (uv_handle_t *)req->handle; 279 if (status != 0 && !uv_is_closing(handle)) { 280 uv_close(handle, connect_close_cb); 281 } 282 } 283 284 bool socket_connect(Loop *loop, RStream *stream, bool is_tcp, const char *address, int timeout, 285 const char **error) 286 { 287 bool success = false; 288 bool closed; 289 int status; 290 uv_connect_t req; 291 req.data = &status; 292 uv_stream_t *uv_stream; 293 294 uv_tcp_t *tcp = &stream->s.uv.tcp; 295 uv_getaddrinfo_t addr_req; 296 addr_req.addrinfo = NULL; 297 const struct addrinfo *addrinfo = NULL; 298 char *addr = NULL; 299 if (is_tcp) { 300 addr = xstrdup(address); 301 char *host_end = strrchr(addr, ':'); 302 if (!host_end) { 303 *error = _("tcp address must be host:port"); 304 goto cleanup; 305 } 306 *host_end = NUL; 307 308 const struct addrinfo hints = { .ai_family = AF_UNSPEC, 309 .ai_socktype = SOCK_STREAM, 310 .ai_flags = AI_NUMERICSERV }; 311 int retval = uv_getaddrinfo(&loop->uv, &addr_req, NULL, 312 addr, host_end + 1, &hints); 313 if (retval != 0) { 314 *error = _("failed to lookup host or port"); 315 goto cleanup; 316 } 317 addrinfo = addr_req.addrinfo; 318 319 tcp_retry: 320 uv_tcp_init(&loop->uv, tcp); 321 uv_tcp_nodelay(tcp, true); 322 uv_tcp_connect(&req, tcp, addrinfo->ai_addr, connect_cb); 323 uv_stream = (uv_stream_t *)tcp; 324 } else { 325 uv_pipe_t *pipe = &stream->s.uv.pipe; 326 uv_pipe_init(&loop->uv, pipe, 0); 327 uv_pipe_connect(&req, pipe, address, connect_cb); 328 uv_stream = (uv_stream_t *)pipe; 329 } 330 uv_stream->data = &closed; 331 closed = false; 332 status = 1; 333 LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, timeout, status != 1); 334 if (status == 0) { 335 stream_init(NULL, &stream->s, -1, false, uv_stream); 336 assert(uv_stream->data != &closed); // Should have been set by stream_init(). 337 success = true; 338 } else { 339 if (!uv_is_closing((uv_handle_t *)uv_stream)) { 340 uv_close((uv_handle_t *)uv_stream, connect_close_cb); 341 } 342 // Wait for the close callback to arrive before retrying or returning, otherwise 343 // it may lead to a hang or stack-use-after-return. 344 LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, -1, closed); 345 346 if (is_tcp && addrinfo->ai_next) { 347 addrinfo = addrinfo->ai_next; 348 goto tcp_retry; 349 } else { 350 *error = _("connection refused"); 351 } 352 } 353 354 cleanup: 355 xfree(addr); 356 uv_freeaddrinfo(addr_req.addrinfo); 357 return success; 358 }