You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1166 lines
35 KiB
1166 lines
35 KiB
/*
|
|
|
|
Copyright 1988, 1998 The Open Group
|
|
Copyright 2002 Sun Microsystems, Inc. All rights reserved.
|
|
Copyright 2001-2004 Oswald Buddenhagen <ossi@kde.org>
|
|
|
|
Permission to use, copy, modify, distribute, and sell this software and its
|
|
documentation for any purpose is hereby granted without fee, provided that
|
|
the above copyright notice appear in all copies and that both that
|
|
copyright notice and this permission notice appear in supporting
|
|
documentation.
|
|
|
|
The above copyright notice and this permission notice shall be included
|
|
in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
Except as contained in this notice, the name of a copyright holder shall
|
|
not be used in advertising or otherwise to promote the sale, use or
|
|
other dealings in this Software without prior written authorization
|
|
from the copyright holder.
|
|
|
|
*/
|
|
|
|
/*
|
|
* xdm - display manager daemon
|
|
* Author: Keith Packard, MIT X Consortium
|
|
*
|
|
* xdmcp.c - Support for XDMCP
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#ifdef XDMCP
|
|
|
|
#include "dm.h"
|
|
#include "dm_error.h"
|
|
#include "dm_auth.h"
|
|
#include "dm_socket.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <ctype.h>
|
|
|
|
#include <netdb.h>
|
|
#if defined(IPv6) && defined(AF_INET6)
|
|
# include <arpa/inet.h>
|
|
#endif
|
|
|
|
/*
|
|
* Forward reference
|
|
*/
|
|
static void broadcast_respond( struct sockaddr *from, int fromlen, int length, int fd );
|
|
static void forward_respond (struct sockaddr *from, int fromlen, int length, int fd);
|
|
static void manage( struct sockaddr *from, int fromlen, int length, int fd );
|
|
static void query_respond( struct sockaddr *from, int fromlen, int length, int fd );
|
|
static void request_respond( struct sockaddr *from, int fromlen, int length, int fd );
|
|
static void send_accept( struct sockaddr *to, int tolen, CARD32 sessionID, ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData, ARRAY8Ptr authorizationName, ARRAY8Ptr authorizationData, int fd );
|
|
static void send_alive( struct sockaddr *from, int fromlen, int length, int fd );
|
|
static void send_decline( struct sockaddr *to, int tolen, ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData, ARRAY8Ptr status, int fd );
|
|
static void send_failed( struct sockaddr *from, int fromlen, const char *name, CARD32 sessionID, const char *reason, int fd );
|
|
static void send_refuse( struct sockaddr *from, int fromlen, CARD32 sessionID, int fd );
|
|
static void send_unwilling( struct sockaddr *from, int fromlen, ARRAY8Ptr authenticationName, ARRAY8Ptr status, int fd );
|
|
static void send_willing( struct sockaddr *from, int fromlen, ARRAY8Ptr authenticationName, ARRAY8Ptr status, int fd );
|
|
|
|
|
|
static XdmcpBuffer buffer;
|
|
|
|
static void
|
|
sendForward( CARD16 connectionType, ARRAY8Ptr address, char *closure )
|
|
{
|
|
#ifdef AF_INET
|
|
struct sockaddr_in in_addr;
|
|
#endif
|
|
#if defined(IPv6) && defined(AF_INET6)
|
|
struct sockaddr_in6 in6_addr;
|
|
#endif
|
|
#ifdef AF_DECnet
|
|
#endif
|
|
struct sockaddr *addr;
|
|
int addrlen;
|
|
|
|
switch (connectionType) {
|
|
#ifdef AF_INET
|
|
case FamilyInternet:
|
|
addr = (struct sockaddr *)&in_addr;
|
|
bzero( (char *)&in_addr, sizeof(in_addr) );
|
|
# ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
|
|
in_addr.sin_len = sizeof(in_addr);
|
|
# endif
|
|
in_addr.sin_family = AF_INET;
|
|
in_addr.sin_port = htons( (short)XDM_UDP_PORT );
|
|
if (address->length != 4)
|
|
return;
|
|
memmove( (char *)&in_addr.sin_addr, address->data, address->length );
|
|
addrlen = sizeof(struct sockaddr_in);
|
|
break;
|
|
#endif
|
|
#if defined(IPv6) && defined(AF_INET6)
|
|
case FamilyInternet6:
|
|
addr = (struct sockaddr *)&in6_addr;
|
|
bzero( (char *)&in6_addr, sizeof(in6_addr) );
|
|
# ifdef SIN6_LEN
|
|
in6_addr.sin6_len = sizeof(in6_addr);
|
|
# endif
|
|
in6_addr.sin6_family = AF_INET6;
|
|
in6_addr.sin6_port = htons( (short)XDM_UDP_PORT );
|
|
if (address->length != 16)
|
|
return;
|
|
memmove( (char *)&in6_addr.sin6_addr, address->data, address->length );
|
|
addrlen = sizeof(struct sockaddr_in6);
|
|
break;
|
|
#endif
|
|
#ifdef AF_DECnet
|
|
case FamilyDECnet:
|
|
#endif
|
|
default:
|
|
return;
|
|
}
|
|
XdmcpFlush( (int)closure, &buffer, (XdmcpNetaddr)addr, addrlen );
|
|
return;
|
|
}
|
|
|
|
static void
|
|
ClientAddress( struct sockaddr *from,
|
|
ARRAY8Ptr addr, /* return */
|
|
ARRAY8Ptr port, /* return */
|
|
CARD16 *type ) /* return */
|
|
{
|
|
int length, family;
|
|
char *data;
|
|
|
|
data = NetaddrPort( (XdmcpNetaddr)from, &length );
|
|
XdmcpAllocARRAY8( port, length );
|
|
memmove( port->data, data, length );
|
|
port->length = length;
|
|
|
|
family = ConvertAddr( (XdmcpNetaddr)from, &length, &data );
|
|
XdmcpAllocARRAY8( addr, length );
|
|
memmove( addr->data, data, length );
|
|
addr->length = length;
|
|
|
|
*type = family;
|
|
}
|
|
|
|
static void
|
|
all_query_respond( struct sockaddr *from, int fromlen,
|
|
ARRAYofARRAY8Ptr authenticationNames,
|
|
xdmOpCode type, int fd )
|
|
{
|
|
ARRAY8Ptr authenticationName;
|
|
ARRAY8 status;
|
|
ARRAY8 addr;
|
|
CARD16 connectionType;
|
|
int family;
|
|
int length;
|
|
|
|
family = ConvertAddr( (XdmcpNetaddr)from, &length, &(addr.data) );
|
|
addr.length = length; /* convert int to short */
|
|
Debug( "all_query_respond: conntype=%d, addr=%02[*:hhx\n",
|
|
family, addr.length, addr.data );
|
|
if (family < 0)
|
|
return;
|
|
connectionType = family;
|
|
|
|
if (type == INDIRECT_QUERY)
|
|
RememberIndirectClient( &addr, connectionType );
|
|
else
|
|
ForgetIndirectClient( &addr, connectionType );
|
|
|
|
authenticationName = ChooseAuthentication( authenticationNames );
|
|
if (Willing( &addr, connectionType, authenticationName, &status, type ))
|
|
send_willing( from, fromlen, authenticationName, &status, fd );
|
|
else
|
|
if (type == QUERY)
|
|
send_unwilling( from, fromlen, authenticationName, &status, fd );
|
|
XdmcpDisposeARRAY8( &status );
|
|
}
|
|
|
|
static void
|
|
indirect_respond( struct sockaddr *from, int fromlen, int length, int fd )
|
|
{
|
|
ARRAYofARRAY8 queryAuthenticationNames;
|
|
ARRAY8 clientAddress;
|
|
ARRAY8 clientPort;
|
|
CARD16 connectionType;
|
|
int expectedLen;
|
|
int i;
|
|
XdmcpHeader header;
|
|
int localHostAsWell;
|
|
|
|
Debug( "<indirect> respond %d\n", length );
|
|
if (!XdmcpReadARRAYofARRAY8( &buffer, &queryAuthenticationNames ))
|
|
return;
|
|
expectedLen = 1;
|
|
for (i = 0; i < (int)queryAuthenticationNames.length; i++)
|
|
expectedLen += 2 + queryAuthenticationNames.data[i].length;
|
|
if (length == expectedLen) {
|
|
ClientAddress( from, &clientAddress, &clientPort, &connectionType );
|
|
/*
|
|
* set up the forward query packet
|
|
*/
|
|
header.version = XDM_PROTOCOL_VERSION;
|
|
header.opcode = (CARD16)FORWARD_QUERY;
|
|
header.length = 0;
|
|
header.length += 2 + clientAddress.length;
|
|
header.length += 2 + clientPort.length;
|
|
header.length += 1;
|
|
for (i = 0; i < (int)queryAuthenticationNames.length; i++)
|
|
header.length += 2 + queryAuthenticationNames.data[i].length;
|
|
XdmcpWriteHeader( &buffer, &header );
|
|
XdmcpWriteARRAY8( &buffer, &clientAddress );
|
|
XdmcpWriteARRAY8( &buffer, &clientPort );
|
|
XdmcpWriteARRAYofARRAY8( &buffer, &queryAuthenticationNames );
|
|
|
|
localHostAsWell =
|
|
ForEachMatchingIndirectHost( &clientAddress, connectionType,
|
|
sendForward, (char *)fd );
|
|
|
|
XdmcpDisposeARRAY8( &clientAddress );
|
|
XdmcpDisposeARRAY8( &clientPort );
|
|
if (localHostAsWell)
|
|
all_query_respond( from, fromlen, &queryAuthenticationNames,
|
|
INDIRECT_QUERY, fd );
|
|
} else
|
|
Debug( "<indirect> length error got %d expect %d\n",
|
|
length, expectedLen );
|
|
XdmcpDisposeARRAYofARRAY8( &queryAuthenticationNames );
|
|
}
|
|
|
|
void
|
|
ProcessRequestSocket( int fd )
|
|
{
|
|
XdmcpHeader header;
|
|
#if defined(IPv6) && defined(AF_INET6)
|
|
struct sockaddr_storage addr;
|
|
#else
|
|
struct sockaddr addr;
|
|
#endif
|
|
int addrlen = sizeof(addr);
|
|
|
|
Debug( "ProcessRequestSocket\n" );
|
|
bzero( (char *)&addr, sizeof(addr) );
|
|
if (!XdmcpFill( fd, &buffer, (XdmcpNetaddr)&addr, &addrlen )) {
|
|
Debug( "XdmcpFill failed\n" );
|
|
return;
|
|
}
|
|
if (!XdmcpReadHeader( &buffer, &header )) {
|
|
Debug( "XdmcpReadHeader failed\n" );
|
|
return;
|
|
}
|
|
if (header.version != XDM_PROTOCOL_VERSION) {
|
|
Debug( "XDMCP header version read was %d, expected %d\n",
|
|
header.version, XDM_PROTOCOL_VERSION );
|
|
return;
|
|
}
|
|
Debug( "header: %d %d %d\n", header.version, header.opcode, header.length );
|
|
switch (header.opcode) {
|
|
case BROADCAST_QUERY:
|
|
broadcast_respond( (struct sockaddr *)&addr, addrlen, header.length, fd );
|
|
break;
|
|
case QUERY:
|
|
query_respond( (struct sockaddr *)&addr, addrlen, header.length, fd );
|
|
break;
|
|
case INDIRECT_QUERY:
|
|
indirect_respond( (struct sockaddr *)&addr, addrlen, header.length, fd );
|
|
break;
|
|
case FORWARD_QUERY:
|
|
forward_respond ((struct sockaddr *)&addr, addrlen, header.length, fd);
|
|
break;
|
|
case REQUEST:
|
|
request_respond( (struct sockaddr *)&addr, addrlen, header.length, fd );
|
|
break;
|
|
case MANAGE:
|
|
manage( (struct sockaddr *)&addr, addrlen, header.length, fd );
|
|
break;
|
|
case KEEPALIVE:
|
|
send_alive( (struct sockaddr *)&addr, addrlen, header.length, fd );
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* respond to a request on the UDP socket.
|
|
*/
|
|
|
|
static void
|
|
direct_query_respond( struct sockaddr *from, int fromlen,
|
|
int length, xdmOpCode type, int fd )
|
|
{
|
|
ARRAYofARRAY8 queryAuthenticationNames;
|
|
int expectedLen;
|
|
int i;
|
|
|
|
if (!XdmcpReadARRAYofARRAY8( &buffer, &queryAuthenticationNames ))
|
|
return;
|
|
expectedLen = 1;
|
|
for (i = 0; i < (int)queryAuthenticationNames.length; i++)
|
|
expectedLen += 2 + queryAuthenticationNames.data[i].length;
|
|
if (length == expectedLen)
|
|
all_query_respond( from, fromlen, &queryAuthenticationNames, type, fd );
|
|
XdmcpDisposeARRAYofARRAY8( &queryAuthenticationNames );
|
|
}
|
|
|
|
static void
|
|
query_respond( struct sockaddr *from, int fromlen, int length, int fd )
|
|
{
|
|
Debug( "<query> respond %d\n", length );
|
|
direct_query_respond( from, fromlen, length, QUERY, fd );
|
|
}
|
|
|
|
static void
|
|
broadcast_respond( struct sockaddr *from, int fromlen, int length, int fd )
|
|
{
|
|
direct_query_respond( from, fromlen, length, BROADCAST_QUERY, fd );
|
|
}
|
|
|
|
/* computes an X display name */
|
|
|
|
static char *
|
|
NetworkAddressToName( CARD16 connectionType, ARRAY8Ptr connectionAddress,
|
|
struct sockaddr *originalAddress, CARD16 displayNumber )
|
|
{
|
|
switch (connectionType) {
|
|
case FamilyInternet:
|
|
#if defined(IPv6) && defined(AF_INET6)
|
|
case FamilyInternet6:
|
|
#endif
|
|
{
|
|
CARD8 *data;
|
|
struct hostent *hostent;
|
|
char *hostname = NULL;
|
|
char *name;
|
|
const char *localhost;
|
|
int multiHomed = 0;
|
|
int type;
|
|
#if defined(IPv6) && defined(AF_INET6)
|
|
struct addrinfo *ai = NULL, *nai, hints;
|
|
char dotted[INET6_ADDRSTRLEN];
|
|
|
|
if (connectionType == FamilyInternet6)
|
|
type = AF_INET6;
|
|
else
|
|
#endif
|
|
type = AF_INET;
|
|
|
|
data = connectionAddress->data;
|
|
hostent = gethostbyaddr( (char *)data,
|
|
connectionAddress->length, type );
|
|
if (hostent) {
|
|
if (sourceAddress) {
|
|
#if defined(IPv6) && defined(AF_INET6)
|
|
bzero( &hints, sizeof(hints) );
|
|
hints.ai_flags = AI_CANONNAME;
|
|
if (!getaddrinfo( hostent->h_name, NULL, &hints, &ai )) {
|
|
hostname = ai->ai_canonname;
|
|
for (nai = ai->ai_next; nai; nai = nai->ai_next)
|
|
if (ai->ai_protocol == nai->ai_protocol &&
|
|
memcmp( ai->ai_addr, nai->ai_addr,
|
|
ai->ai_addrlen ))
|
|
multiHomed = 1;
|
|
}
|
|
#else
|
|
hostent = gethostbyname( hostent->h_name );
|
|
if (hostent && hostent->h_addrtype == AF_INET) {
|
|
multiHomed = hostent->h_addr_list[1] != NULL;
|
|
hostname = hostent->h_name;
|
|
}
|
|
#endif
|
|
} else
|
|
hostname = hostent->h_name;
|
|
}
|
|
|
|
localhost = localHostname();
|
|
|
|
/*
|
|
* protect against bogus host names
|
|
*/
|
|
if (hostname && *hostname && *hostname != '.' && !multiHomed) {
|
|
if (!strcmp( localhost, hostname ))
|
|
ASPrintf( &name, "localhost:%d", displayNumber );
|
|
else {
|
|
if (removeDomainname) {
|
|
char *remoteDot;
|
|
char *localDot;
|
|
|
|
/* check for a common domain name. This
|
|
* could reduce names by recognising common
|
|
* super-domain names as well, but I don't think
|
|
* this is as useful, and will confuse more
|
|
* people
|
|
*/
|
|
if ((localDot = (char*)strchr( localhost, '.' )) &&
|
|
(remoteDot = (char*)strchr( hostname, '.' )))
|
|
{
|
|
/* smash the name in place; it won't
|
|
* be needed later.
|
|
*/
|
|
if (!strcmp( localDot+1, remoteDot+1 ))
|
|
*remoteDot = '\0';
|
|
}
|
|
}
|
|
|
|
ASPrintf( &name, "%s:%d", hostname, displayNumber );
|
|
}
|
|
} else {
|
|
#if defined(IPv6) && defined(AF_INET6)
|
|
if (multiHomed) {
|
|
if (connectionType == FamilyInternet) {
|
|
data = (CARD8 *)
|
|
&((struct sockaddr_in *)originalAddress)->sin_addr;
|
|
} else {
|
|
data = (CARD8 *)
|
|
&((struct sockaddr_in6 *)originalAddress)->sin6_addr;
|
|
}
|
|
}
|
|
inet_ntop( type, data, dotted, sizeof(dotted) );
|
|
ASPrintf( &name, "%s:%d", dotted, displayNumber );
|
|
#else
|
|
if (multiHomed)
|
|
data = (CARD8 *)
|
|
&((struct sockaddr_in *)originalAddress)->sin_addr;
|
|
ASPrintf( &name, "%[4|'.'hhu:%d", data, displayNumber );
|
|
#endif
|
|
}
|
|
#if defined(IPv6) && defined(AF_INET6)
|
|
if (ai)
|
|
freeaddrinfo( ai );
|
|
#endif
|
|
return name;
|
|
}
|
|
#ifdef DNET
|
|
case FamilyDECnet:
|
|
return NULL;
|
|
#endif /* DNET */
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
static void
|
|
forward_respond ( struct sockaddr *from, int fromlen ATTR_UNUSED,
|
|
int length, int fd)
|
|
{
|
|
ARRAY8 clientAddress;
|
|
ARRAY8 clientPort;
|
|
ARRAYofARRAY8 authenticationNames;
|
|
struct sockaddr *client;
|
|
int clientlen;
|
|
int expectedLen;
|
|
int i;
|
|
|
|
Debug( "<forward> respond %d\n", length );
|
|
clientAddress.length = 0;
|
|
clientAddress.data = 0;
|
|
clientPort.length = 0;
|
|
clientPort.data = 0;
|
|
authenticationNames.length = 0;
|
|
authenticationNames.data = 0;
|
|
if (XdmcpReadARRAY8( &buffer, &clientAddress ) &&
|
|
XdmcpReadARRAY8( &buffer, &clientPort ) &&
|
|
XdmcpReadARRAYofARRAY8( &buffer, &authenticationNames ))
|
|
{
|
|
expectedLen = 0;
|
|
expectedLen += 2 + clientAddress.length;
|
|
expectedLen += 2 + clientPort.length;
|
|
expectedLen += 1; /* authenticationNames */
|
|
for (i = 0; i < (int)authenticationNames.length; i++)
|
|
expectedLen += 2 + authenticationNames.data[i].length;
|
|
if (length == expectedLen) {
|
|
int j;
|
|
|
|
j = 0;
|
|
for (i = 0; i < (int)clientPort.length; i++)
|
|
j = j * 256 + clientPort.data[i];
|
|
Debug( "<forward> client address (port %d) %[*hhu\n", j,
|
|
clientAddress.length, clientAddress.data );
|
|
switch (from->sa_family) {
|
|
#ifdef AF_INET
|
|
case AF_INET:
|
|
{
|
|
struct sockaddr_in in_addr;
|
|
|
|
if (clientAddress.length != 4 || clientPort.length != 2)
|
|
goto badAddress;
|
|
bzero( (char *)&in_addr, sizeof(in_addr) );
|
|
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
|
|
in_addr.sin_len = sizeof(in_addr);
|
|
#endif
|
|
in_addr.sin_family = AF_INET;
|
|
memmove( &in_addr.sin_addr, clientAddress.data, 4 );
|
|
memmove( (char *)&in_addr.sin_port, clientPort.data, 2 );
|
|
client = (struct sockaddr *)&in_addr;
|
|
clientlen = sizeof(in_addr);
|
|
all_query_respond( client, clientlen, &authenticationNames,
|
|
FORWARD_QUERY, fd );
|
|
}
|
|
break;
|
|
#endif
|
|
#if defined(IPv6) && defined(AF_INET6)
|
|
case AF_INET6:
|
|
{
|
|
struct sockaddr_in6 in6_addr;
|
|
|
|
if (clientAddress.length != 16 || clientPort.length != 2)
|
|
goto badAddress;
|
|
bzero( (char *)&in6_addr, sizeof(in6_addr) );
|
|
#ifdef SIN6_LEN
|
|
in6_addr.sin6_len = sizeof(in6_addr);
|
|
#endif
|
|
in6_addr.sin6_family = AF_INET6;
|
|
memmove( &in6_addr,clientAddress.data,clientAddress.length );
|
|
memmove( (char *)&in6_addr.sin6_port, clientPort.data, 2 );
|
|
client = (struct sockaddr *)&in6_addr;
|
|
clientlen = sizeof(in6_addr);
|
|
all_query_respond( client, clientlen, &authenticationNames,
|
|
FORWARD_QUERY, fd );
|
|
}
|
|
break;
|
|
#endif
|
|
#ifdef AF_UNIX
|
|
case AF_UNIX:
|
|
{
|
|
struct sockaddr_un un_addr;
|
|
|
|
if (clientAddress.length >= sizeof(un_addr.sun_path))
|
|
goto badAddress;
|
|
bzero( (char *)&un_addr, sizeof(un_addr) );
|
|
un_addr.sun_family = AF_UNIX;
|
|
memmove( un_addr.sun_path, clientAddress.data, clientAddress.length );
|
|
un_addr.sun_path[clientAddress.length] = '\0';
|
|
client = (struct sockaddr *)&un_addr;
|
|
#if defined(HAVE_STRUCT_SOCKADDR_IN_SIN_LEN) && !defined(__Lynx__) && defined(UNIXCONN)
|
|
un_addr.sun_len = strlen( un_addr.sun_path );
|
|
clientlen = SUN_LEN( &un_addr );
|
|
#else
|
|
clientlen = sizeof(un_addr);
|
|
#endif
|
|
all_query_respond( client, clientlen, &authenticationNames,
|
|
FORWARD_QUERY, fd );
|
|
}
|
|
break;
|
|
#endif
|
|
#ifdef AF_CHAOS
|
|
case AF_CHAOS:
|
|
goto badAddress;
|
|
#endif
|
|
#ifdef AF_DECnet
|
|
case AF_DECnet:
|
|
goto badAddress;
|
|
#endif
|
|
}
|
|
} else
|
|
Debug( "<forward> length error got %d expect %d\n", length, expectedLen );
|
|
}
|
|
badAddress:
|
|
XdmcpDisposeARRAY8( &clientAddress );
|
|
XdmcpDisposeARRAY8( &clientPort );
|
|
XdmcpDisposeARRAYofARRAY8( &authenticationNames );
|
|
}
|
|
|
|
static ARRAY8 Hostname;
|
|
|
|
static void
|
|
send_willing( struct sockaddr *from, int fromlen,
|
|
ARRAY8Ptr authenticationName, ARRAY8Ptr status, int fd )
|
|
{
|
|
XdmcpHeader header;
|
|
|
|
Debug( "send <willing> %.*s %.*s\n", authenticationName->length,
|
|
authenticationName->data,
|
|
status->length,
|
|
status->data );
|
|
header.version = XDM_PROTOCOL_VERSION;
|
|
header.opcode = (CARD16)WILLING;
|
|
header.length =
|
|
6 + authenticationName->length + Hostname.length + status->length;
|
|
XdmcpWriteHeader( &buffer, &header );
|
|
XdmcpWriteARRAY8( &buffer, authenticationName );
|
|
XdmcpWriteARRAY8( &buffer, &Hostname );
|
|
XdmcpWriteARRAY8( &buffer, status );
|
|
XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen );
|
|
}
|
|
|
|
static void
|
|
send_unwilling( struct sockaddr *from, int fromlen,
|
|
ARRAY8Ptr authenticationName, ARRAY8Ptr status, int fd )
|
|
{
|
|
XdmcpHeader header;
|
|
|
|
Debug( "send <unwilling> %.*s %.*s\n", authenticationName->length,
|
|
authenticationName->data,
|
|
status->length,
|
|
status->data );
|
|
header.version = XDM_PROTOCOL_VERSION;
|
|
header.opcode = (CARD16)UNWILLING;
|
|
header.length = 4 + Hostname.length + status->length;
|
|
XdmcpWriteHeader( &buffer, &header );
|
|
XdmcpWriteARRAY8( &buffer, &Hostname );
|
|
XdmcpWriteARRAY8( &buffer, status );
|
|
XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen );
|
|
}
|
|
|
|
static unsigned long globalSessionID;
|
|
|
|
#define NextSessionID() (++globalSessionID)
|
|
|
|
void init_session_id( void )
|
|
{
|
|
/* Set randomly so we are unlikely to reuse id's from a previous
|
|
* incarnation so we don't say "Alive" to those displays.
|
|
* Start with low digits 0 to make debugging easier.
|
|
*/
|
|
globalSessionID = (time( (Time_t *)0 ) & 0x7fff) * 16000;
|
|
|
|
Hostname.data = (char *)localHostname();
|
|
Hostname.length = strlen( Hostname.data );
|
|
}
|
|
|
|
static ARRAY8 outOfMemory = { (CARD16)13, (CARD8Ptr)"Out of memory" };
|
|
static ARRAY8 noValidAddr = { (CARD16)16, (CARD8Ptr)"No valid address" };
|
|
static ARRAY8 noValidAuth = { (CARD16)22, (CARD8Ptr)"No valid authorization" };
|
|
static ARRAY8 noAuthentic = { (CARD16)29, (CARD8Ptr)"XDM has no authentication key" };
|
|
|
|
static void
|
|
request_respond( struct sockaddr *from, int fromlen, int length, int fd )
|
|
{
|
|
CARD16 displayNumber;
|
|
ARRAY16 connectionTypes;
|
|
ARRAYofARRAY8 connectionAddresses;
|
|
ARRAY8 authenticationName;
|
|
ARRAY8 authenticationData;
|
|
ARRAYofARRAY8 authorizationNames;
|
|
ARRAY8 manufacturerDisplayID;
|
|
ARRAY8Ptr reason = 0;
|
|
int expectlen;
|
|
int i, j;
|
|
struct protoDisplay *pdpy;
|
|
ARRAY8 authorizationName, authorizationData;
|
|
ARRAY8Ptr connectionAddress;
|
|
|
|
Debug( "<request> respond %d\n", length );
|
|
connectionTypes.data = 0;
|
|
connectionAddresses.data = 0;
|
|
authenticationName.data = 0;
|
|
authenticationData.data = 0;
|
|
authorizationNames.data = 0;
|
|
authorizationName.length = 0;
|
|
authorizationData.length = 0;
|
|
manufacturerDisplayID.data = 0;
|
|
if (XdmcpReadCARD16( &buffer, &displayNumber ) &&
|
|
XdmcpReadARRAY16( &buffer, &connectionTypes ) &&
|
|
XdmcpReadARRAYofARRAY8( &buffer, &connectionAddresses ) &&
|
|
XdmcpReadARRAY8( &buffer, &authenticationName ) &&
|
|
XdmcpReadARRAY8( &buffer, &authenticationData ) &&
|
|
XdmcpReadARRAYofARRAY8( &buffer, &authorizationNames ) &&
|
|
XdmcpReadARRAY8( &buffer, &manufacturerDisplayID ))
|
|
{
|
|
expectlen = 0;
|
|
expectlen += 2; /* displayNumber */
|
|
expectlen += 1 + 2 * connectionTypes.length; /* connectionTypes */
|
|
expectlen += 1; /* connectionAddresses */
|
|
for (i = 0; i < (int)connectionAddresses.length; i++)
|
|
expectlen += 2 + connectionAddresses.data[i].length;
|
|
expectlen += 2 + authenticationName.length; /* authenticationName */
|
|
expectlen += 2 + authenticationData.length; /* authenticationData */
|
|
expectlen += 1; /* authoriationNames */
|
|
for (i = 0; i < (int)authorizationNames.length; i++)
|
|
expectlen += 2 + authorizationNames.data[i].length;
|
|
expectlen += 2 + manufacturerDisplayID.length; /* displayID */
|
|
if (expectlen != length) {
|
|
Debug( "<request> length error got %d expect %d\n",
|
|
length, expectlen );
|
|
goto abort;
|
|
}
|
|
if (connectionTypes.length == 0 ||
|
|
connectionAddresses.length != connectionTypes.length)
|
|
{
|
|
reason = &noValidAddr;
|
|
pdpy = 0;
|
|
goto decline;
|
|
}
|
|
pdpy = FindProtoDisplay( (XdmcpNetaddr)from, fromlen, displayNumber );
|
|
if (!pdpy) {
|
|
|
|
/* Check this Display against the Manager's policy */
|
|
reason = Accept( from, fromlen, displayNumber );
|
|
if (reason)
|
|
goto decline;
|
|
|
|
/* Check the Display's stream services against Manager's policy */
|
|
i = SelectConnectionTypeIndex( &connectionTypes,
|
|
&connectionAddresses );
|
|
if (i < 0) {
|
|
reason = &noValidAddr;
|
|
goto decline;
|
|
}
|
|
|
|
/* The Manager considers this a new session */
|
|
connectionAddress = &connectionAddresses.data[i];
|
|
pdpy = NewProtoDisplay( (XdmcpNetaddr)from, fromlen, displayNumber,
|
|
connectionTypes.data[i], connectionAddress,
|
|
NextSessionID() );
|
|
Debug( "NewProtoDisplay %p\n", pdpy );
|
|
if (!pdpy) {
|
|
reason = &outOfMemory;
|
|
goto decline;
|
|
}
|
|
}
|
|
if (authorizationNames.length == 0)
|
|
j = 0;
|
|
else
|
|
j = SelectAuthorizationTypeIndex( &authenticationName,
|
|
&authorizationNames );
|
|
if (j < 0) {
|
|
reason = &noValidAuth;
|
|
goto decline;
|
|
}
|
|
if (!CheckAuthentication( pdpy,
|
|
&manufacturerDisplayID,
|
|
&authenticationName,
|
|
&authenticationData ))
|
|
{
|
|
reason = &noAuthentic;
|
|
goto decline;
|
|
}
|
|
if (j < (int)authorizationNames.length) {
|
|
Xauth *auth;
|
|
SetProtoDisplayAuthorization( pdpy,
|
|
(unsigned short)authorizationNames.data[j].length,
|
|
(char *)authorizationNames.data[j].data );
|
|
auth = pdpy->xdmcpAuthorization;
|
|
if (!auth)
|
|
auth = pdpy->fileAuthorization;
|
|
if (auth) {
|
|
authorizationName.length = auth->name_length;
|
|
authorizationName.data = (CARD8Ptr) auth->name;
|
|
authorizationData.length = auth->data_length;
|
|
authorizationData.data = (CARD8Ptr) auth->data;
|
|
}
|
|
}
|
|
if (pdpy) {
|
|
send_accept( from, fromlen, pdpy->sessionID,
|
|
&authenticationName,
|
|
&authenticationData,
|
|
&authorizationName,
|
|
&authorizationData, fd );
|
|
} else {
|
|
decline:
|
|
send_decline( from, fromlen, &authenticationName,
|
|
&authenticationData,
|
|
reason, fd );
|
|
if (pdpy)
|
|
DisposeProtoDisplay( pdpy );
|
|
}
|
|
}
|
|
abort:
|
|
XdmcpDisposeARRAY16( &connectionTypes );
|
|
XdmcpDisposeARRAYofARRAY8( &connectionAddresses );
|
|
XdmcpDisposeARRAY8( &authenticationName );
|
|
XdmcpDisposeARRAY8( &authenticationData );
|
|
XdmcpDisposeARRAYofARRAY8( &authorizationNames );
|
|
XdmcpDisposeARRAY8( &manufacturerDisplayID );
|
|
}
|
|
|
|
static void
|
|
send_accept( struct sockaddr *to, int tolen, CARD32 sessionID,
|
|
ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData,
|
|
ARRAY8Ptr authorizationName, ARRAY8Ptr authorizationData,
|
|
int fd )
|
|
{
|
|
XdmcpHeader header;
|
|
|
|
Debug( "<accept> session ID %ld\n", (long)sessionID );
|
|
header.version = XDM_PROTOCOL_VERSION;
|
|
header.opcode = (CARD16)ACCEPT;
|
|
header.length = 4; /* session ID */
|
|
header.length += 2 + authenticationName->length;
|
|
header.length += 2 + authenticationData->length;
|
|
header.length += 2 + authorizationName->length;
|
|
header.length += 2 + authorizationData->length;
|
|
XdmcpWriteHeader( &buffer, &header );
|
|
XdmcpWriteCARD32( &buffer, sessionID );
|
|
XdmcpWriteARRAY8( &buffer, authenticationName );
|
|
XdmcpWriteARRAY8( &buffer, authenticationData );
|
|
XdmcpWriteARRAY8( &buffer, authorizationName );
|
|
XdmcpWriteARRAY8( &buffer, authorizationData );
|
|
XdmcpFlush( fd, &buffer, (XdmcpNetaddr)to, tolen );
|
|
}
|
|
|
|
static void
|
|
send_decline( struct sockaddr *to, int tolen,
|
|
ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData,
|
|
ARRAY8Ptr status, int fd )
|
|
{
|
|
XdmcpHeader header;
|
|
|
|
Debug( "<decline> %.*s\n", status->length, status->data );
|
|
header.version = XDM_PROTOCOL_VERSION;
|
|
header.opcode = (CARD16)DECLINE;
|
|
header.length = 0;
|
|
header.length += 2 + status->length;
|
|
header.length += 2 + authenticationName->length;
|
|
header.length += 2 + authenticationData->length;
|
|
XdmcpWriteHeader( &buffer, &header );
|
|
XdmcpWriteARRAY8( &buffer, status );
|
|
XdmcpWriteARRAY8( &buffer, authenticationName );
|
|
XdmcpWriteARRAY8( &buffer, authenticationData );
|
|
XdmcpFlush( fd, &buffer, (XdmcpNetaddr)to, tolen );
|
|
}
|
|
|
|
static void
|
|
manage( struct sockaddr *from, int fromlen, int length, int fd )
|
|
{
|
|
CARD32 sessionID;
|
|
CARD16 displayNumber;
|
|
ARRAY8 displayClass;
|
|
int expectlen;
|
|
struct protoDisplay *pdpy;
|
|
struct display *d;
|
|
char *name = NULL;
|
|
char *class2 = NULL;
|
|
XdmcpNetaddr from_save;
|
|
ARRAY8 clientAddress, clientPort;
|
|
CARD16 connectionType;
|
|
|
|
Debug( "<manage> %d\n", length );
|
|
displayClass.data = 0;
|
|
displayClass.length = 0;
|
|
if (XdmcpReadCARD32( &buffer, &sessionID ) &&
|
|
XdmcpReadCARD16( &buffer, &displayNumber ) &&
|
|
XdmcpReadARRAY8( &buffer, &displayClass ))
|
|
{
|
|
expectlen = 4 + /* session ID */
|
|
2 + /* displayNumber */
|
|
2 + displayClass.length; /* displayClass */
|
|
if (expectlen != length) {
|
|
Debug( "<manage> length error got %d expect %d\n", length, expectlen );
|
|
goto abort;
|
|
}
|
|
pdpy = FindProtoDisplay( (XdmcpNetaddr)from, fromlen, displayNumber );
|
|
Debug( "<manage> session ID %ld, pdpy %p\n", (long)sessionID, pdpy );
|
|
if (!pdpy || pdpy->sessionID != sessionID) {
|
|
/*
|
|
* We may have already started a session for this display
|
|
* but it hasn't seen the response in the form of an
|
|
* XOpenDisplay() yet. So check if it is in the list of active
|
|
* displays, and if so check that the session id's match.
|
|
* If all this is true, then we have a duplicate request that
|
|
* can be ignored.
|
|
*/
|
|
if (!pdpy &&
|
|
(d = FindDisplayByAddress( (XdmcpNetaddr)from, fromlen,
|
|
displayNumber )) &&
|
|
d->sessionID == sessionID)
|
|
{
|
|
Debug( "manage: got duplicate pkt, ignoring\n" );
|
|
goto abort;
|
|
}
|
|
Debug( "session ID %ld refused\n", (long)sessionID );
|
|
if (pdpy)
|
|
Debug( "existing session ID %ld\n", (long)pdpy->sessionID );
|
|
send_refuse( from, fromlen, sessionID, fd );
|
|
} else {
|
|
name = NetworkAddressToName( pdpy->connectionType,
|
|
&pdpy->connectionAddress,
|
|
from,
|
|
pdpy->displayNumber );
|
|
if (!name) {
|
|
Debug( "could not compute display name\n" );
|
|
send_failed( from, fromlen, "(no name)", sessionID,
|
|
"out of memory", fd );
|
|
goto abort;
|
|
}
|
|
Debug( "computed display name: %s\n", name );
|
|
if ((d = FindDisplayByName( name ))) {
|
|
Debug( "terminating active session for %s\n", d->name );
|
|
StopDisplay( d );
|
|
}
|
|
if (displayClass.length) {
|
|
if (!StrNDup( &class2, (char *)displayClass.data,
|
|
displayClass.length ))
|
|
{
|
|
send_failed( from, fromlen, name, sessionID,
|
|
"out of memory", fd );
|
|
goto abort;
|
|
}
|
|
}
|
|
if (!(from_save = (XdmcpNetaddr)Malloc( fromlen ))) {
|
|
send_failed( from, fromlen, name, sessionID,
|
|
"out of memory", fd );
|
|
goto abort;
|
|
}
|
|
memmove( from_save, from, fromlen );
|
|
if (!(d = NewDisplay( name ))) {
|
|
free( (char *)from_save );
|
|
send_failed( from, fromlen, name, sessionID,
|
|
"out of memory", fd );
|
|
goto abort;
|
|
}
|
|
d->class2 = class2;
|
|
class2 = 0;
|
|
d->displayType = dForeign | dTransient | dFromXDMCP;
|
|
d->sessionID = pdpy->sessionID;
|
|
d->from.data = (unsigned char *)from_save;
|
|
d->from.length = fromlen;
|
|
d->displayNumber = pdpy->displayNumber;
|
|
ClientAddress( from, &clientAddress, &clientPort,
|
|
&connectionType );
|
|
d->useChooser = 0;
|
|
d->xdmcpFd = fd;
|
|
if (IsIndirectClient( &clientAddress, connectionType )) {
|
|
Debug( "IsIndirectClient\n" );
|
|
ForgetIndirectClient( &clientAddress, connectionType );
|
|
if (UseChooser( &clientAddress, connectionType )) {
|
|
d->useChooser = 1;
|
|
Debug( "use chooser for %s\n", d->name );
|
|
}
|
|
}
|
|
d->clientAddr = clientAddress;
|
|
d->connectionType = connectionType;
|
|
d->remoteHost = NetworkAddressToHostname (pdpy->connectionType,
|
|
&pdpy->connectionAddress);
|
|
|
|
XdmcpDisposeARRAY8( &clientPort );
|
|
if (pdpy->fileAuthorization) {
|
|
d->authorizations = (Xauth **)Malloc( sizeof(Xauth *) );
|
|
if (!d->authorizations) {
|
|
free( (char *)from_save );
|
|
free( (char *)d );
|
|
send_failed( from, fromlen, name, sessionID,
|
|
"out of memory", fd );
|
|
goto abort;
|
|
}
|
|
d->authorizations[0] = pdpy->fileAuthorization;
|
|
d->authNum = 1;
|
|
pdpy->fileAuthorization = 0;
|
|
}
|
|
DisposeProtoDisplay( pdpy );
|
|
Debug( "starting display %s,%s\n", d->name, d->class2 );
|
|
if (LoadDisplayResources( d ) < 0) {
|
|
LogError( "Unable to read configuration for display %s; "
|
|
"stopping it.\n", d->name );
|
|
StopDisplay( d );
|
|
} else
|
|
StartDisplay( d );
|
|
CloseGetter();
|
|
}
|
|
}
|
|
abort:
|
|
XdmcpDisposeARRAY8( &displayClass );
|
|
if (name)
|
|
free( (char *)name );
|
|
if (class2)
|
|
free( (char *)class2 );
|
|
}
|
|
|
|
void
|
|
SendFailed( struct display *d, const char *reason )
|
|
{
|
|
Debug( "display start failed, sending <failed>\n" );
|
|
send_failed( (struct sockaddr *)(d->from.data), d->from.length, d->name,
|
|
d->sessionID, reason, d->xdmcpFd );
|
|
}
|
|
|
|
static void
|
|
send_failed( struct sockaddr *from, int fromlen,
|
|
const char *name, CARD32 sessionID, const char *reason, int fd )
|
|
{
|
|
char buf[360];
|
|
XdmcpHeader header;
|
|
ARRAY8 status;
|
|
|
|
sprintf( buf, "Session %ld failed for display %.260s: %s",
|
|
(long)sessionID, name, reason );
|
|
Debug( "send_failed(%\"s)\n", buf );
|
|
status.length = strlen( buf );
|
|
status.data = (CARD8Ptr) buf;
|
|
header.version = XDM_PROTOCOL_VERSION;
|
|
header.opcode = (CARD16)FAILED;
|
|
header.length = 6 + status.length;
|
|
XdmcpWriteHeader( &buffer, &header );
|
|
XdmcpWriteCARD32( &buffer, sessionID );
|
|
XdmcpWriteARRAY8( &buffer, &status );
|
|
XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen );
|
|
}
|
|
|
|
static void
|
|
send_refuse( struct sockaddr *from, int fromlen, CARD32 sessionID, int fd )
|
|
{
|
|
XdmcpHeader header;
|
|
|
|
Debug( "send <refuse> %ld\n", (long)sessionID );
|
|
header.version = XDM_PROTOCOL_VERSION;
|
|
header.opcode = (CARD16)REFUSE;
|
|
header.length = 4;
|
|
XdmcpWriteHeader( &buffer, &header );
|
|
XdmcpWriteCARD32( &buffer, sessionID );
|
|
XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen );
|
|
}
|
|
|
|
static void
|
|
send_alive( struct sockaddr *from, int fromlen, int length, int fd )
|
|
{
|
|
CARD32 sessionID;
|
|
CARD16 displayNumber;
|
|
struct display *d;
|
|
XdmcpHeader header;
|
|
CARD8 sendRunning;
|
|
CARD32 sendSessionID;
|
|
|
|
Debug( "send <alive>\n" );
|
|
if (XdmcpReadCARD16( &buffer, &displayNumber ) &&
|
|
XdmcpReadCARD32( &buffer, &sessionID ))
|
|
{
|
|
if (length == 6) {
|
|
if (!(d = FindDisplayBySessionID( sessionID )))
|
|
d = FindDisplayByAddress( (XdmcpNetaddr)from, fromlen,
|
|
displayNumber );
|
|
sendRunning = 0;
|
|
sendSessionID = 0;
|
|
if (d && d->status == running) {
|
|
if (d->sessionID == sessionID)
|
|
sendRunning = 1;
|
|
sendSessionID = d->sessionID;
|
|
}
|
|
header.version = XDM_PROTOCOL_VERSION;
|
|
header.opcode = (CARD16)ALIVE;
|
|
header.length = 5;
|
|
Debug( "<alive>: %d %ld\n", sendRunning, (long)sendSessionID );
|
|
XdmcpWriteHeader( &buffer, &header );
|
|
XdmcpWriteCARD8( &buffer, sendRunning );
|
|
XdmcpWriteCARD32( &buffer, sendSessionID );
|
|
XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen );
|
|
}
|
|
}
|
|
}
|
|
|
|
char *
|
|
NetworkAddressToHostname( CARD16 connectionType, ARRAY8Ptr connectionAddress )
|
|
{
|
|
switch (connectionType) {
|
|
case FamilyInternet:
|
|
#if defined(IPv6) && defined(AF_INET6)
|
|
case FamilyInternet6:
|
|
#endif
|
|
{
|
|
struct hostent *he;
|
|
char *name, *lname;
|
|
char *myDot;
|
|
int af_type;
|
|
#if defined(IPv6) && defined(AF_INET6)
|
|
char dotted[INET6_ADDRSTRLEN];
|
|
|
|
if (connectionType == FamilyInternet6)
|
|
af_type = AF_INET6;
|
|
else
|
|
#endif
|
|
af_type = AF_INET;
|
|
|
|
he = gethostbyaddr( (char *)connectionAddress->data,
|
|
connectionAddress->length, af_type );
|
|
if (he) {
|
|
#if defined(IPv6) && defined(AF_INET6)
|
|
struct addrinfo *ai, *nai;
|
|
if (!getaddrinfo( he->h_name, NULL, NULL, &ai )) {
|
|
for (nai = ai; nai; nai = nai->ai_next) {
|
|
if (af_type == nai->ai_family &&
|
|
!memcmp( nai->ai_family == AF_INET ?
|
|
(char *)&((struct sockaddr_in *)nai->ai_addr)->sin_addr :
|
|
(char *)&((struct sockaddr_in6 *)nai->ai_addr)->sin6_addr,
|
|
connectionAddress->data,
|
|
connectionAddress->length ))
|
|
{
|
|
freeaddrinfo( ai );
|
|
goto oki;
|
|
}
|
|
}
|
|
freeaddrinfo( ai );
|
|
#else
|
|
if ((he = gethostbyname( he->h_name )) &&
|
|
he->h_addrtype == AF_INET)
|
|
{
|
|
int i;
|
|
for (i = 0; he->h_addr_list[i]; i++)
|
|
if (!memcmp( he->h_addr_list[i],
|
|
connectionAddress->data, 4 ))
|
|
goto oki;
|
|
#endif
|
|
LogError( "DNS spoof attempt or misconfigured resolver.\n" );
|
|
}
|
|
goto gotnone;
|
|
oki:
|
|
if (StrDup( &name, he->h_name ) &&
|
|
!strchr( name, '.' ) &&
|
|
(myDot = (char*)strchr( localHostname(), '.' )))
|
|
{
|
|
if (ASPrintf( &lname, "%s%s", name, myDot )) {
|
|
#if defined(IPv6) && defined(AF_INET6)
|
|
if (!getaddrinfo( lname, NULL, NULL, &ai )) {
|
|
for (nai = ai; nai; nai = nai->ai_next) {
|
|
if (af_type == nai->ai_family &&
|
|
!memcmp( nai->ai_family == AF_INET ?
|
|
(char *)&((struct sockaddr_in *)nai->ai_addr)->sin_addr :
|
|
(char *)&((struct sockaddr_in6 *)nai->ai_addr)->sin6_addr,
|
|
connectionAddress->data,
|
|
connectionAddress->length ))
|
|
{
|
|
freeaddrinfo( ai );
|
|
free( name );
|
|
return lname;
|
|
}
|
|
}
|
|
freeaddrinfo( ai );
|
|
}
|
|
#else
|
|
if ((he = gethostbyname( lname )) &&
|
|
he->h_addrtype == AF_INET)
|
|
{
|
|
int i;
|
|
for (i = 0; he->h_addr_list[i]; i++)
|
|
if (!memcmp( he->h_addr_list[i],
|
|
connectionAddress->data, 4 ))
|
|
{
|
|
free( name );
|
|
return lname;
|
|
}
|
|
}
|
|
#endif
|
|
free( lname );
|
|
}
|
|
}
|
|
} else {
|
|
gotnone:
|
|
/* can't get name, so use emergency fallback */
|
|
#if defined(IPv6) && defined(AF_INET6)
|
|
inet_ntop( af_type, connectionAddress->data,
|
|
dotted, sizeof(dotted) );
|
|
StrDup( &name, dotted );
|
|
#else
|
|
ASPrintf( &name, "%[4|'.'hhu", connectionAddress->data );
|
|
#endif
|
|
LogWarn( "Cannot convert Internet address %s to host name\n",
|
|
name );
|
|
}
|
|
return name;
|
|
}
|
|
#ifdef DNET
|
|
case FamilyDECnet:
|
|
break;
|
|
#endif /* DNET */
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#endif /* XDMCP */
|
|
|