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