1/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) 2 * All rights reserved. 3 * 4 * This package is an SSL implementation written 5 * by Eric Young (eay@cryptsoft.com). 6 * The implementation was written so as to conform with Netscapes SSL. 7 * 8 * This library is free for commercial and non-commercial use as long as 9 * the following conditions are aheared to. The following conditions 10 * apply to all code found in this distribution, be it the RC4, RSA, 11 * lhash, DES, etc., code; not just the SSL code. The SSL documentation 12 * included with this distribution is covered by the same copyright terms 13 * except that the holder is Tim Hudson (tjh@cryptsoft.com). 14 * 15 * Copyright remains Eric Young's, and as such any Copyright notices in 16 * the code are not to be removed. 17 * If this package is used in a product, Eric Young should be given attribution 18 * as the author of the parts of the library used. 19 * This can be in the form of a textual message at program startup or 20 * in documentation (online or textual) provided with the package. 21 * 22 * Redistribution and use in source and binary forms, with or without 23 * modification, are permitted provided that the following conditions 24 * are met: 25 * 1. Redistributions of source code must retain the copyright 26 * notice, this list of conditions and the following disclaimer. 27 * 2. Redistributions in binary form must reproduce the above copyright 28 * notice, this list of conditions and the following disclaimer in the 29 * documentation and/or other materials provided with the distribution. 30 * 3. All advertising materials mentioning features or use of this software 31 * must display the following acknowledgement: 32 * "This product includes cryptographic software written by 33 * Eric Young (eay@cryptsoft.com)" 34 * The word 'cryptographic' can be left out if the rouines from the library 35 * being used are not cryptographic related :-). 36 * 4. If you include any Windows specific code (or a derivative thereof) from 37 * the apps directory (application code) you must include an acknowledgement: 38 * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" 39 * 40 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND 41 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 42 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 43 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 44 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 45 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 46 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 47 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 48 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 49 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 50 * SUCH DAMAGE. 51 * 52 * The licence and distribution terms for any publically available version or 53 * derivative of this code cannot be changed. i.e. this code cannot simply be 54 * copied and put under another distribution licence 55 * [including the GNU Public Licence.] */ 56 57#include <openssl/bio.h> 58 59#include <assert.h> 60#include <errno.h> 61#include <stdio.h> 62 63#if !defined(OPENSSL_WINDOWS) 64#include <sys/socket.h> 65#include <netinet/in.h> 66#include <arpa/inet.h> 67#include <unistd.h> 68#endif 69 70#include <openssl/buf.h> 71#include <openssl/err.h> 72#include <openssl/mem.h> 73 74#include "internal.h" 75 76 77enum { 78 BIO_CONN_S_BEFORE, 79 BIO_CONN_S_BLOCKED_CONNECT, 80 BIO_CONN_S_OK, 81}; 82 83typedef struct bio_connect_st { 84 int state; 85 86 char *param_hostname; 87 char *param_port; 88 int nbio; 89 90 uint8_t ip[4]; 91 unsigned short port; 92 93 struct sockaddr_storage them; 94 socklen_t them_length; 95 96 /* the file descriptor is kept in bio->num in order to match the socket 97 * BIO. */ 98 99 /* info_callback is called when the connection is initially made 100 * callback(BIO,state,ret); The callback should return 'ret', state is for 101 * compatibility with the SSL info_callback. */ 102 int (*info_callback)(const BIO *bio, int state, int ret); 103} BIO_CONNECT; 104 105/* maybe_copy_ipv4_address sets |*ipv4| to the IPv4 address from |ss| (in 106 * big-endian order), if |ss| contains an IPv4 socket address. */ 107static void maybe_copy_ipv4_address(uint8_t *ipv4, 108 const struct sockaddr_storage *ss) { 109 const struct sockaddr_in *sin; 110 111 if (ss->ss_family != AF_INET) { 112 return; 113 } 114 115 sin = (const struct sockaddr_in*) ss; 116 memcpy(ipv4, &sin->sin_addr, 4); 117} 118 119static int conn_state(BIO *bio, BIO_CONNECT *c) { 120 int ret = -1, i; 121 char *p, *q; 122 int (*cb)(const BIO *, int, int) = NULL; 123 124 if (c->info_callback != NULL) { 125 cb = c->info_callback; 126 } 127 128 for (;;) { 129 switch (c->state) { 130 case BIO_CONN_S_BEFORE: 131 p = c->param_hostname; 132 if (p == NULL) { 133 OPENSSL_PUT_ERROR(BIO, conn_state, BIO_R_NO_HOSTNAME_SPECIFIED); 134 goto exit_loop; 135 } 136 for (; *p != 0; p++) { 137 if (*p == ':' || *p == '/') { 138 break; 139 } 140 } 141 142 i = *p; 143 if (i == ':' || i == '/') { 144 *(p++) = 0; 145 if (i == ':') { 146 for (q = p; *q; q++) { 147 if (*q == '/') { 148 *q = 0; 149 break; 150 } 151 } 152 if (c->param_port != NULL) { 153 OPENSSL_free(c->param_port); 154 } 155 c->param_port = BUF_strdup(p); 156 } 157 } 158 159 if (c->param_port == NULL) { 160 OPENSSL_PUT_ERROR(BIO, conn_state, BIO_R_NO_PORT_SPECIFIED); 161 ERR_add_error_data(2, "host=", c->param_hostname); 162 goto exit_loop; 163 } 164 165 if (!bio_ip_and_port_to_socket_and_addr( 166 &bio->num, &c->them, &c->them_length, c->param_hostname, 167 c->param_port)) { 168 OPENSSL_PUT_ERROR(BIO, conn_state, BIO_R_UNABLE_TO_CREATE_SOCKET); 169 ERR_add_error_data(4, "host=", c->param_hostname, ":", c->param_port); 170 goto exit_loop; 171 } 172 173 memset(c->ip, 0, 4); 174 maybe_copy_ipv4_address(c->ip, &c->them); 175 176 if (c->nbio) { 177 if (!bio_socket_nbio(bio->num, 1)) { 178 OPENSSL_PUT_ERROR(BIO, conn_state, BIO_R_ERROR_SETTING_NBIO); 179 ERR_add_error_data(4, "host=", c->param_hostname, ":", 180 c->param_port); 181 goto exit_loop; 182 } 183 } 184 185 i = 1; 186 ret = setsockopt(bio->num, SOL_SOCKET, SO_KEEPALIVE, (char *)&i, 187 sizeof(i)); 188 if (ret < 0) { 189 OPENSSL_PUT_SYSTEM_ERROR(setsockopt); 190 OPENSSL_PUT_ERROR(BIO, conn_state, BIO_R_KEEPALIVE); 191 ERR_add_error_data(4, "host=", c->param_hostname, ":", c->param_port); 192 goto exit_loop; 193 } 194 195 BIO_clear_retry_flags(bio); 196 ret = connect(bio->num, (struct sockaddr*) &c->them, c->them_length); 197 if (ret < 0) { 198 if (bio_fd_should_retry(ret)) { 199 BIO_set_flags(bio, (BIO_FLAGS_IO_SPECIAL | BIO_FLAGS_SHOULD_RETRY)); 200 c->state = BIO_CONN_S_BLOCKED_CONNECT; 201 bio->retry_reason = BIO_RR_CONNECT; 202 } else { 203 OPENSSL_PUT_SYSTEM_ERROR(connect); 204 OPENSSL_PUT_ERROR(BIO, conn_state, BIO_R_CONNECT_ERROR); 205 ERR_add_error_data(4, "host=", c->param_hostname, ":", 206 c->param_port); 207 } 208 goto exit_loop; 209 } else { 210 c->state = BIO_CONN_S_OK; 211 } 212 break; 213 214 case BIO_CONN_S_BLOCKED_CONNECT: 215 i = bio_sock_error(bio->num); 216 if (i) { 217 if (bio_fd_should_retry(ret)) { 218 BIO_set_flags(bio, (BIO_FLAGS_IO_SPECIAL | BIO_FLAGS_SHOULD_RETRY)); 219 c->state = BIO_CONN_S_BLOCKED_CONNECT; 220 bio->retry_reason = BIO_RR_CONNECT; 221 ret = -1; 222 } else { 223 BIO_clear_retry_flags(bio); 224 OPENSSL_PUT_SYSTEM_ERROR(connect); 225 OPENSSL_PUT_ERROR(BIO, conn_state, BIO_R_NBIO_CONNECT_ERROR); 226 ERR_add_error_data(4, "host=", c->param_hostname, ":", c->param_port); 227 ret = 0; 228 } 229 goto exit_loop; 230 } else { 231 c->state = BIO_CONN_S_OK; 232 } 233 break; 234 235 case BIO_CONN_S_OK: 236 ret = 1; 237 goto exit_loop; 238 default: 239 assert(0); 240 goto exit_loop; 241 } 242 243 if (cb != NULL) { 244 ret = cb((BIO *)bio, c->state, ret); 245 if (ret == 0) { 246 goto end; 247 } 248 } 249 } 250 251exit_loop: 252 if (cb != NULL) { 253 ret = cb((BIO *)bio, c->state, ret); 254 } 255 256end: 257 return ret; 258} 259 260static BIO_CONNECT *BIO_CONNECT_new(void) { 261 BIO_CONNECT *ret = OPENSSL_malloc(sizeof(BIO_CONNECT)); 262 263 if (ret == NULL) { 264 return NULL; 265 } 266 memset(ret, 0, sizeof(BIO_CONNECT)); 267 268 ret->state = BIO_CONN_S_BEFORE; 269 return ret; 270} 271 272static void BIO_CONNECT_free(BIO_CONNECT *c) { 273 if (c == NULL) { 274 return; 275 } 276 277 if (c->param_hostname != NULL) { 278 OPENSSL_free(c->param_hostname); 279 } 280 if (c->param_port != NULL) { 281 OPENSSL_free(c->param_port); 282 } 283 OPENSSL_free(c); 284} 285 286static int conn_new(BIO *bio) { 287 bio->init = 0; 288 bio->num = -1; 289 bio->flags = 0; 290 bio->ptr = (char *)BIO_CONNECT_new(); 291 return bio->ptr != NULL; 292} 293 294static void conn_close_socket(BIO *bio) { 295 BIO_CONNECT *c = (BIO_CONNECT *) bio->ptr; 296 297 if (bio->num == -1) { 298 return; 299 } 300 301 /* Only do a shutdown if things were established */ 302 if (c->state == BIO_CONN_S_OK) { 303 shutdown(bio->num, 2); 304 } 305 close(bio->num); 306 bio->num = -1; 307} 308 309static int conn_free(BIO *bio) { 310 if (bio == NULL) { 311 return 0; 312 } 313 314 if (bio->shutdown) { 315 conn_close_socket(bio); 316 } 317 318 BIO_CONNECT_free((BIO_CONNECT*) bio->ptr); 319 320 return 1; 321} 322 323static int conn_read(BIO *bio, char *out, int out_len) { 324 int ret = 0; 325 BIO_CONNECT *data; 326 327 data = (BIO_CONNECT *)bio->ptr; 328 if (data->state != BIO_CONN_S_OK) { 329 ret = conn_state(bio, data); 330 if (ret <= 0) { 331 return ret; 332 } 333 } 334 335 bio_clear_socket_error(); 336 ret = recv(bio->num, out, out_len, 0); 337 BIO_clear_retry_flags(bio); 338 if (ret <= 0) { 339 if (bio_fd_should_retry(ret)) { 340 BIO_set_retry_read(bio); 341 } 342 } 343 344 return ret; 345} 346 347static int conn_write(BIO *bio, const char *in, int in_len) { 348 int ret; 349 BIO_CONNECT *data; 350 351 data = (BIO_CONNECT *)bio->ptr; 352 if (data->state != BIO_CONN_S_OK) { 353 ret = conn_state(bio, data); 354 if (ret <= 0) { 355 return ret; 356 } 357 } 358 359 bio_clear_socket_error(); 360 ret = send(bio->num, in, in_len, 0); 361 BIO_clear_retry_flags(bio); 362 if (ret <= 0) { 363 if (bio_fd_should_retry(ret)) { 364 BIO_set_retry_write(bio); 365 } 366 } 367 368 return ret; 369} 370 371static long conn_ctrl(BIO *bio, int cmd, long num, void *ptr) { 372 int *ip; 373 const char **pptr; 374 long ret = 1; 375 BIO_CONNECT *data; 376 377 data = (BIO_CONNECT *)bio->ptr; 378 379 switch (cmd) { 380 case BIO_CTRL_RESET: 381 ret = 0; 382 data->state = BIO_CONN_S_BEFORE; 383 conn_close_socket(bio); 384 bio->flags = 0; 385 break; 386 case BIO_C_DO_STATE_MACHINE: 387 /* use this one to start the connection */ 388 if (data->state != BIO_CONN_S_OK) 389 ret = (long)conn_state(bio, data); 390 else 391 ret = 1; 392 break; 393 case BIO_C_GET_CONNECT: 394 /* TODO(fork): can this be removed? (Or maybe this whole file). */ 395 if (ptr != NULL) { 396 pptr = (const char **)ptr; 397 if (num == 0) { 398 *pptr = data->param_hostname; 399 } else if (num == 1) { 400 *pptr = data->param_port; 401 } else if (num == 2) { 402 *pptr = (char *) &data->ip[0]; 403 } else if (num == 3) { 404 *((int *)ptr) = data->port; 405 } 406 if (!bio->init) { 407 *pptr = "not initialized"; 408 } 409 ret = 1; 410 } 411 break; 412 case BIO_C_SET_CONNECT: 413 if (ptr != NULL) { 414 bio->init = 1; 415 if (num == 0) { 416 if (data->param_hostname != NULL) { 417 OPENSSL_free(data->param_hostname); 418 } 419 data->param_hostname = BUF_strdup(ptr); 420 } else if (num == 1) { 421 if (data->param_port != NULL) { 422 OPENSSL_free(data->param_port); 423 } 424 data->param_port = BUF_strdup(ptr); 425 } else { 426 ret = 0; 427 } 428 } 429 break; 430 case BIO_C_SET_NBIO: 431 data->nbio = (int)num; 432 break; 433 case BIO_C_GET_FD: 434 if (bio->init) { 435 ip = (int *)ptr; 436 if (ip != NULL) { 437 *ip = bio->num; 438 } 439 ret = 1; 440 } else { 441 ret = 0; 442 } 443 break; 444 case BIO_CTRL_GET_CLOSE: 445 ret = bio->shutdown; 446 break; 447 case BIO_CTRL_SET_CLOSE: 448 bio->shutdown = (int)num; 449 break; 450 case BIO_CTRL_PENDING: 451 case BIO_CTRL_WPENDING: 452 ret = 0; 453 break; 454 case BIO_CTRL_FLUSH: 455 break; 456 case BIO_CTRL_SET_CALLBACK: { 457#if 0 /* FIXME: Should this be used? -- Richard Levitte */ 458 OPENSSL_PUT_ERROR(BIO, XXX, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); 459 ret = -1; 460#else 461 ret = 0; 462#endif 463 } break; 464 case BIO_CTRL_GET_CALLBACK: { 465 int (**fptr)(const BIO *bio, int state, int xret); 466 fptr = (int (**)(const BIO *bio, int state, int xret))ptr; 467 *fptr = data->info_callback; 468 } break; 469 default: 470 ret = 0; 471 break; 472 } 473 return (ret); 474} 475 476static long conn_callback_ctrl(BIO *bio, int cmd, bio_info_cb fp) { 477 long ret = 1; 478 BIO_CONNECT *data; 479 480 data = (BIO_CONNECT *)bio->ptr; 481 482 switch (cmd) { 483 case BIO_CTRL_SET_CALLBACK: { 484 data->info_callback = (int (*)(const struct bio_st *, int, int))fp; 485 } break; 486 default: 487 ret = 0; 488 break; 489 } 490 return ret; 491} 492 493static int conn_puts(BIO *bp, const char *str) { 494 return conn_write(bp, str, strlen(str)); 495} 496 497BIO *BIO_new_connect(const char *hostname) { 498 BIO *ret; 499 500 ret = BIO_new(BIO_s_connect()); 501 if (ret == NULL) { 502 return NULL; 503 } 504 if (!BIO_set_conn_hostname(ret, hostname)) { 505 BIO_free(ret); 506 return NULL; 507 } 508 return ret; 509} 510 511static const BIO_METHOD methods_connectp = { 512 BIO_TYPE_CONNECT, "socket connect", conn_write, conn_read, 513 conn_puts, NULL /* connect_gets, */, conn_ctrl, conn_new, 514 conn_free, conn_callback_ctrl, 515}; 516 517const BIO_METHOD *BIO_s_connect(void) { return &methods_connectp; } 518 519int BIO_set_conn_hostname(BIO *bio, const char *name) { 520 return BIO_ctrl(bio, BIO_C_SET_CONNECT, 0, (void*) name); 521} 522 523int BIO_set_conn_port(BIO *bio, const char *port_str) { 524 return BIO_ctrl(bio, BIO_C_SET_CONNECT, 1, (void*) port_str); 525} 526 527int BIO_set_nbio(BIO *bio, int on) { 528 return BIO_ctrl(bio, BIO_C_SET_NBIO, on, NULL); 529} 530