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