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 separate 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 = 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 iConn = _IceConnectionObjs[i];
108
109		if (iConn->want_to_close || iConn->free_asap ||
110		    (context && iConn->context &&
111		     iConn->context != context))
112		{
113		    /* force a new connection to be created */
114		    break;
115		}
116
117		if (majorOpcodeCheck)
118		{
119		    for (j = iConn->his_min_opcode;
120		        j <= iConn->his_max_opcode; j++)
121		    {
122			if (iConn->process_msg_info[
123			    j - iConn->his_min_opcode].in_use &&
124			    iConn->process_msg_info[
125			    j - iConn->his_min_opcode].my_opcode ==
126			    majorOpcodeCheck)
127			    break;
128		    }
129
130		    if (j <= iConn->his_max_opcode ||
131			(iConn->protosetup_to_you &&
132			iConn->protosetup_to_you->my_opcode ==
133			majorOpcodeCheck))
134		    {
135			/* force a new connection to be created */
136			break;
137		    }
138		}
139
140		iConn->open_ref_count++;
141		if (context && !iConn->context)
142		    iConn->context = context;
143		return (iConn);
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 occurred 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    if (_X_LIKELY(pData != NULL)) {
351	STORE_STRING (pData, IceVendorString);
352	STORE_STRING (pData, IceReleaseString);
353
354	for (i = 0; i < _IceAuthCount; i++)
355	{
356	    if (authUsableFlags[i])
357	    {
358		STORE_STRING (pData, _IceAuthNames[i]);
359	    }
360	}
361
362	for (i = 0; i < _IceVersionCount; i++)
363	{
364	    STORE_CARD16 (pData, _IceVersions[i].major_version);
365	    STORE_CARD16 (pData, _IceVersions[i].minor_version);
366	}
367    }
368    else {
369	SEND_STRING (iceConn, IceVendorString);
370	SEND_STRING (iceConn, IceReleaseString);
371
372	for (i = 0; i < _IceAuthCount; i++)
373	{
374	    if (authUsableFlags[i])
375	    {
376		SEND_STRING (iceConn, _IceAuthNames[i]);
377	    }
378	}
379
380	for (i = 0; i < _IceVersionCount; i++)
381	{
382	    CARD16 v;
383	    v = _IceVersions[i].major_version;
384	    IceWriteData16 (iceConn, 2, &v);
385	    v = _IceVersions[i].minor_version;
386	    IceWriteData16 (iceConn, 2, &v);
387	}
388    }
389    IceFlush (iceConn);
390
391    /*
392     * Process messages until we get a Connection Reply or an Error Message.
393     * Authentication will take place behind the scenes.
394     */
395
396    replyWait.sequence_of_request = setup_sequence;
397    replyWait.major_opcode_of_request = 0;
398    replyWait.minor_opcode_of_request = ICE_ConnectionSetup;
399    replyWait.reply = (IcePointer) &reply;
400
401    gotReply = False;
402    ioErrorOccured = False;
403
404    while (!gotReply && !ioErrorOccured && iceConn)
405    {
406	ioErrorOccured = (IceProcessMessages (
407	    iceConn, &replyWait, &gotReply) == IceProcessMessagesIOError);
408
409	if (ioErrorOccured)
410	{
411	    if (errorStringRet && errorLength > 0) {
412		strncpy (errorStringRet, "IO error occurred opening connection",
413		    errorLength);
414		errorStringRet[errorLength - 1] = '\0';
415	    }
416	    _IceFreeConnection (iceConn);
417	    iceConn = NULL;
418	}
419	else if (gotReply)
420	{
421	    if (reply.type == ICE_CONNECTION_REPLY)
422	    {
423		if (reply.connection_reply.version_index >= _IceVersionCount)
424		{
425		    if (errorStringRet && errorLength > 0) {
426			strncpy (errorStringRet,
427			    "Got a bad version index in the Connection Reply",
428			    errorLength);
429			errorStringRet[errorLength - 1] = '\0';
430		    }
431
432		    free (reply.connection_reply.vendor);
433		    free (reply.connection_reply.release);
434		    _IceFreeConnection (iceConn);
435		    iceConn = NULL;
436		}
437		else
438		{
439		    iceConn->my_ice_version_index =
440			reply.connection_reply.version_index;
441		    iceConn->vendor = reply.connection_reply.vendor;
442		    iceConn->release = reply.connection_reply.release;
443
444		    _IceConnectionObjs[_IceConnectionCount] = iceConn;
445		    _IceConnectionStrings[_IceConnectionCount] =
446			iceConn->connection_string;
447		    _IceConnectionCount++;
448
449		    free (iceConn->connect_to_you);
450		    iceConn->connect_to_you = NULL;
451
452		    iceConn->connection_status = IceConnectAccepted;
453		}
454	    }
455	    else /* reply.type == ICE_CONNECTION_ERROR */
456	    {
457		/* Connection failed */
458
459		if (errorStringRet && errorLength > 0) {
460		    strncpy (errorStringRet,
461			reply.connection_error.error_message, errorLength);
462		    errorStringRet[errorLength - 1] = '\0';
463		}
464
465		free (reply.connection_error.error_message);
466
467		_IceFreeConnection (iceConn);
468		iceConn = NULL;
469	    }
470	}
471    }
472
473    if (iceConn && _IceWatchProcs)
474    {
475	/*
476	 * Notify the watch procedures that an iceConn was opened.
477	 */
478
479	_IceConnectionOpened (iceConn);
480    }
481
482    return (iceConn);
483}
484
485
486
487IcePointer
488IceGetConnectionContext (
489	IceConn    iceConn
490)
491{
492    return (iceConn->context);
493}
494
495
496
497/* ------------------------------------------------------------------------- *
498 *                            local routines                                 *
499 * ------------------------------------------------------------------------- */
500
501#define ICE_CONNECTION_RETRIES 5
502
503
504static XtransConnInfo
505ConnectToPeer (char *networkIdsList, char **actualConnectionRet)
506{
507    char addrbuf[256];
508    char* address;
509    char *ptr, *endptr, *delim;
510    int  madeConnection = 0;
511    size_t  len;
512    int  retry, connect_stat;
513    size_t  address_size;
514    XtransConnInfo trans_conn = NULL;
515
516    *actualConnectionRet = NULL;
517
518    ptr = networkIdsList;
519    len = strlen (networkIdsList);
520    endptr = networkIdsList + len;
521
522    if (len < sizeof addrbuf)
523    {
524       address = addrbuf;
525       address_size = 256;
526    }
527    else
528    {
529       address = malloc (len + 1);
530       if (address == NULL)
531           return NULL;
532       address_size = len;
533    }
534
535    while (ptr < endptr && !madeConnection)
536    {
537	if ((delim = strchr (ptr, ',')) == NULL)
538	    delim = endptr;
539
540	len = delim - ptr;
541	if (len > address_size - 1)
542	    len = address_size - 1;
543	strncpy (address, ptr, len);
544	address[len] = '\0';
545
546	ptr = delim + 1;
547
548	for (retry = ICE_CONNECTION_RETRIES; retry >= 0; retry--)
549	{
550	    if ((trans_conn = _IceTransOpenCOTSClient (address)) == NULL)
551	    {
552		break;
553	    }
554
555	    if ((connect_stat = _IceTransConnect (trans_conn, address)) < 0)
556	    {
557		_IceTransClose (trans_conn);
558		trans_conn = NULL;
559
560		if (connect_stat == TRANS_TRY_CONNECT_AGAIN)
561		{
562		    sleep(1);
563		    continue;
564		}
565		else
566		    break;
567	    }
568	    else
569	    {
570		madeConnection = 1;
571		break;
572	    }
573	}
574    }
575
576    if (madeConnection)
577    {
578	/*
579	 * We need to return the actual network connection string
580	 */
581
582	*actualConnectionRet = strdup(address);
583
584	/*
585	 * Return the file descriptor
586	 */
587    }
588    else trans_conn = NULL;
589
590    if (address != addrbuf) free (address);
591
592    return trans_conn;
593}
594