/* * CDE - Common Desktop Environment * * Copyright (c) 1993-2012, The Open Group. All rights reserved. * * These libraries and programs are free software; you can * redistribute them and/or modify them under the terms of the GNU * Lesser General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * These libraries and programs are distributed in the hope that * they will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public * License along with these libraries and programs; if not, write * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth * Floor, Boston, MA 02110-1301 USA */ /* $XConsortium: spc-net.c /main/9 1996/11/21 19:53:44 drk $ * File: spc-net.c * Language: C * * (c) Copyright 1989, Hewlett-Packard Company, all rights reserved. * * (c) Copyright 1993, 1994 Hewlett-Packard Company * * (c) Copyright 1993, 1994 International Business Machines Corp. * * (c) Copyright 1993, 1994 Sun Microsystems, Inc. * * (c) Copyright 1993, 1994 Novell, Inc. * */ #define __need_timeval #define __need_fd_set #define __need_all_errors #include /* NOTE: sbport.h must be the first include. */ #include #include #include #define X_INCLUDE_NETDB_H #define XOS_USE_XT_LOCKING #include #include #include #include #include "DtSvcLock.h" extern int SPC_Initialized; /* **** **** Client-side code **** */ /* Variables representing the local machine (initialized only once) */ static struct hostent *official_hp = NULL; XeString official_hostname = NULL; /* * my_gethost will return a copy of the data returned by gethostbyname * */ /*----------------------------------------------------------------------+*/ static struct hostent *my_gethost(XeString hostname) /*----------------------------------------------------------------------+*/ { struct hostent *host_def, *copy; int alias_count, i, addr_count, addrlen; _Xgethostbynameparams host_buf; host_def = _XGethostbyname(hostname, host_buf); if (host_def == NULL) return(FALSE); copy=(struct hostent *)XeMalloc(sizeof(struct hostent)); /* Copy non-pointer info */ memcpy((char *)copy, (char *)host_def, sizeof(struct hostent)); alias_count=0; while(host_def->h_aliases[alias_count++]); copy->h_aliases=(char **)XeMalloc(alias_count*(sizeof(XeString))); addr_count=0; while(host_def->h_addr_list[addr_count++]); copy->h_addr_list=(char **)XeMalloc(addr_count*(sizeof(XeString))); /* Copy Hostname */ copy->h_name=SPC_copy_string(host_def->h_name); /* Copy the host address. We do not use SPC_copy_string here, because the address is not a string, and may have embedded NULLs in it. */ addrlen = host_def->h_length; addr_count -= 1; copy->h_addr_list[addr_count]=XeChar_NULL; for (i=0; i < addr_count; i++) { copy->h_addr_list[i]=(char *)XeMalloc(addrlen); memcpy(copy->h_addr_list[i], host_def->h_addr_list[i], addrlen); } while(alias_count--) { copy->h_aliases[alias_count]=SPC_copy_string(host_def->h_aliases[alias_count]); } return(copy); } /* * SPC_Lookup_Host will try its darndest to return a hostent structure * for the passed hostname. * */ /*----------------------------------------------------------------------+*/ static struct hostent *SPC_Lookup_Host(XeString hostname) /*----------------------------------------------------------------------+*/ { struct hostent *official_def; official_def = my_gethost(hostname); if(!official_def) { SPC_Error(SPC_Unknown_Host, hostname); return(SPC_ERROR); } return(official_def); } /* * * SPC_Init_Local_Host_Info will initialize the local host info. It * is intended to be called only once. * */ /*----------------------------------------------------------------------+*/ Boolean SPC_Init_Local_Host_Info(void) /*----------------------------------------------------------------------+*/ { _DtSvcProcessLock(); official_hostname=(XeString) XeMalloc(MAXHOSTNAMELEN+1); Xegethostname(official_hostname, MAXHOSTNAMELEN); official_hostname[MAXHOSTNAMELEN]=0; if(!official_hp) official_hp = SPC_Lookup_Host(official_hostname); _DtSvcProcessUnlock(); return(official_hp != NULL); } /* * SPC_Local_Hostname takes a string indicating a hostname, and returns * TRUE if it represents a local host, and FALSE if it is remote. */ /*----------------------------------------------------------------------+*/ int SPC_Local_Hostname(XeString hostname) /*----------------------------------------------------------------------+*/ { /* Return TRUE if the specified hostname is local, otherwise it's remote */ /* If no hostname specified, then local by definition */ if (!hostname || !*hostname) return TRUE; #ifdef DEBUG return(FALSE); #endif return(XeIsLocalHostP(hostname)); } /* * * SPC_Open_Connection will a connection pointer to be used for any * subsequent communication to the remote host. It will either return * an already opened connection, or create and initialize a new one. * */ /*----------------------------------------------------------------------+*/ SPC_Connection_Ptr SPC_Open_Connection(XeString hostname) /*----------------------------------------------------------------------+*/ { SPC_Connection_Ptr connection; int seqno; XeString canonical_hostname; int tmp_errorno; /* I was told that XeFindShortHost was the correct routine to use here, but that may change in the future. */ canonical_hostname=XeFindShortHost(hostname); /* check for a currently open connection */ connection=SPC_Lookup_Connection(canonical_hostname); if(connection) { if(connection->connected) { XeFree(canonical_hostname); return(connection); } else { SPC_Close_Connection(connection); connection=NULL; } } /* None currently open. Grab a new one & initialize it. */ if((connection = SPC_Make_Connection(canonical_hostname))==SPC_ERROR) { SPC_Close_Connection(connection); XeFree(canonical_hostname); return(SPC_ERROR); } connection->local=official_hp; if((connection->remote = SPC_Lookup_Host(canonical_hostname)) == SPC_ERROR) { SPC_Close_Connection(connection); XeFree(canonical_hostname); return(SPC_ERROR); } if(SPC_Contact_Server(connection)==SPC_ERROR) { SPC_Close_Connection(connection); XeFree(canonical_hostname); return(SPC_ERROR); } connection->connected=TRUE; if(SPC_Validate_User(canonical_hostname, connection)==SPC_ERROR) { SPC_Close_Connection(connection); XeFree(canonical_hostname); return(SPC_ERROR); } seqno=SPC_Write_Protocol_Request(connection, NULL, ENVIRON_RESET); _DtSvcProcessLock(); tmp_errorno = XeSPCErrorNumber; if(SPC_Waitfor_Reply(connection, NULL, seqno) == SPC_ERROR) { SPC_Close_Connection(connection); /* * XeSPCErrorNumber could have been changed but want to * return the value from Write_Protocol_Request to the * client. */ if (tmp_errorno != 0) XeSPCErrorNumber = tmp_errorno; XeFree(canonical_hostname); _DtSvcProcessUnlock(); return(SPC_ERROR); } _DtSvcProcessUnlock(); /* We no long ever send a RESET_TERMIO request as this was hpux */ /* specific and VERY non-portable. */ if (connection->protocol_version >= 2) { seqno=SPC_Write_Protocol_Request(connection, NULL, RESET_TERMIOS); if(SPC_Waitfor_Reply(connection, NULL, seqno) == SPC_ERROR) { SPC_Close_Connection(connection); XeFree(canonical_hostname); return(SPC_ERROR); } } XeFree(canonical_hostname); return(connection); } /*----------------------------------------------------------------------+*/ int SPC_Open_Socket(SPC_Connection_Ptr conn, int type) /*----------------------------------------------------------------------+*/ { struct servent *service; conn->sid=socket(type, SOCK_STREAM, 0); if(conn->sid == ERROR) { SPC_Error(SPC_Bad_Socket); return(SPC_ERROR); } service=getservbyname(SPC_SERVICE, SPC_PROTOCOL); if (!service) { SPC_Error(SPC_Bad_Service, SPC_SERVICE, SPC_PROTOCOL); return(FALSE); } return(service->s_port); } /* * * SPC_Contact_Server will attempt to contact the server specified by * the passed connection data structure. IT ASSUMES THAT ALL FIELDS * EXCEPT THE SOCKET ID ARE FILLED IN!!! * */ /*----------------------------------------------------------------------+*/ int SPC_Contact_Server(SPC_Connection_Ptr connection) /*----------------------------------------------------------------------+*/ { struct sockaddr_in saddr; short addrtype; struct hostent *remote; /* Check that the connection is initialized correctly */ if(!connection) return(SPC_ERROR); if(!(remote=connection->remote)) return(SPC_ERROR); if(connection->connected) return(TRUE); addrtype=saddr.sin_family=remote->h_addrtype; if(!(saddr.sin_port=SPC_Open_Socket(connection, addrtype))) return(SPC_ERROR); memcpy(&saddr.sin_addr, remote->h_addr, remote->h_length); if(connect(connection->sid, (struct sockaddr *)&saddr, sizeof(saddr)) == ERROR) { XeString shorthost = XeFindShortHost(remote->h_name); SPC_Error(SPC_Bad_Connect, shorthost); XeFree(shorthost); return(SPC_ERROR); } return(TRUE); } /* **** **** Server (daemon) side code **** */ #define BACKLOG 50 #define MAX_SERVER_BIND_ATTEMPTS 30 #define SERVER_PAUSE_INTERVAL 10 /*----------------------------------------------------------------------+*/ SPC_Connection_Ptr SPC_Init_Child(SPC_Connection_Ptr conn, int from) /*----------------------------------------------------------------------+*/ { /* We are the child. Close the connection file descriptor (which is the socket, not our input). */ close(conn->sid); /* Make the from file descriptor correspond to STDIN/STDOUT */ dup2(from, STDIN); close(from); dup2(STDIN, STDOUT); /* make conn point to STDIN */ conn->sid=STDIN; return(conn); } SPC_Connection_Ptr SPC_Standalone_Daemon(SPC_Connection_Ptr conn) { struct sockaddr_in saddr, client_saddr; int len=sizeof(client_saddr); int server_bind_attempts = MAX_SERVER_BIND_ATTEMPTS; int server_bind_pause = SERVER_PAUSE_INTERVAL; int pid, from; #if defined(__aix) int on=1; /* required by setsockopt */ #endif saddr.sin_family=AF_INET; if(!(saddr.sin_port=SPC_Open_Socket(conn, saddr.sin_family))) return(SPC_ERROR); saddr.sin_addr.s_addr=INADDR_ANY; /* Any host address */ /* Reuse the socket address if it is still in a timeout state */ #if defined(__aix) if (setsockopt(conn->sid, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))==ERROR) { #else if (setsockopt(conn->sid, SOL_SOCKET, SO_REUSEADDR, NULL, 0)==ERROR) { #endif SPC_Error(SPC_Bad_Reuse); return(SPC_ERROR); } while (bind(conn->sid, (struct sockaddr *)&saddr, sizeof(saddr)) == ERROR) { if (errno == EADDRINUSE) { SPC_Error(SPC_Bind_Timeout); /* Try to get the connection in a little while */ if (server_bind_attempts > 0) { server_bind_attempts--; sleep(server_bind_pause); } else { /* We don't want to wait forever */ SPC_Error(SPC_Timeout); return(SPC_ERROR); } } else { SPC_Error(SPC_Bad_Bind); return(SPC_ERROR); } } /* Set up a queue for incoming connection requests */ listen(conn->sid, BACKLOG); /* We are running standalone, so we need to loop forever waiting for requests. When we get one, we will fork a child to take care of the processing for us, and then the parent will listen some more */ for(;;) { struct hostent *addr_ret; _Xgethostbynameparams addr_buf; /* Attempt to accept a connection with a client */ from = accept(conn->sid, (struct sockaddr *)&client_saddr, &len); if (from == ERROR) { SPC_Error(SPC_Bad_Accept); return(SPC_ERROR); } addr_ret = _XGethostbyaddr((char *)&client_saddr.sin_addr, sizeof(client_saddr.sin_addr), client_saddr.sin_family, addr_buf); conn->remote = addr_ret; strncpy(conn->hostname, conn->remote->h_name, MAXHOSTNAMELEN); #ifdef DEBUG pid = NULL; #else /* Fork a process to handle I/O from/to client */ pid = fork(); #endif if (pid == ERROR) { SPC_Error(SPC_Cannot_Fork); /* We don't return here, but simply go around for a new try */ } if (!pid) /* We are the child. Do whatever processing we need to do on the connection & return */ return(SPC_Init_Child(conn, from)); /* Otherwise, we are still the parent. Loop around for another connection request */ } } /*----------------------------------------------------------------------+*/ int SPC_Inetd_Daemon(SPC_Connection_Ptr conn) /*----------------------------------------------------------------------+*/ { conn->sid=0; return(TRUE); } /*----------------------------------------------------------------------+*/ SPC_Connection_Ptr SPC_Start_Daemon(int standalone) /*----------------------------------------------------------------------+*/ { SPC_Connection_Ptr connection; /* Do whatever it takes to initialize SPC */ _DtSvcProcessLock(); if (!SPC_Initialized) if(SPC_Initialize()==SPC_ERROR) { _DtSvcProcessUnlock(); return(SPC_ERROR); } _DtSvcProcessUnlock(); /* Get ourselves a connection structure. We don't know the name of the remote client yet, so use the null string as hostname */ if((connection=SPC_Make_Connection(NULL))==SPC_ERROR) return(SPC_ERROR); connection->local=official_hp; if(standalone) { if((SPC_Standalone_Daemon(connection))==SPC_ERROR) return(SPC_ERROR); } else { if((SPC_Inetd_Daemon(connection))==SPC_ERROR) return(SPC_ERROR); } connection->connected=TRUE; return(connection); }