connect.c revision 266e564d
1/* $Xorg: connect.c,v 1.4 2001/02/09 02:03:26 xorgcvs Exp $ */
2/******************************************************************************
3
4
5Copyright 1993, 1998  The Open Group
6
7Permission to use, copy, modify, distribute, and sell this software and its
8documentation for any purpose is hereby granted without fee, provided that
9the above copyright notice appear in all copies and that both that
10copyright notice and this permission notice appear in supporting
11documentation.
12
13The above copyright notice and this permission notice shall be included in
14all copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
19OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
20AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
23Except as contained in this notice, the name of The Open Group shall not be
24used in advertising or otherwise to promote the sale, use or other dealings
25in this Software without prior written authorization from The Open Group.
26
27Author: Ralph Mor, X Consortium
28******************************************************************************/
29/* $XFree86: xc/lib/ICE/connect.c,v 3.9 2001/12/14 19:53:35 dawes Exp $ */
30
31#ifdef HAVE_CONFIG_H
32#include <config.h>
33#endif
34#include <X11/ICE/ICElib.h>
35#include "ICElibint.h"
36#include <X11/Xtrans/Xtrans.h>
37#include "globals.h"
38
39static XtransConnInfo ConnectToPeer(char *networkIdsList,
40				    char **actualConnectionRet);
41
42#define Strstr strstr
43
44IceConn
45IceOpenConnection (networkIdsList, context, mustAuthenticate, majorOpcodeCheck,
46    errorLength, errorStringRet)
47
48char 	   *networkIdsList;
49IcePointer context;
50Bool 	   mustAuthenticate;
51int  	   majorOpcodeCheck;
52int  	   errorLength;
53char 	   *errorStringRet;
54
55{
56    IceConn			iceConn;
57    int				extra, i, j;
58    int		       		endian;
59    Bool			gotReply, ioErrorOccured;
60    unsigned long		setup_sequence;
61    iceByteOrderMsg		*pByteOrderMsg;
62    iceConnectionSetupMsg	*pSetupMsg;
63    char			*pData;
64    IceReplyWaitInfo 		replyWait;
65    _IceReply		 	reply;
66    int				authUsableCount;
67    int				authUsableFlags[MAX_ICE_AUTH_NAMES];
68    int				authIndices[MAX_ICE_AUTH_NAMES];
69
70    if (errorStringRet && errorLength > 0)
71	*errorStringRet = '\0';
72
73    if (networkIdsList == NULL || *networkIdsList == '\0')
74    {
75	strncpy (errorStringRet,
76	    "networkIdsList argument is NULL", errorLength);
77	return (NULL);
78    }
79
80    /*
81     * Check to see if we can use a previously created ICE connection.
82     *
83     * If iceConn->want_to_close is True, or iceConn->free_asap is True,
84     * we can not use the iceConn.
85     *
86     * If 'context' is non-NULL, we will only use a previously opened ICE
87     * connection if the specified 'context' is equal to the context
88     * associated with the ICE connection, or if the context associated
89     * with the ICE connection is NULL.
90     *
91     * If 'majorOpcodeCheck' is non-zero, it will contain a protocol major
92     * opcode that we should make sure is not already active on the ICE
93     * connection.  Some clients will want two seperate connections for the
94     * same protocol to the same destination client.
95     */
96
97    for (i = 0; i < _IceConnectionCount; i++)
98    {
99	char *strptr;
100	if ((strptr = (char *) Strstr (
101	    networkIdsList, _IceConnectionStrings[i])) != NULL)
102	{
103	    char ch = *(strptr + strlen (_IceConnectionStrings[i]));
104	    if (ch == ',' || ch == '\0')
105	    {
106		/*
107		 * OK, we found a connection.  Make sure we can reuse it.
108		 */
109
110		IceConn iceConn = _IceConnectionObjs[i];
111
112		if (iceConn->want_to_close || iceConn->free_asap ||
113		    (context && iceConn->context &&
114		     iceConn->context != context))
115		{
116		    /* force a new connection to be created */
117		    break;
118		}
119
120		if (majorOpcodeCheck)
121		{
122		    for (j = iceConn->his_min_opcode;
123		        j <= iceConn->his_max_opcode; j++)
124		    {
125			if (iceConn->process_msg_info[
126			    j - iceConn->his_min_opcode].in_use &&
127			    iceConn->process_msg_info[
128			    j - iceConn->his_min_opcode].my_opcode ==
129			    majorOpcodeCheck)
130			    break;
131		    }
132
133		    if (j <= iceConn->his_max_opcode ||
134			(iceConn->protosetup_to_you &&
135			iceConn->protosetup_to_you->my_opcode ==
136			majorOpcodeCheck))
137		    {
138			/* force a new connection to be created */
139			break;
140		    }
141		}
142
143		iceConn->open_ref_count++;
144		if (context && !iceConn->context)
145		    iceConn->context = context;
146		return (iceConn);
147	    }
148	}
149    }
150
151    if ((iceConn = (IceConn) malloc (sizeof (struct _IceConn))) == NULL)
152    {
153	strncpy (errorStringRet, "Can't malloc", errorLength);
154	return (NULL);
155    }
156
157
158    /*
159     * Open a network connection with the peer client.
160     */
161
162    if ((iceConn->trans_conn = ConnectToPeer (networkIdsList,
163	&iceConn->connection_string)) == NULL)
164    {
165	free ((char *) iceConn);
166	strncpy (errorStringRet, "Could not open network socket", errorLength);
167	return (NULL);
168    }
169
170    /*
171     * Set close-on-exec so that programs that fork() don't get confused.
172     */
173
174    _IceTransSetOption (iceConn->trans_conn, TRANS_CLOSEONEXEC, 1);
175
176    iceConn->listen_obj = NULL;
177
178    iceConn->connection_status = IceConnectPending;
179    iceConn->io_ok = True;
180    iceConn->dispatch_level = 0;
181    iceConn->context = context;
182    iceConn->my_ice_version_index = 0;
183    iceConn->send_sequence = 0;
184    iceConn->receive_sequence = 0;
185
186    iceConn->vendor = NULL;
187    iceConn->release = NULL;
188    iceConn->outbuf = NULL;
189
190    iceConn->scratch = NULL;
191    iceConn->scratch_size = 0;
192
193    iceConn->process_msg_info = NULL;
194
195    iceConn->connect_to_you = NULL;
196    iceConn->protosetup_to_you = NULL;
197
198    iceConn->connect_to_me = NULL;
199    iceConn->protosetup_to_me = NULL;
200
201    if ((iceConn->inbuf = iceConn->inbufptr =
202	(char *) malloc (ICE_INBUFSIZE)) == NULL)
203    {
204	_IceFreeConnection (iceConn);
205	strncpy (errorStringRet, "Can't malloc", errorLength);
206	return (NULL);
207    }
208
209    iceConn->inbufmax = iceConn->inbuf + ICE_INBUFSIZE;
210
211    if ((iceConn->outbuf = iceConn->outbufptr =
212	(char *) calloc (1, ICE_OUTBUFSIZE)) == NULL)
213    {
214	_IceFreeConnection (iceConn);
215	strncpy (errorStringRet, "Can't malloc", errorLength);
216	return (NULL);
217    }
218
219    iceConn->outbufmax = iceConn->outbuf + ICE_OUTBUFSIZE;
220
221    iceConn->open_ref_count = 1;
222    iceConn->proto_ref_count = 0;
223
224    iceConn->skip_want_to_close = False;
225    iceConn->want_to_close = False;
226    iceConn->free_asap = False;
227
228    iceConn->saved_reply_waits = NULL;
229    iceConn->ping_waits = NULL;
230
231    iceConn->connect_to_you = (_IceConnectToYouInfo *) malloc (
232	sizeof (_IceConnectToYouInfo));
233    iceConn->connect_to_you->auth_active = 0;
234
235    /*
236     * Send our byte order.
237     */
238
239    IceGetHeader (iceConn, 0, ICE_ByteOrder,
240	SIZEOF (iceByteOrderMsg), iceByteOrderMsg, pByteOrderMsg);
241
242    endian = 1;
243    if (*(char *) &endian)
244	pByteOrderMsg->byteOrder = IceLSBfirst;
245    else
246	pByteOrderMsg->byteOrder = IceMSBfirst;
247
248    IceFlush (iceConn);
249
250
251    /*
252     * Now read the ByteOrder message from the other client.
253     * iceConn->swap should be set to the appropriate boolean
254     * value after the call to IceProcessMessages.
255     */
256
257    iceConn->waiting_for_byteorder = True;
258
259    ioErrorOccured = False;
260    while (iceConn->waiting_for_byteorder == True && !ioErrorOccured)
261    {
262	ioErrorOccured = (IceProcessMessages (
263	    iceConn, NULL, NULL) == IceProcessMessagesIOError);
264    }
265
266    if (ioErrorOccured)
267    {
268	_IceFreeConnection (iceConn);
269	strncpy (errorStringRet, "IO error occured opening connection",
270	     errorLength);
271	return (NULL);
272    }
273
274    if (iceConn->connection_status == IceConnectRejected)
275    {
276	/*
277	 * We failed to get the required ByteOrder message.
278	 */
279
280	_IceFreeConnection (iceConn);
281	strncpy (errorStringRet,
282	    "Internal error - did not receive the expected ByteOrder message",
283	     errorLength);
284	return (NULL);
285    }
286
287
288    /*
289     * Determine which authentication methods are available for
290     * the Connection Setup authentication.
291     */
292
293    _IceGetPoValidAuthIndices (
294	"ICE", iceConn->connection_string,
295	_IceAuthCount, _IceAuthNames, &authUsableCount, authIndices);
296
297    for (i = 0; i < _IceAuthCount; i++)
298    {
299	authUsableFlags[i] = 0;
300	for (j = 0; j < authUsableCount && !authUsableFlags[i]; j++)
301	    authUsableFlags[i] = (authIndices[j] == i);
302    }
303
304
305    /*
306     * Now send a Connection Setup message.
307     */
308
309    extra = STRING_BYTES (IceVendorString) + STRING_BYTES (IceReleaseString);
310
311    for (i = 0; i < _IceAuthCount; i++)
312	if (authUsableFlags[i])
313	{
314	    extra += STRING_BYTES (_IceAuthNames[i]);
315	}
316
317    extra += (_IceVersionCount * 4);
318
319    IceGetHeaderExtra (iceConn, 0, ICE_ConnectionSetup,
320	SIZEOF (iceConnectionSetupMsg), WORD64COUNT (extra),
321	iceConnectionSetupMsg, pSetupMsg, pData);
322
323    setup_sequence = iceConn->send_sequence;
324
325    pSetupMsg->versionCount = _IceVersionCount;
326    pSetupMsg->authCount = authUsableCount;
327    pSetupMsg->mustAuthenticate = mustAuthenticate;
328
329    STORE_STRING (pData, IceVendorString);
330    STORE_STRING (pData, IceReleaseString);
331
332    for (i = 0; i < _IceAuthCount; i++)
333	if (authUsableFlags[i])
334	{
335	    STORE_STRING (pData, _IceAuthNames[i]);
336	}
337
338    for (i = 0; i < _IceVersionCount; i++)
339    {
340	STORE_CARD16 (pData, _IceVersions[i].major_version);
341	STORE_CARD16 (pData, _IceVersions[i].minor_version);
342    }
343
344    IceFlush (iceConn);
345
346
347    /*
348     * Process messages until we get a Connection Reply or an Error Message.
349     * Authentication will take place behind the scenes.
350     */
351
352    replyWait.sequence_of_request = setup_sequence;
353    replyWait.major_opcode_of_request = 0;
354    replyWait.minor_opcode_of_request = ICE_ConnectionSetup;
355    replyWait.reply = (IcePointer) &reply;
356
357    gotReply = False;
358    ioErrorOccured = False;
359
360    while (!gotReply && !ioErrorOccured)
361    {
362	ioErrorOccured = (IceProcessMessages (
363	    iceConn, &replyWait, &gotReply) == IceProcessMessagesIOError);
364
365	if (ioErrorOccured)
366	{
367	    strncpy (errorStringRet, "IO error occured opening connection",
368		errorLength);
369	    _IceFreeConnection (iceConn);
370	    iceConn = NULL;
371	}
372	else if (gotReply)
373	{
374	    if (reply.type == ICE_CONNECTION_REPLY)
375	    {
376		if (reply.connection_reply.version_index >= _IceVersionCount)
377		{
378		    strncpy (errorStringRet,
379	    		"Got a bad version index in the Connection Reply",
380			errorLength);
381
382		    free (reply.connection_reply.vendor);
383		    free (reply.connection_reply.release);
384		    _IceFreeConnection (iceConn);
385		    iceConn = NULL;
386		}
387		else
388		{
389		    iceConn->my_ice_version_index =
390			reply.connection_reply.version_index;
391		    iceConn->vendor = reply.connection_reply.vendor;
392		    iceConn->release = reply.connection_reply.release;
393
394		    _IceConnectionObjs[_IceConnectionCount] = iceConn;
395		    _IceConnectionStrings[_IceConnectionCount] =
396			iceConn->connection_string;
397		    _IceConnectionCount++;
398
399		    free ((char *) iceConn->connect_to_you);
400		    iceConn->connect_to_you = NULL;
401
402		    iceConn->connection_status = IceConnectAccepted;
403		}
404	    }
405	    else /* reply.type == ICE_CONNECTION_ERROR */
406	    {
407		/* Connection failed */
408
409		strncpy (errorStringRet, reply.connection_error.error_message,
410		    errorLength);
411
412		free (reply.connection_error.error_message);
413
414		_IceFreeConnection (iceConn);
415		iceConn = NULL;
416	    }
417	}
418    }
419
420    if (iceConn && _IceWatchProcs)
421    {
422	/*
423	 * Notify the watch procedures that an iceConn was opened.
424	 */
425
426	_IceConnectionOpened (iceConn);
427    }
428
429    return (iceConn);
430}
431
432
433
434IcePointer
435IceGetConnectionContext (iceConn)
436
437IceConn    iceConn;
438
439{
440    return (iceConn->context);
441}
442
443
444
445/* ------------------------------------------------------------------------- *
446 *                            local routines                                 *
447 * ------------------------------------------------------------------------- */
448
449#define ICE_CONNECTION_RETRIES 5
450
451
452static XtransConnInfo
453ConnectToPeer (char *networkIdsList, char **actualConnectionRet)
454{
455    char addrbuf[256];
456    char* address;
457    char *ptr, *endptr, *delim;
458    int  madeConnection = 0;
459    int  len, retry;
460    int  connect_stat;
461    int  address_size;
462    XtransConnInfo trans_conn = NULL;
463
464    *actualConnectionRet = NULL;
465
466    ptr = networkIdsList;
467    len = strlen (networkIdsList);
468    endptr = networkIdsList + len;
469
470    if (len < sizeof addrbuf)
471    {
472       address = addrbuf;
473       address_size = 256;
474    }
475    else
476    {
477       address = malloc (len + 1);
478       address_size = len;
479    }
480
481    while (ptr < endptr && !madeConnection)
482    {
483	if ((delim = (char *) strchr (ptr, ',')) == NULL)
484	    delim = endptr;
485
486	len = delim - ptr;
487	if (len > address_size - 1)
488	    len = address_size - 1;
489	strncpy (address, ptr, len);
490	address[len] = '\0';
491
492	ptr = delim + 1;
493
494	for (retry = ICE_CONNECTION_RETRIES; retry >= 0; retry--)
495	{
496	    if ((trans_conn = _IceTransOpenCOTSClient (address)) == NULL)
497	    {
498		break;
499	    }
500
501	    if ((connect_stat = _IceTransConnect (trans_conn, address)) < 0)
502	    {
503		_IceTransClose (trans_conn);
504
505		if (connect_stat == TRANS_TRY_CONNECT_AGAIN)
506		{
507		    sleep(1);
508		    continue;
509		}
510		else
511		    break;
512	    }
513	    else
514	    {
515		madeConnection = 1;
516		break;
517	    }
518	}
519    }
520
521    if (madeConnection)
522    {
523	/*
524	 * We need to return the actual network connection string
525	 */
526
527	*actualConnectionRet = strdup(address);
528
529	/*
530	 * Return the file descriptor
531	 */
532    }
533    else trans_conn = NULL;
534
535    if (address != addrbuf) free (address);
536
537    return trans_conn;
538}
539