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