Kill lots of warnings in DtSvc.
[oweals/cde.git] / cde / lib / DtSvc / DtEncap / spc-net.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $XConsortium: spc-net.c /main/9 1996/11/21 19:53:44 drk $
24  * File:         spc-net.c 
25  * Language:     C
26  *
27  * (c) Copyright 1989, Hewlett-Packard Company, all rights reserved.
28  *
29  * (c) Copyright 1993, 1994 Hewlett-Packard Company                     *
30  * (c) Copyright 1993, 1994 International Business Machines Corp.       *
31  * (c) Copyright 1993, 1994 Sun Microsystems, Inc.                      *
32  * (c) Copyright 1993, 1994 Novell, Inc.                                *
33  */
34
35 #define  __need_timeval
36 #define  __need_fd_set
37 #define  __need_all_errors
38
39 #include <bms/sbport.h> /* NOTE: sbport.h must be the first include. */
40 #include <errno.h>
41 #include <sys/socket.h>
42 #include <netinet/in.h>
43
44 #define X_INCLUDE_NETDB_H
45 #define XOS_USE_XT_LOCKING
46 #include <X11/Xos_r.h>
47
48 #include <SPC/spcP.h>
49 #include <bms/MemoryMgr.h>
50 #include <SPC/spc-proto.h>
51 #include "DtSvcLock.h"
52
53 extern int SPC_Initialized;
54
55 /*
56  ****
57  **** Client-side code
58  ****
59 */
60
61 /* Variables representing the local machine (initialized only once) */
62
63 static struct hostent *official_hp = NULL;
64 XeString official_hostname = NULL;
65
66 /*
67  * my_gethost will return a copy of the data returned by gethostbyname
68  *
69 */
70
71 /*----------------------------------------------------------------------+*/
72 static struct hostent *my_gethost(XeString hostname)
73 /*----------------------------------------------------------------------+*/
74 {
75   struct hostent *host_def, *copy;
76   int alias_count, i, addr_count, addrlen;
77   _Xgethostbynameparams host_buf;
78
79   host_def = _XGethostbyname(hostname, host_buf);
80   if (host_def == NULL)
81     return(FALSE);
82   
83   copy=(struct hostent *)XeMalloc(sizeof(struct hostent));
84   
85   /* Copy non-pointer info */
86   memcpy((char *)copy, (char *)host_def, sizeof(struct hostent));
87   
88   alias_count=0;
89
90   while(host_def->h_aliases[alias_count++]);
91
92   copy->h_aliases=(char **)XeMalloc(alias_count*(sizeof(XeString)));
93   
94   addr_count=0;
95   while(host_def->h_addr_list[addr_count++]);
96
97   copy->h_addr_list=(char **)XeMalloc(addr_count*(sizeof(XeString)));
98   
99   /* Copy Hostname */
100
101   copy->h_name=SPC_copy_string(host_def->h_name);
102
103   /* Copy the host address.  We do not use SPC_copy_string here,
104      because the address is not a string, and may have embedded
105      NULLs in it. */
106   
107   addrlen = host_def->h_length;
108   addr_count -= 1;
109   copy->h_addr_list[addr_count]=XeChar_NULL;
110   for (i=0; i < addr_count; i++) {
111     copy->h_addr_list[i]=(char *)XeMalloc(addrlen);
112     memcpy(copy->h_addr_list[i], host_def->h_addr_list[i], addrlen);
113   }
114   
115   while(alias_count--) {
116     copy->h_aliases[alias_count]=SPC_copy_string(host_def->h_aliases[alias_count]);
117   }
118
119   return(copy);
120 }
121                                  
122 /*
123  * SPC_Lookup_Host will try its darndest to return a hostent structure
124  * for the passed hostname.
125  *
126 */
127
128 /*----------------------------------------------------------------------+*/
129 static struct hostent *SPC_Lookup_Host(XeString hostname)
130 /*----------------------------------------------------------------------+*/
131 {
132
133   struct hostent *official_def;
134   
135   official_def = my_gethost(hostname);
136   if(!official_def) {
137     SPC_Error(SPC_Unknown_Host, hostname);
138     return(SPC_ERROR);
139   }
140
141   return(official_def);
142   
143 }
144
145 /*
146  *
147  * SPC_Init_Local_Host_Info will initialize the local host info.  It
148  * is intended to be called only once.
149  *
150 */
151
152 /*----------------------------------------------------------------------+*/
153 Boolean SPC_Init_Local_Host_Info(void)
154 /*----------------------------------------------------------------------+*/
155 {
156   _DtSvcProcessLock();
157   official_hostname=(XeString) XeMalloc(MAXHOSTNAMELEN+1);
158   
159   Xegethostname(official_hostname, MAXHOSTNAMELEN);
160   official_hostname[MAXHOSTNAMELEN]=0;
161   
162   if(!official_hp)
163     official_hp = SPC_Lookup_Host(official_hostname);
164
165   _DtSvcProcessUnlock();
166   return(official_hp != NULL);
167 }
168
169 /*
170  * SPC_Local_Hostname takes a string indicating a hostname, and returns
171  * TRUE if it represents a local host, and FALSE if it is remote.
172  */
173
174 /*----------------------------------------------------------------------+*/
175 int
176 SPC_Local_Hostname(XeString hostname)
177 /*----------------------------------------------------------------------+*/
178 {
179   /* Return TRUE if the specified hostname is local, otherwise it's remote */
180
181   /* If no hostname specified, then local by definition */
182
183   if (!hostname || !*hostname) return TRUE;
184
185 #ifdef DEBUG  
186   return(FALSE);
187 #endif
188
189   return(XeIsLocalHostP(hostname));
190 }
191
192 /*
193  *
194  * SPC_Open_Connection will a connection pointer to be used for any
195  * subsequent communication to the remote host.  It will either return
196  * an already opened connection, or create and initialize a new one.
197  *
198 */
199
200 /*----------------------------------------------------------------------+*/
201 SPC_Connection_Ptr SPC_Open_Connection(XeString hostname)
202 /*----------------------------------------------------------------------+*/
203 {
204   SPC_Connection_Ptr connection;
205   int seqno;
206   XeString canonical_hostname;
207   int tmp_errorno;
208
209   /* I was told that XeFindShortHost was the correct routine to use here,
210      but that may change in the future. */
211
212   canonical_hostname=XeFindShortHost(hostname);
213   
214   /* check for a currently open connection */
215   connection=SPC_Lookup_Connection(canonical_hostname);
216   if(connection) {
217     if(connection->connected) {
218       XeFree(canonical_hostname);
219       return(connection);
220     }
221     else {
222       SPC_Close_Connection(connection);
223       connection=NULL;
224     }
225   }
226       
227   /* None currently open.  Grab a new one & initialize it. */
228
229   if((connection = SPC_Make_Connection(canonical_hostname))==SPC_ERROR) {
230     SPC_Close_Connection(connection);
231     XeFree(canonical_hostname);
232     return(SPC_ERROR);
233   }
234   connection->local=official_hp;
235   if((connection->remote = SPC_Lookup_Host(canonical_hostname)) == SPC_ERROR) {
236     SPC_Close_Connection(connection);
237     XeFree(canonical_hostname);
238     return(SPC_ERROR);
239   }
240   
241   if(SPC_Contact_Server(connection)==SPC_ERROR) {
242     SPC_Close_Connection(connection);
243     XeFree(canonical_hostname);
244     return(SPC_ERROR);
245   }
246
247   connection->connected=TRUE;
248   
249   if(SPC_Validate_User(canonical_hostname, connection)==SPC_ERROR) {
250     SPC_Close_Connection(connection);
251     XeFree(canonical_hostname);
252     return(SPC_ERROR);
253   }
254
255   seqno=SPC_Write_Protocol_Request(connection, NULL, ENVIRON_RESET);
256   _DtSvcProcessLock();
257   tmp_errorno = XeSPCErrorNumber;
258   if(SPC_Waitfor_Reply(connection, NULL, seqno) == SPC_ERROR) {
259     SPC_Close_Connection(connection);
260     /*
261      * XeSPCErrorNumber could have been changed but want to
262      * return the value from Write_Protocol_Request to the 
263      * client.
264      */
265     if (tmp_errorno != 0)
266       XeSPCErrorNumber = tmp_errorno;
267     XeFree(canonical_hostname);
268     _DtSvcProcessUnlock();
269     return(SPC_ERROR);
270   }
271   _DtSvcProcessUnlock();
272
273   /* We no long ever send a RESET_TERMIO request as this was hpux */
274   /* specific and VERY non-portable.                              */
275
276   if (connection->protocol_version >= 2) {
277       seqno=SPC_Write_Protocol_Request(connection, NULL, RESET_TERMIOS);
278       if(SPC_Waitfor_Reply(connection, NULL, seqno) == SPC_ERROR) {
279           SPC_Close_Connection(connection);
280           XeFree(canonical_hostname);
281           return(SPC_ERROR);
282       }
283   }
284   
285   XeFree(canonical_hostname);
286   return(connection);
287   
288 }
289
290 /*----------------------------------------------------------------------+*/
291 int
292 SPC_Open_Socket(SPC_Connection_Ptr conn,
293                 int type)
294 /*----------------------------------------------------------------------+*/
295 {
296
297   struct servent *service;
298   
299   conn->sid=socket(type, SOCK_STREAM, 0);
300   if(conn->sid == ERROR) {
301     SPC_Error(SPC_Bad_Socket);
302     return(SPC_ERROR);
303   }
304   
305   service=getservbyname(SPC_SERVICE, SPC_PROTOCOL);
306   if (!service) {
307     SPC_Error(SPC_Bad_Service, SPC_SERVICE, SPC_PROTOCOL);
308     return(FALSE);
309   }
310
311   return(service->s_port);
312 }
313
314
315 /*
316  *
317  * SPC_Contact_Server will attempt to contact the server specified by
318  * the passed connection data structure.  IT ASSUMES THAT ALL FIELDS
319  * EXCEPT THE SOCKET ID ARE FILLED IN!!!
320  *
321 */
322
323 /*----------------------------------------------------------------------+*/
324 int
325 SPC_Contact_Server(SPC_Connection_Ptr connection)
326 /*----------------------------------------------------------------------+*/
327 {
328   struct sockaddr_in saddr;
329   short addrtype;
330   struct hostent *remote;
331   
332   
333   /* Check that the connection is initialized correctly */
334   if(!connection)
335     return(SPC_ERROR);
336   if(!(remote=connection->remote))
337     return(SPC_ERROR);
338   if(connection->connected)
339     return(TRUE);
340   
341   addrtype=saddr.sin_family=remote->h_addrtype;
342   if(!(saddr.sin_port=SPC_Open_Socket(connection, addrtype)))
343     return(SPC_ERROR);
344   memcpy(&saddr.sin_addr, remote->h_addr, remote->h_length);
345
346   if(connect(connection->sid, (struct sockaddr *)&saddr, sizeof(saddr)) == ERROR) {
347     SPC_Error(SPC_Bad_Connect,
348               XeFindShortHost(remote->h_name));
349     return(SPC_ERROR);
350   }
351
352   return(TRUE);
353 }
354
355 /*
356  ****
357  **** Server (daemon) side code
358  ****
359 */ 
360
361 #define BACKLOG                  50
362 #define MAX_SERVER_BIND_ATTEMPTS 30
363 #define SERVER_PAUSE_INTERVAL    10
364
365 /*----------------------------------------------------------------------+*/
366 SPC_Connection_Ptr SPC_Init_Child(SPC_Connection_Ptr conn,
367                                   int from)
368 /*----------------------------------------------------------------------+*/
369 {
370   /* We are the child.  Close the connection file descriptor
371      (which is the socket, not our input). */
372   close(conn->sid);
373
374   /* Make the from file descriptor correspond to STDIN/STDOUT */
375   dup2(from, STDIN);
376   close(from);
377   dup2(STDIN, STDOUT);
378
379   /* make conn point to STDIN */
380   
381   conn->sid=STDIN;
382
383   return(conn);
384 }
385
386 SPC_Connection_Ptr SPC_Standalone_Daemon(SPC_Connection_Ptr conn)
387 {
388   struct sockaddr_in saddr, client_saddr;
389 #ifdef USL
390   /* Only UnixWare 2.02 uses the Spec1170 parameter profile for accept(). */
391   size_t len=sizeof(client_saddr);
392 #else
393   int len=sizeof(client_saddr);
394 #endif
395   int server_bind_attempts      = MAX_SERVER_BIND_ATTEMPTS;
396   int server_bind_pause         = SERVER_PAUSE_INTERVAL;
397   int pid, from;
398 #if defined(__aix)
399   int on=1;                               /* required by setsockopt */
400 #endif
401
402   saddr.sin_family=AF_INET;
403   if(!(saddr.sin_port=SPC_Open_Socket(conn, saddr.sin_family)))
404     return(SPC_ERROR);
405   saddr.sin_addr.s_addr=INADDR_ANY; /* Any host address */
406
407   /* Reuse the socket address if it is still in a timeout state */
408
409 #if defined(__aix)
410   if (setsockopt(conn->sid, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))==ERROR) {
411 #else
412   if (setsockopt(conn->sid, SOL_SOCKET, SO_REUSEADDR, NULL, 0)==ERROR) {
413 #endif
414     SPC_Error(SPC_Bad_Reuse);
415     return(SPC_ERROR);
416   }
417
418   while (bind(conn->sid, (struct sockaddr *)&saddr, sizeof(saddr)) == ERROR) {
419     if (errno == EADDRINUSE) {
420       SPC_Error(SPC_Bind_Timeout);
421       /* Try to get the connection in a little while */
422       if (server_bind_attempts > 0) {
423         server_bind_attempts--;
424         sleep(server_bind_pause);
425       }
426       else {
427         /* We don't want to wait forever */
428         SPC_Error(SPC_Timeout);
429         return(SPC_ERROR);
430       }
431     } else {
432       SPC_Error(SPC_Bad_Bind);
433       return(SPC_ERROR);
434     }
435   }
436
437   /* Set up a queue for incoming connection requests */
438
439   listen(conn->sid, BACKLOG);
440
441   /* We are running standalone, so we need to loop forever waiting for
442      requests.  When we get one, we will fork a child to take care of
443      the processing for us, and then the parent will listen some more */
444
445   for(;;) {
446     struct hostent              *addr_ret;
447     _Xgethostbynameparams       addr_buf;
448
449     /* Attempt to accept a connection with a client */
450     
451     from = accept(conn->sid, (struct sockaddr *)&client_saddr, &len);
452     if (from == ERROR) {
453       SPC_Error(SPC_Bad_Accept);
454       return(SPC_ERROR);
455     }
456     
457     addr_ret = _XGethostbyaddr((char *)&client_saddr.sin_addr,
458                                sizeof(client_saddr.sin_addr),
459                                client_saddr.sin_family,
460                                addr_buf);
461
462     conn->remote = addr_ret;
463     strncpy(conn->hostname, conn->remote->h_name, MAXHOSTNAMELEN);
464
465 #ifdef DEBUG
466     pid = NULL;
467 #else    
468     /* Fork a process to handle I/O from/to client */
469     pid = fork();
470 #endif
471     
472     if (pid == ERROR) {
473       SPC_Error(SPC_Cannot_Fork);
474       /* We don't return here, but simply go around for a new try */
475     }
476
477     if (!pid)
478       /* We are the child.  Do whatever processing we need to do
479          on the connection & return */
480       return(SPC_Init_Child(conn, from));
481     /* Otherwise, we are still the parent.  Loop around for another
482        connection request */
483   }
484 }
485
486 /*----------------------------------------------------------------------+*/
487 int
488 SPC_Inetd_Daemon(SPC_Connection_Ptr conn)
489 /*----------------------------------------------------------------------+*/
490 {
491   conn->sid=0;
492   return(TRUE);
493 }
494
495 /*----------------------------------------------------------------------+*/
496 SPC_Connection_Ptr SPC_Start_Daemon(int standalone)
497 /*----------------------------------------------------------------------+*/
498 {
499
500   SPC_Connection_Ptr connection;
501
502   /* Do whatever it takes to initialize SPC */
503   _DtSvcProcessLock();
504   if (!SPC_Initialized)
505       if(SPC_Initialize()==SPC_ERROR) {
506           _DtSvcProcessUnlock();
507           return(SPC_ERROR);
508       }
509   _DtSvcProcessUnlock();
510
511   /* Get ourselves a connection structure.  We don't know the name
512      of the remote client yet, so use the null string as hostname */
513
514   if((connection=SPC_Make_Connection(NULL))==SPC_ERROR)
515     return(SPC_ERROR);
516   connection->local=official_hp;
517   if(standalone) {
518     if((SPC_Standalone_Daemon(connection))==SPC_ERROR)
519       return(SPC_ERROR);
520   } else {
521     if((SPC_Inetd_Daemon(connection))==SPC_ERROR)
522       return(SPC_ERROR);
523   }
524   connection->connected=TRUE;
525   return(connection);
526 }