1 /* 2 * Copyright (c) 2002, 2025, Oracle and/or its affiliates. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 * DEALINGS IN THE SOFTWARE. 22 */ 23 /* 24 25 Copyright 1988, 1998 The Open Group 26 27 Permission to use, copy, modify, distribute, and sell this software and its 28 documentation for any purpose is hereby granted without fee, provided that 29 the above copyright notice appear in all copies and that both that 30 copyright notice and this permission notice appear in supporting 31 documentation. 32 33 The above copyright notice and this permission notice shall be included 34 in all copies or substantial portions of the Software. 35 36 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 37 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 38 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 39 IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 40 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 41 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 42 OTHER DEALINGS IN THE SOFTWARE. 43 44 Except as contained in this notice, the name of a copyright holder shall 45 not be used in advertising or otherwise to promote the sale, use or 46 other dealings in this Software without prior written authorization 47 from the copyright holder. 48 49 */ 50 51 /* 52 * xdm - display manager daemon 53 * Author: Keith Packard, MIT X Consortium 54 * 55 * socket.c - Support for BSD sockets 56 */ 57 58 #include "dm.h" 59 #include "dm_error.h" 60 61 #ifdef XDMCP 62 63 # include <errno.h> 64 # include "dm_socket.h" 65 66 # include <sys/un.h> 67 # include <netdb.h> 68 # include <arpa/inet.h> 69 70 extern int chooserFd; 71 72 extern FD_TYPE WellKnownSocketsMask; 73 extern int WellKnownSocketsMax; 74 75 void 76 CreateWellKnownSockets (void) 77 { 78 char *name = localHostname (); 79 registerHostname (name, strlen (name)); 80 81 if (request_port == 0) 82 return; 83 84 # ifdef IPv6 85 chooserFd = socket (AF_INET6, SOCK_STREAM, 0); 86 if (chooserFd == -1) 87 # endif 88 chooserFd = socket (AF_INET, SOCK_STREAM, 0); 89 Debug ("Created chooser socket %d\n", chooserFd); 90 if (chooserFd == -1) 91 { 92 LogError ("chooser socket creation failed: %s\n", _SysErrorMsg (errno)); 93 return; 94 } 95 listen (chooserFd, 5); 96 if (chooserFd > WellKnownSocketsMax) 97 WellKnownSocketsMax = chooserFd; 98 FD_SET (chooserFd, &WellKnownSocketsMask); 99 } 100 101 int 102 GetChooserAddr ( 103 char *addr, 104 int *lenp) 105 { 106 # ifdef HAVE_STRUCT_SOCKADDR_STORAGE 107 struct sockaddr_storage in_addr; 108 # else 109 struct sockaddr_in in_addr; 110 # endif 111 int len; 112 int retval = 0; 113 114 len = sizeof in_addr; 115 if (chooserFd < 0) 116 return -1; /* TODO check other listening sockets */ 117 if (getsockname (chooserFd, (struct sockaddr *)&in_addr, (void *)&len) < 0) 118 return -1; 119 if (len > sizeof in_addr) 120 return -1; 121 # ifdef IPv6 122 if (((struct sockaddr *)&in_addr)->sa_family == AF_INET6) 123 Debug ("Chooser socket port: %d (IPv6)\n", 124 ntohs(((struct sockaddr_in6 *) &in_addr)->sin6_port)); 125 else 126 # endif 127 Debug ("Chooser socket port: %d\n", 128 ntohs(((struct sockaddr_in *) &in_addr)->sin_port)); 129 if (*lenp < len) 130 retval = -2; 131 else 132 memcpy(addr, &in_addr, len); 133 *lenp = len; 134 135 return retval; 136 } 137 138 static int 139 CreateListeningSocket (struct sockaddr *sock_addr, int salen) 140 { 141 int fd; 142 const char *addrstring = "unknown"; 143 # ifdef IPv6 144 char addrbuf[INET6_ADDRSTRLEN]; 145 # elif defined(HAVE_INET_NTOP) 146 char addrbuf[INET_ADDRSTRLEN] = ""; 147 # endif 148 149 if (request_port == 0) 150 return -1; 151 152 if (debugLevel > 0) { 153 # ifdef HAVE_INET_NTOP 154 void *ipaddr; 155 # ifdef IPv6 156 if (sock_addr->sa_family == AF_INET6) { 157 ipaddr = & ((struct sockaddr_in6 *) sock_addr)->sin6_addr; 158 } else 159 # endif 160 { 161 ipaddr = & ((struct sockaddr_in *) sock_addr)->sin_addr; 162 } 163 addrstring = 164 inet_ntop(sock_addr->sa_family, ipaddr, addrbuf, sizeof(addrbuf)); 165 166 # else 167 addrstring = inet_ntoa(((struct sockaddr_in *) sock_addr)->sin_addr); 168 # endif 169 170 Debug ("creating socket to listen on port %d of address %s\n", 171 request_port,addrstring); 172 } 173 174 fd = socket (sock_addr->sa_family, SOCK_DGRAM, 0); 175 176 if (fd == -1) { 177 LogError ("XDMCP socket creation failed: %s\n", _SysErrorMsg (errno)); 178 return fd; 179 } 180 RegisterCloseOnFork (fd); 181 182 # if defined(IPv6) && defined(IPV6_V6ONLY) 183 if (sock_addr->sa_family == AF_INET6) { 184 int zero = 0; 185 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero)) < 0) { 186 LogError ("Could not disable V6ONLY on XDMCP socket: %s\n", 187 _SysErrorMsg (errno)); 188 } 189 } 190 # endif 191 192 if (bind (fd, sock_addr, salen) == -1) 193 { 194 LogError ("error binding socket address %d: %s\n", request_port, 195 _SysErrorMsg (errno)); 196 close (fd); 197 fd = -1; 198 return fd; 199 } 200 if (fd > WellKnownSocketsMax) 201 WellKnownSocketsMax = fd; 202 FD_SET (fd, &WellKnownSocketsMask); 203 return fd; 204 } 205 206 struct socklist { 207 struct socklist * next; 208 struct socklist * mcastgroups; 209 struct sockaddr * addr; 210 int salen; 211 int addrlen; 212 int fd; 213 int ref; /* referenced bit - see UpdateListenSockets */ 214 }; 215 216 static struct socklist *listensocks; 217 218 static void 219 DestroyListeningSocket (struct socklist *s) 220 { 221 if (s->fd >= 0) { 222 FD_CLR (s->fd, &WellKnownSocketsMask); 223 close(s->fd); 224 s->fd = -1; 225 } 226 if (s->addr) { 227 free(s->addr); 228 s->addr = NULL; 229 } 230 if (s->mcastgroups) { 231 struct socklist *g, *n; 232 233 for (g = s->mcastgroups; g != NULL; g = n) { 234 n = g->next; 235 free(g->addr); 236 free(g); 237 } 238 s->mcastgroups = NULL; 239 } 240 } 241 242 static struct socklist* 243 FindInList(struct socklist *list, ARRAY8Ptr addr) 244 { 245 struct socklist *s; 246 247 for (s = list; s != NULL; s = s->next) { 248 if (s->addrlen == addr->length) { 249 char *addrdata; 250 251 switch (s->addr->sa_family) { 252 case AF_INET: 253 addrdata = (char *) 254 &(((struct sockaddr_in *)s->addr)->sin_addr.s_addr); 255 break; 256 # ifdef IPv6 257 case AF_INET6: 258 addrdata = (char *) 259 &(((struct sockaddr_in6 *)s->addr)->sin6_addr.s6_addr); 260 break; 261 # endif 262 default: 263 /* Unrecognized address family */ 264 continue; 265 } 266 if (memcmp(addrdata, addr->data, addr->length) == 0) { 267 return s; 268 } 269 } 270 } 271 return NULL; 272 } 273 274 static struct socklist * 275 CreateSocklistEntry(ARRAY8Ptr addr) 276 { 277 struct socklist *s = malloc (sizeof(struct socklist)); 278 if (s == NULL) { 279 LogOutOfMem("CreateSocklistEntry"); 280 return NULL; 281 } 282 283 bzero(s, sizeof(struct socklist)); 284 285 if (addr->length == 4) /* IPv4 */ 286 { 287 struct sockaddr_in *sin; 288 sin = malloc (sizeof(struct sockaddr_in)); 289 if (sin == NULL) { 290 LogOutOfMem("CreateSocklistEntry"); 291 free(s); 292 return NULL; 293 } 294 s->addr = (struct sockaddr *) sin; 295 296 bzero (sin, sizeof (struct sockaddr_in)); 297 # ifdef BSD44SOCKETS 298 sin->sin_len = sizeof(struct sockaddr_in); 299 # endif 300 s->salen = sizeof(struct sockaddr_in); 301 s->addrlen = sizeof(struct in_addr); 302 sin->sin_family = AF_INET; 303 sin->sin_port = htons ((short) request_port); 304 memcpy(&sin->sin_addr, addr->data, addr->length); 305 } 306 # ifdef IPv6 307 else if (addr->length == 16) /* IPv6 */ 308 { 309 struct sockaddr_in6 *sin6; 310 sin6 = malloc (sizeof(struct sockaddr_in6)); 311 if (sin6 == NULL) { 312 LogOutOfMem("CreateSocklistEntry"); 313 free(s); 314 return NULL; 315 } 316 s->addr = (struct sockaddr *) sin6; 317 318 bzero (sin6, sizeof (struct sockaddr_in6)); 319 # ifdef SIN6_LEN 320 sin6->sin6_len = sizeof(struct sockaddr_in6); 321 # endif 322 s->salen = sizeof(struct sockaddr_in6); 323 s->addrlen = sizeof(struct in6_addr); 324 sin6->sin6_family = AF_INET6; 325 sin6->sin6_port = htons ((short) request_port); 326 memcpy(&sin6->sin6_addr, addr->data, addr->length); 327 } 328 # endif 329 else { 330 /* Unknown address type */ 331 free(s); 332 s = NULL; 333 } 334 return s; 335 } 336 337 static void 338 UpdateListener(ARRAY8Ptr addr, void **closure) 339 { 340 struct socklist *s; 341 342 *closure = NULL; 343 344 if (addr == NULL || addr->length == 0) { 345 ARRAY8 tmpaddr; 346 struct in_addr in; 347 # ifdef IPv6 348 struct in6_addr in6 = in6addr_any; 349 tmpaddr.length = sizeof(in6); 350 tmpaddr.data = (CARD8Ptr) &in6; 351 UpdateListener(&tmpaddr, closure); 352 if (*closure) return; 353 # endif 354 in.s_addr = htonl (INADDR_ANY); 355 tmpaddr.length = sizeof(in); 356 tmpaddr.data = (CARD8Ptr) ∈ 357 UpdateListener(&tmpaddr, closure); 358 return; 359 } 360 361 s = FindInList(listensocks, addr); 362 363 if (s) { 364 *closure = (void *) s; 365 s->ref = 1; 366 return; 367 } 368 369 s = CreateSocklistEntry(addr); 370 371 if (s == NULL) 372 return; 373 374 s->fd = CreateListeningSocket(s->addr, s->salen); 375 if (s->fd < 0) { 376 free(s->addr); 377 free(s); 378 return; 379 } 380 s->ref = 1; 381 s->next = listensocks; 382 listensocks = s; 383 *closure = (void *) s; 384 } 385 386 # define JOIN_MCAST_GROUP 0 387 # define LEAVE_MCAST_GROUP 1 388 389 static void 390 ChangeMcastMembership(struct socklist *s, struct socklist *g, int op) 391 { 392 int sockopt; 393 394 switch (s->addr->sa_family) 395 { 396 case AF_INET: 397 { 398 # ifdef HAVE_INET_NTOP 399 char addrbuf[INET_ADDRSTRLEN] = ""; 400 # endif 401 const char *addrstring; 402 struct ip_mreq mreq; 403 memcpy(&mreq.imr_multiaddr, 404 &((struct sockaddr_in *) g->addr)->sin_addr, 405 sizeof(struct in_addr)); 406 memcpy(&mreq.imr_interface, 407 &((struct sockaddr_in *) s->addr)->sin_addr, 408 sizeof(struct in_addr)); 409 if (op == JOIN_MCAST_GROUP) { 410 sockopt = IP_ADD_MEMBERSHIP; 411 } else { 412 sockopt = IP_DROP_MEMBERSHIP; 413 } 414 # ifdef HAVE_INET_NTOP 415 addrstring = inet_ntop(s->addr->sa_family, 416 &((struct sockaddr_in *) g->addr)->sin_addr, 417 addrbuf, sizeof(addrbuf)); 418 # else 419 addrstring = inet_ntoa(((struct sockaddr_in *) g->addr)->sin_addr); 420 # endif 421 if (setsockopt(s->fd, IPPROTO_IP, sockopt, 422 &mreq, sizeof(mreq)) < 0) { 423 LogError ("XDMCP socket multicast %s to %s failed, errno %d\n", 424 (op == JOIN_MCAST_GROUP) ? "join" : "drop", 425 addrstring, errno); 426 } else if (debugLevel > 0) { 427 Debug ("XDMCP socket multicast %s to %s succeeded\n", 428 (op == JOIN_MCAST_GROUP) ? "join" : "drop", 429 addrstring); 430 } 431 return; 432 } 433 # ifdef IPv6 434 # ifndef IPV6_JOIN_GROUP 435 # define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP 436 # endif 437 # ifndef IPV6_LEAVE_GROUP 438 # define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP 439 # endif 440 case AF_INET6: 441 { 442 struct ipv6_mreq mreq6; 443 memcpy(&mreq6.ipv6mr_multiaddr, 444 &((struct sockaddr_in6 *) g->addr)->sin6_addr, 445 sizeof(struct in6_addr)); 446 mreq6.ipv6mr_interface = 0; /* TODO: fix this */ 447 if (op == JOIN_MCAST_GROUP) { 448 sockopt = IPV6_JOIN_GROUP; 449 } else { 450 sockopt = IPV6_LEAVE_GROUP; 451 } 452 if (setsockopt(s->fd, IPPROTO_IPV6, sockopt, 453 &mreq6, sizeof(mreq6)) < 0) { 454 int saveerr = errno; 455 char addrbuf[INET6_ADDRSTRLEN]; 456 457 inet_ntop(s->addr->sa_family, 458 &((struct sockaddr_in6 *) g->addr)->sin6_addr, 459 addrbuf, sizeof(addrbuf)); 460 461 LogError ("XDMCP socket multicast %s to %s failed, errno %d\n", 462 (op == JOIN_MCAST_GROUP) ? "join" : "drop", addrbuf, 463 saveerr); 464 } else if (debugLevel > 0) { 465 char addrbuf[INET6_ADDRSTRLEN]; 466 467 inet_ntop(s->addr->sa_family, 468 &((struct sockaddr_in6 *) g->addr)->sin6_addr, 469 addrbuf, sizeof(addrbuf)); 470 471 Debug ("XDMCP socket multicast %s to %s succeeded\n", 472 (op == JOIN_MCAST_GROUP) ? "join" : "drop", addrbuf); 473 } 474 return; 475 } 476 # endif 477 } 478 } 479 480 static void 481 UpdateMcastGroup(ARRAY8Ptr addr, void **closure) 482 { 483 struct socklist *s = (struct socklist *) *closure; 484 struct socklist *g; 485 486 if (s == NULL) 487 return; 488 489 g = FindInList(s->mcastgroups, addr); 490 491 if (g) { /* Already in the group, mark & continue */ 492 g->ref = 1; 493 return; 494 } 495 496 /* Need to join the group */ 497 g = CreateSocklistEntry(addr); 498 if (g == NULL) 499 return; 500 501 ChangeMcastMembership(s, g, JOIN_MCAST_GROUP); 502 g->ref = 1; 503 g->next = s->mcastgroups; 504 s->mcastgroups = g; 505 } 506 507 /* Open or close listening sockets to match the current settings read in 508 from the access database. */ 509 void UpdateListenSockets (void) 510 { 511 struct socklist *s, *g, **ls, **lg, *ns, *ng; 512 void *tmpPtr = NULL; 513 514 /* Clear Ref bits - any not marked by UpdateCallback will be closed */ 515 for (s = listensocks; s != NULL; s = s->next) { 516 s->ref = 0; 517 for (g = s->mcastgroups; g != NULL ; g = g->next) { 518 g->ref = 0; 519 } 520 } 521 ForEachListenAddr(UpdateListener, UpdateMcastGroup, &tmpPtr); 522 for (s = listensocks, ls = &listensocks; s != NULL; s = ns) { 523 ns = s->next; 524 if (s->ref == 0) { 525 DestroyListeningSocket(s); 526 *ls = s->next; 527 free(s); 528 } else { 529 ls = &(s->next); 530 for (lg = &s->mcastgroups, g = *lg; g != NULL ; g = ng) { 531 ng = g->next; 532 if (g->ref == 0) { 533 ChangeMcastMembership(s,g,LEAVE_MCAST_GROUP); 534 *lg = g->next; 535 free(g); 536 } else { 537 lg = &(g->next); 538 } 539 } 540 } 541 } 542 } 543 544 /* Close all additional listening sockets beyond the basic chooserFd and 545 remove them from the WellKnownSocketsMask. */ 546 void CloseListenSockets (void) 547 { 548 struct socklist *s, *n; 549 550 for (s = listensocks; s != NULL; s = n) { 551 n = s->next; 552 DestroyListeningSocket(s); 553 free(s); 554 } 555 listensocks = NULL; 556 } 557 558 /* For each listening socket identified in readmask, process the incoming 559 XDMCP request */ 560 void ProcessListenSockets (fd_set *readmask) 561 { 562 struct socklist *s; 563 564 for (s = listensocks; s != NULL; s = s->next) { 565 if (FD_ISSET(s->fd, readmask)) 566 ProcessRequestSocket(s->fd); 567 } 568 } 569 570 #endif /* XDMCP */ 571