Home | History | Annotate | Line # | Download | only in xdm
      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) &in;
    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