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