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