📄 tunala.c
字号:
meth = (server_mode ? SSLv23_server_method() : SSLv23_client_method()); if(meth == NULL) goto err; if(engine_id) { ENGINE_load_builtin_engines(); if((e = ENGINE_by_id(engine_id)) == NULL) { fprintf(stderr, "Error obtaining '%s' engine, openssl " "errors follow\n", engine_id); goto err; } if(!ENGINE_set_default(e, ENGINE_METHOD_ALL)) { fprintf(stderr, "Error assigning '%s' engine, openssl " "errors follow\n", engine_id); goto err; } ENGINE_free(e); } if((ctx = SSL_CTX_new(meth)) == NULL) goto err; /* cacert */ if(CAfile) { if(!X509_STORE_load_locations(SSL_CTX_get_cert_store(ctx), CAfile, NULL)) { fprintf(stderr, "Error loading CA cert(s) in '%s'\n", CAfile); goto err; } fprintf(stderr, "Info, operating with CA cert(s) in '%s'\n", CAfile); } else fprintf(stderr, "Info, operating without a CA cert(-list)\n"); if(!SSL_CTX_set_default_verify_paths(ctx)) { fprintf(stderr, "Error setting default verify paths\n"); goto err; } /* cert and key */ if((cert || key) && !ctx_set_cert(ctx, cert, key)) goto err; /* dcert and dkey */ if((dcert || dkey) && !ctx_set_cert(ctx, dcert, dkey)) goto err; /* temporary RSA key generation */ if(tmp_rsa) SSL_CTX_set_tmp_rsa_callback(ctx, cb_generate_tmp_rsa); /* cipher_list */ if(cipher_list) { if(!SSL_CTX_set_cipher_list(ctx, cipher_list)) { fprintf(stderr, "Error setting cipher list '%s'\n", cipher_list); goto err; } fprintf(stderr, "Info, set cipher list '%s'\n", cipher_list); } else fprintf(stderr, "Info, operating with default cipher list\n"); /* dh_file & dh_special */ if((dh_file || dh_special) && !ctx_set_dh(ctx, dh_file, dh_special)) goto err; /* ctx_options */ SSL_CTX_set_options(ctx, ctx_options); /* out_state (output of SSL handshake states to screen). */ if(out_state) cb_ssl_info_set_output(stderr); /* out_verify */ if(out_verify > 0) { cb_ssl_verify_set_output(stderr); cb_ssl_verify_set_level(out_verify); } /* verify_depth */ cb_ssl_verify_set_depth(verify_depth); /* Success! (includes setting verify_mode) */ SSL_CTX_set_info_callback(ctx, cb_ssl_info); SSL_CTX_set_verify(ctx, verify_mode, cb_ssl_verify); ret = ctx;err: if(!ret) { ERR_print_errors_fp(stderr); if(ctx) SSL_CTX_free(ctx); } return ret;}/*****************//* Selector bits *//*****************/static void selector_sets_init(select_sets_t *s){ s->max = 0; FD_ZERO(&s->reads); FD_ZERO(&s->sends); FD_ZERO(&s->excepts);}static void selector_init(tunala_selector_t *selector){ selector_sets_init(&selector->last_selected); selector_sets_init(&selector->next_select);}#define SEL_EXCEPTS 0x00#define SEL_READS 0x01#define SEL_SENDS 0x02static void selector_add_raw_fd(tunala_selector_t *s, int fd, int flags){ FD_SET(fd, &s->next_select.excepts); if(flags & SEL_READS) FD_SET(fd, &s->next_select.reads); if(flags & SEL_SENDS) FD_SET(fd, &s->next_select.sends); /* Adjust "max" */ if(s->next_select.max < (fd + 1)) s->next_select.max = fd + 1;}static void selector_add_listener(tunala_selector_t *selector, int fd){ selector_add_raw_fd(selector, fd, SEL_READS);}static void selector_add_tunala(tunala_selector_t *s, tunala_item_t *t){ /* Set clean read if sm.clean_in is not full */ if(t->clean_read != -1) { selector_add_raw_fd(s, t->clean_read, (buffer_full(state_machine_get_buffer(&t->sm, SM_CLEAN_IN)) ? SEL_EXCEPTS : SEL_READS)); } /* Set clean send if sm.clean_out is not empty */ if(t->clean_send != -1) { selector_add_raw_fd(s, t->clean_send, (buffer_empty(state_machine_get_buffer(&t->sm, SM_CLEAN_OUT)) ? SEL_EXCEPTS : SEL_SENDS)); } /* Set dirty read if sm.dirty_in is not full */ if(t->dirty_read != -1) { selector_add_raw_fd(s, t->dirty_read, (buffer_full(state_machine_get_buffer(&t->sm, SM_DIRTY_IN)) ? SEL_EXCEPTS : SEL_READS)); } /* Set dirty send if sm.dirty_out is not empty */ if(t->dirty_send != -1) { selector_add_raw_fd(s, t->dirty_send, (buffer_empty(state_machine_get_buffer(&t->sm, SM_DIRTY_OUT)) ? SEL_EXCEPTS : SEL_SENDS)); }}static int selector_select(tunala_selector_t *selector){ memcpy(&selector->last_selected, &selector->next_select, sizeof(select_sets_t)); selector_sets_init(&selector->next_select); return select(selector->last_selected.max, &selector->last_selected.reads, &selector->last_selected.sends, &selector->last_selected.excepts, NULL);}/* This returns -1 for error, 0 for no new connections, or 1 for success, in * which case *newfd is populated. */static int selector_get_listener(tunala_selector_t *selector, int fd, int *newfd){ if(FD_ISSET(fd, &selector->last_selected.excepts)) return -1; if(!FD_ISSET(fd, &selector->last_selected.reads)) return 0; if((*newfd = ip_accept_connection(fd)) == -1) return -1; return 1;}/************************//* "Tunala" world stuff *//************************/static int tunala_world_make_room(tunala_world_t *world){ unsigned int newsize; tunala_item_t *newarray; if(world->tunnels_used < world->tunnels_size) return 1; newsize = (world->tunnels_size == 0 ? 16 : ((world->tunnels_size * 3) / 2)); if((newarray = malloc(newsize * sizeof(tunala_item_t))) == NULL) return 0; memset(newarray, 0, newsize * sizeof(tunala_item_t)); if(world->tunnels_used > 0) memcpy(newarray, world->tunnels, world->tunnels_used * sizeof(tunala_item_t)); if(world->tunnels_size > 0) free(world->tunnels); /* migrate */ world->tunnels = newarray; world->tunnels_size = newsize; return 1;}static int tunala_world_new_item(tunala_world_t *world, int fd, const char *ip, unsigned short port, int flipped){ tunala_item_t *item; int newfd; SSL *new_ssl = NULL; if(!tunala_world_make_room(world)) return 0; if((new_ssl = SSL_new(world->ssl_ctx)) == NULL) { fprintf(stderr, "Error creating new SSL\n"); ERR_print_errors_fp(stderr); return 0; } item = world->tunnels + (world->tunnels_used++); state_machine_init(&item->sm); item->clean_read = item->clean_send = item->dirty_read = item->dirty_send = -1; if((newfd = ip_create_connection_split(ip, port)) == -1) goto err; /* Which way round? If we're a server, "fd" is the dirty side and the * connection we open is the clean one. For a client, it's the other way * around. Unless, of course, we're "flipped" in which case everything * gets reversed. :-) */ if((world->server_mode && !flipped) || (!world->server_mode && flipped)) { item->dirty_read = item->dirty_send = fd; item->clean_read = item->clean_send = newfd; } else { item->clean_read = item->clean_send = fd; item->dirty_read = item->dirty_send = newfd; } /* We use the SSL's "app_data" to indicate a call-back induced "kill" */ SSL_set_app_data(new_ssl, NULL); if(!state_machine_set_SSL(&item->sm, new_ssl, world->server_mode)) goto err; return 1;err: tunala_world_del_item(world, world->tunnels_used - 1); return 0;}static void tunala_world_del_item(tunala_world_t *world, unsigned int idx){ tunala_item_t *item = world->tunnels + idx; if(item->clean_read != -1) close(item->clean_read); if(item->clean_send != item->clean_read) close(item->clean_send); item->clean_read = item->clean_send = -1; if(item->dirty_read != -1) close(item->dirty_read); if(item->dirty_send != item->dirty_read) close(item->dirty_send); item->dirty_read = item->dirty_send = -1; state_machine_close(&item->sm); /* OK, now we fix the item array */ if(idx + 1 < world->tunnels_used) /* We need to scroll entries to the left */ memmove(world->tunnels + idx, world->tunnels + (idx + 1), (world->tunnels_used - (idx + 1)) * sizeof(tunala_item_t)); world->tunnels_used--;}static int tunala_item_io(tunala_selector_t *selector, tunala_item_t *item){ int c_r, c_s, d_r, d_s; /* Four boolean flags */ /* Take ourselves out of the gene-pool if there was an except */ if((item->clean_read != -1) && FD_ISSET(item->clean_read, &selector->last_selected.excepts)) return 0; if((item->clean_send != -1) && FD_ISSET(item->clean_send, &selector->last_selected.excepts)) return 0; if((item->dirty_read != -1) && FD_ISSET(item->dirty_read, &selector->last_selected.excepts)) return 0; if((item->dirty_send != -1) && FD_ISSET(item->dirty_send, &selector->last_selected.excepts)) return 0; /* Grab our 4 IO flags */ c_r = c_s = d_r = d_s = 0; if(item->clean_read != -1) c_r = FD_ISSET(item->clean_read, &selector->last_selected.reads); if(item->clean_send != -1) c_s = FD_ISSET(item->clean_send, &selector->last_selected.sends); if(item->dirty_read != -1) d_r = FD_ISSET(item->dirty_read, &selector->last_selected.reads); if(item->dirty_send != -1) d_s = FD_ISSET(item->dirty_send, &selector->last_selected.sends); /* If no IO has happened for us, skip needless data looping */ if(!c_r && !c_s && !d_r && !d_s) return 1; if(c_r) c_r = (buffer_from_fd(state_machine_get_buffer(&item->sm, SM_CLEAN_IN), item->clean_read) <= 0); if(c_s) c_s = (buffer_to_fd(state_machine_get_buffer(&item->sm, SM_CLEAN_OUT), item->clean_send) <= 0); if(d_r) d_r = (buffer_from_fd(state_machine_get_buffer(&item->sm, SM_DIRTY_IN), item->dirty_read) <= 0); if(d_s) d_s = (buffer_to_fd(state_machine_get_buffer(&item->sm, SM_DIRTY_OUT), item->dirty_send) <= 0); /* If any of the flags is non-zero, that means they need closing */ if(c_r) { close(item->clean_read); if(item->clean_send == item->clean_read) item->clean_send = -1; item->clean_read = -1; } if(c_s && (item->clean_send != -1)) { close(item->clean_send); if(item->clean_send == item->clean_read) item->clean_read = -1; item->clean_send = -1; } if(d_r) { close(item->dirty_read); if(item->dirty_send == item->dirty_read) item->dirty_send = -1; item->dirty_read = -1; } if(d_s && (item->dirty_send != -1)) { close(item->dirty_send); if(item->dirty_send == item->dirty_read) item->dirty_read = -1; item->dirty_send = -1; } /* This function name is attributed to the term donated by David * Schwartz on openssl-dev, message-ID: * <NCBBLIEPOCNJOAEKBEAKEEDGLIAA.davids@webmaster.com>. :-) */ if(!state_machine_churn(&item->sm)) /* If the SSL closes, it will also zero-out the _in buffers * and will in future process just outgoing data. As and * when the outgoing data has gone, it will return zero * here to tell us to bail out. */ return 0; /* Otherwise, we return zero if both sides are dead. */ if(((item->clean_read == -1) || (item->clean_send == -1)) && ((item->dirty_read == -1) || (item->dirty_send == -1))) return 0; /* If only one side closed, notify the SSL of this so it can take * appropriate action. */ if((item->clean_read == -1) || (item->clean_send == -1)) { if(!state_machine_close_clean(&item->sm)) return 0; } if((item->dirty_read == -1) || (item->dirty_send == -1)) { if(!state_machine_close_dirty(&item->sm)) return 0; } return 1;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -