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