static wi_integer_t _wi_socket_read_buffer(wi_socket_t *socket, wi_time_interval_t timeout, void *buffer, size_t length) { wi_socket_state_t state; wi_integer_t bytes; #ifdef HAVE_OPENSSL_SSL_H if(socket->ssl) { if(timeout > 0.0 && SSL_pending(socket->ssl) == 0) { state = wi_socket_wait_descriptor(socket->sd, timeout, true, false); if(state != WI_SOCKET_READY) { if(state == WI_SOCKET_TIMEOUT) wi_error_set_errno(ETIMEDOUT); return -1; } } ERR_clear_error(); bytes = SSL_read(socket->ssl, buffer, length); if(bytes <= 0) { wi_error_set_openssl_ssl_error_with_result(socket->ssl, bytes); ERR_clear_error(); } return bytes; } else { #endif if(timeout > 0.0) { state = wi_socket_wait_descriptor(socket->sd, timeout, true, false); if(state != WI_SOCKET_READY) { if(state == WI_SOCKET_TIMEOUT) wi_error_set_errno(ETIMEDOUT); return -1; } } bytes = read(socket->sd, buffer, length); if(bytes <= 0) { if(bytes < 0) wi_error_set_errno(errno); else wi_error_set_libwired_error(WI_ERROR_SOCKET_EOF); } return bytes; #ifdef HAVE_OPENSSL_SSL_H } #endif return 0; }
int32_t wi_socket_write_buffer(wi_socket_t *socket, wi_time_interval_t timeout, const void *buffer, size_t length) { int bytes; if(timeout > 0.0) { if(wi_socket_wait_descriptor(socket->sd, timeout, false, true) <= 0) return -1; } #ifdef WI_SSL if(socket->ssl) { bytes = SSL_write(socket->ssl, buffer, length); if(bytes < 0) { wi_error_set_ssl_error(); socket->broken = true; } } else { #endif bytes = write(socket->sd, buffer, length); if(bytes < 0) wi_error_set_errno(errno); #ifdef WI_SSL } #endif return bytes; }
wi_socket_state_t wi_socket_wait(wi_socket_t *socket, wi_time_interval_t timeout) { if(wi_string_length(socket->buffer) > 0) return WI_SOCKET_READY; return wi_socket_wait_descriptor(socket->sd, timeout, (socket->direction & WI_SOCKET_READ), (socket->direction & WI_SOCKET_WRITE)); }
int32_t wi_socket_read_buffer(wi_socket_t *socket, wi_time_interval_t timeout, void *buffer, size_t length) { int bytes; if(timeout > 0.0) { #ifdef WI_SSL if(socket->ssl && SSL_pending(socket->ssl) == 0) { #endif if(wi_socket_wait_descriptor(socket->sd, timeout, true, false) <= 0) return -1; #ifdef WI_SSL } #endif } #ifdef WI_SSL if(socket->ssl) { bytes = SSL_read(socket->ssl, buffer, length); if(bytes <= 0) { wi_error_set_ssl_error(); socket->broken = true; } } else { #endif bytes = read(socket->sd, buffer, length); if(bytes < 0) wi_error_set_errno(errno); else if(bytes == 0) wi_error_set_lib_error(WI_ERROR_SOCKET_EOF); #ifdef WI_SSL } #endif return bytes; }
wi_boolean_t wi_socket_connect(wi_socket_t *socket, wi_time_interval_t timeout) { struct sockaddr *sa; wi_socket_state_t state; wi_uinteger_t length; int err; wi_boolean_t blocking; sa = wi_address_sa(socket->address); length = wi_address_sa_length(socket->address); if(timeout > 0.0) { blocking = wi_socket_blocking(socket); if(blocking) wi_socket_set_blocking(socket, false); err = connect(socket->sd, sa, length); if(err < 0) { if(errno != EINPROGRESS) { wi_error_set_errno(errno); return false; } do { state = wi_socket_wait_descriptor(socket->sd, 1.0, true, true); timeout -= 1.0; } while(state == WI_SOCKET_TIMEOUT && timeout >= 0.0); if(state == WI_SOCKET_ERROR) return false; if(timeout <= 0.0) { wi_error_set_errno(ETIMEDOUT); return false; } err = wi_socket_error(socket); if(err != 0) { wi_error_set_errno(err); return false; } } if(blocking) wi_socket_set_blocking(socket, true); } else { if(connect(socket->sd, sa, length) < 0) { wi_error_set_errno(errno); return false; } } socket->direction = WI_SOCKET_READ; return true; }
wi_integer_t wi_socket_read_buffer(wi_socket_t *socket, wi_time_interval_t timeout, void *buffer, size_t length) { wi_time_interval_t interval; wi_socket_state_t state; wi_uinteger_t offset; wi_integer_t bytes; WI_ASSERT(buffer != NULL, "buffer of length %u should not be NULL", length); if(timeout > 0.0) { #ifdef HAVE_OPENSSL_SSL_H if(!socket->ssl || (socket->ssl && SSL_pending(socket->ssl) == 0)) { #endif state = wi_socket_wait_descriptor(socket->sd, timeout, true, false); if(state != WI_SOCKET_READY) { if(state == WI_SOCKET_TIMEOUT) wi_error_set_errno(ETIMEDOUT); return -1; } #ifdef HAVE_OPENSSL_SSL_H } #endif } interval = 0.0; #ifdef HAVE_OPENSSL_SSL_H if(socket->ssl) { do { bytes = SSL_read(socket->ssl, buffer, length); if(bytes <= 0) { if(bytes < 0 && SSL_get_error(socket->ssl, bytes) == SSL_ERROR_WANT_READ) { wi_thread_sleep(0.1); if(timeout > 0.0) { interval += 0.1; if(interval >= timeout) { wi_error_set_errno(ETIMEDOUT); break; } } } else { wi_error_set_openssl_ssl_error_with_result(socket->ssl, bytes); socket->broken = true; break; } } } while(bytes <= 0); return bytes; } else { #endif offset = 0; do { bytes = read(socket->sd, buffer + offset, length - offset); if(bytes <= 0) { if(bytes < 0) wi_error_set_errno(errno); else wi_error_set_libwired_error(WI_ERROR_SOCKET_EOF); return bytes; } offset += bytes; if(offset < length) { wi_thread_sleep(0.1); if(timeout > 0.0) { interval += 0.1; if(interval >= timeout) { wi_error_set_errno(ETIMEDOUT); return -1; } } } } while(offset < length); return offset; #ifdef HAVE_OPENSSL_SSL_H } #endif return 0; }
wi_boolean_t wi_socket_accept_tls(wi_socket_t *socket, wi_socket_tls_t *tls, wi_time_interval_t timeout) { wi_socket_state_t state; int err, result; wi_boolean_t blocking; socket->ssl = SSL_new(tls->ssl_ctx); if(!socket->ssl) { wi_error_set_openssl_error(); return false; } if(SSL_set_fd(socket->ssl, socket->sd) != 1) { wi_error_set_openssl_error(); return false; } if(!tls->private_key && tls->dh) { if(SSL_set_tmp_dh(socket->ssl, tls->dh) != 1) { wi_error_set_openssl_error(); return false; } } if(timeout > 0.0) { blocking = wi_socket_blocking(socket); if(blocking) wi_socket_set_blocking(socket, false); result = SSL_accept(socket->ssl); if(result != 1) { do { err = SSL_get_error(socket->ssl, result); if(err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) { wi_error_set_openssl_ssl_error_with_result(socket->ssl, result); return false; } state = wi_socket_wait_descriptor(socket->sd, 1.0, (err == SSL_ERROR_WANT_READ), (err == SSL_ERROR_WANT_WRITE)); if(state == WI_SOCKET_ERROR) break; else if(state == WI_SOCKET_READY) { result = SSL_accept(socket->ssl); if(result == 1) break; } timeout -= 1.0; } while(timeout >= 0.0); if(state == WI_SOCKET_ERROR) return false; if(timeout <= 0.0) { wi_error_set_errno(ETIMEDOUT); return false; } } if(blocking) wi_socket_set_blocking(socket, true); } else { result = SSL_accept(socket->ssl); if(result != 1) { wi_error_set_openssl_ssl_error_with_result(socket->ssl, result); return false; } } return true; }
static void wd_transfer_upload(wd_transfer_t *transfer) { wi_pool_t *pool; wi_string_t *path; wd_account_t *account; char buffer[WD_TRANSFER_BUFFER_SIZE]; wi_time_interval_t timeout, interval, speedinterval, statusinterval, accountinterval; wi_socket_state_t state; ssize_t result, speedbytes, statsbytes; int sd, bytes; pool = wi_pool_init(wi_pool_alloc()); /* start upload */ wi_log_info(WI_STR("Receiving \"%@\" from %@"), transfer->path, wd_user_identifier(transfer->user)); wi_socket_set_direction(transfer->socket, WI_SOCKET_READ); // if(!wi_socket_set_blocking(transfer->socket, false)) // wi_log_warn(WI_STR("Could not set non-blocking for %@: %m"), wd_user_ip(transfer->user)); sd = wi_socket_descriptor(transfer->socket); speedinterval = statusinterval = accountinterval = wi_time_interval(); speedbytes = statsbytes = 0; account = wd_user_account(transfer->user); /* update status */ wi_lock_lock(wd_status_lock); wd_current_uploads++; wd_total_uploads++; wd_write_status(true); wi_lock_unlock(wd_status_lock); while(wd_transfer_state(transfer) == WD_TRANSFER_RUNNING && transfer->transferred < transfer->size) { /* wait to read */ timeout = 0.0; do { state = wi_socket_wait_descriptor(sd, 0.1, true, false); if(state == WI_SOCKET_TIMEOUT) { timeout += 0.1; if(timeout >= 30.0) break; } } while(state == WI_SOCKET_TIMEOUT && wd_transfer_state(transfer) == WD_TRANSFER_RUNNING); if(state == WI_SOCKET_ERROR) { wi_log_err(WI_STR("Could not wait for upload from %@: %m"), wd_user_ip(transfer->user)); break; } if(timeout >= 30.0) { wi_log_err(WI_STR("Timed out waiting to read upload from %@"), wd_user_ip(transfer->user)); break; } /* read data */ bytes = wi_socket_read_buffer(transfer->socket, 30.0, buffer, sizeof(buffer)); if(bytes <= 0) { if(bytes < 0) { wi_log_err(WI_STR("Could not read upload from %@: %m"), wd_user_ip(transfer->user)); } break; } if((wi_file_offset_t) bytes > transfer->size - transfer->transferred) bytes = transfer->size - transfer->transferred; /* write data */ result = write(transfer->fd, buffer, bytes); if(result <= 0) { if(result < 0) { wi_log_err(WI_STR("Could not write upload to %@: %s"), transfer->realpath, strerror(errno)); } break; } /* update counters */ interval = wi_time_interval(); transfer->transferred += bytes; speedbytes += bytes; statsbytes += bytes; /* update speed */ transfer->speed = speedbytes / (interval - speedinterval); wd_transfer_limit_upload_speed(transfer, account, speedbytes, interval, speedinterval); if(interval - speedinterval > 30.0) { speedbytes = 0; speedinterval = interval; } /* update status */ if(interval - statusinterval > wd_current_uploads) { wi_lock_lock(wd_status_lock); wd_uploads_traffic += statsbytes; wd_write_status(false); wi_lock_unlock(wd_status_lock); statsbytes = 0; statusinterval = interval; } /* update account */ if(interval - accountinterval > 15.0) { account = wd_user_account(transfer->user); accountinterval = interval; } wi_pool_drain(pool); } wi_log_info(WI_STR("Received %@/%@ (%llu/%llu bytes) of \"%@\" from %@"), wd_files_string_for_bytes(transfer->transferred - transfer->offset), wd_files_string_for_bytes(transfer->size - transfer->offset), transfer->transferred - transfer->offset, transfer->size - transfer->offset, transfer->path, wd_user_identifier(transfer->user)); /* update status */ wd_transfer_set_state(transfer, WD_TRANSFER_STOPPED); wi_lock_lock(wd_status_lock); wd_current_uploads--; wd_uploads_traffic += statsbytes; wd_write_status(true); wi_lock_unlock(wd_status_lock); if(transfer->transferred == transfer->size) { path = wi_string_by_deleting_path_extension(transfer->realpath); if(wi_fs_rename_path(transfer->realpath, path)) { path = wi_string_by_appending_path_extension(transfer->path, WI_STR(WD_TRANSFERS_PARTIAL_EXTENSION)); wd_files_move_comment(path, transfer->path); } else { wi_log_warn(WI_STR("Could not move %@ to %@: %m"), transfer->realpath, path); } } wi_release(pool); }
static wi_boolean_t wd_transfer_download(wd_transfer_t *transfer) { wi_pool_t *pool; wi_socket_t *socket; wi_p7_socket_t *p7_socket; wd_account_t *account; char buffer[WD_TRANSFER_BUFFER_SIZE]; wi_socket_state_t state; wi_time_interval_t timeout, interval, speedinterval, statusinterval, accountinterval; wi_file_offset_t sendbytes, speedbytes, statsbytes; wi_uinteger_t i, transfers; ssize_t readbytes; int sd; wi_boolean_t data, result; wd_user_state_t user_state; interval = wi_time_interval(); speedinterval = interval; statusinterval = interval; accountinterval = interval; speedbytes = 0; statsbytes = 0; i = 0; socket = wd_user_socket(transfer->user); sd = wi_socket_descriptor(socket); p7_socket = wd_user_p7_socket(transfer->user); account = wd_user_account(transfer->user); data = true; result = true; wd_transfers_note_statistics(WD_TRANSFER_DOWNLOAD, WD_TRANSFER_STATISTICS_ADD, 0); wi_dictionary_rdlock(wd_transfers_user_downloads); transfers = (wi_integer_t) wi_dictionary_data_for_key(wd_transfers_user_downloads, transfer->key); wi_dictionary_unlock(wd_transfers_user_downloads); pool = wi_pool_init(wi_pool_alloc()); wd_user_lock_socket(transfer->user); while(wd_user_state(transfer->user) == WD_USER_LOGGED_IN) { if(data && transfer->remainingdatasize == 0) data = false; if(!data && transfer->remainingrsrcsize == 0) break; readbytes = read(data ? transfer->datafd : transfer->rsrcfd, buffer, sizeof(buffer)); if(readbytes <= 0) { if(readbytes < 0) { wi_log_error(WI_STR("Could not read download from \"%@\": %m"), data ? transfer->realdatapath : transfer->realrsrcpath, strerror(errno)); } result = false; break; } timeout = wi_time_interval(); do { user_state = wd_user_state(transfer->user); state = wi_socket_wait_descriptor(sd, 0.1, false, true); if(state == WI_SOCKET_TIMEOUT) { if(wi_time_interval() - timeout >= 30.0) break; } } while(state == WI_SOCKET_TIMEOUT && user_state == WD_USER_LOGGED_IN); if(state == WI_SOCKET_ERROR || wi_time_interval() - timeout >= 30.0) { wi_log_error(WI_STR("Could not wait for download to %@: %@"), wd_user_identifier(transfer->user), (state == WI_SOCKET_ERROR) ? wi_error_string() : WI_STR("Timed out")); result = false; break; } if(user_state != WD_USER_LOGGED_IN) { result = false; break; } if(data) { sendbytes = (transfer->remainingdatasize < (wi_file_offset_t) readbytes) ? transfer->remainingdatasize : (wi_file_offset_t) readbytes; } else { sendbytes = (transfer->remainingrsrcsize < (wi_file_offset_t) readbytes) ? transfer->remainingrsrcsize : (wi_file_offset_t) readbytes; } if(!wi_p7_socket_write_oobdata(p7_socket, 30.0, buffer, sendbytes)) { wi_log_error(WI_STR("Could not write download to %@: %m"), wd_user_identifier(transfer->user)); result = false; break; } if(data) transfer->remainingdatasize -= sendbytes; else transfer->remainingrsrcsize -= sendbytes; interval = wi_time_interval(); transfer->transferred += sendbytes; transfer->actualtransferred += sendbytes; speedbytes += sendbytes; statsbytes += sendbytes; transfer->speed = speedbytes / (interval - speedinterval); wd_transfer_limit_speed(transfer, wd_transfers_total_download_speed, wd_account_transfer_download_speed_limit(account), wd_current_downloads, transfers, speedbytes, interval, speedinterval); if(interval - speedinterval > 30.0) { speedbytes = 0; speedinterval = interval; } if(interval - statusinterval > wd_current_downloads) { wd_transfers_note_statistics(WD_TRANSFER_DOWNLOAD, WD_TRANSFER_STATISTICS_DATA, statsbytes); statsbytes = 0; statusinterval = interval; } if(interval - accountinterval > 15.0) { account = wd_user_account(transfer->user); accountinterval = interval; wi_dictionary_rdlock(wd_transfers_user_downloads); transfers = (wi_integer_t) wi_dictionary_data_for_key(wd_transfers_user_downloads, transfer->key); wi_dictionary_unlock(wd_transfers_user_downloads); } if(++i % 1000 == 0) wi_pool_drain(pool); } wd_user_unlock_socket(transfer->user); wi_release(pool); wd_transfers_note_statistics(WD_TRANSFER_DOWNLOAD, WD_TRANSFER_STATISTICS_REMOVE, statsbytes); return result; }
wi_boolean_t wi_socket_connect(wi_socket_t *socket, wi_socket_context_t *context, wi_time_interval_t timeout) { struct sockaddr *sa; uint32_t length; int state, err; #ifdef WI_SSL int ret; #endif wi_boolean_t blocking; blocking = wi_socket_blocking(socket); if(blocking) wi_socket_set_blocking(socket, false); sa = wi_address_sa(socket->address); length = wi_address_sa_length(socket->address); err = connect(socket->sd, sa, length); if(err < 0) { if(errno != EINPROGRESS) { wi_error_set_errno(errno); return false; } do { state = wi_socket_wait_descriptor(socket->sd, 1.0, true, true); timeout -= 1.0; } while(state == 0 && timeout >= 0.0); if(state < 0) return false; if(timeout <= 0.0) { wi_error_set_errno(ETIMEDOUT); return false; } err = wi_socket_error(socket); if(err != 0) { wi_error_set_errno(err); return false; } } #ifdef WI_SSL if(context && context->ssl_ctx) { socket->ssl = SSL_new(context->ssl_ctx); if(!socket->ssl) { wi_error_set_ssl_error(); return false; } if(SSL_set_fd(socket->ssl, socket->sd) != 1) { wi_error_set_ssl_error(); return false; } ret = SSL_connect(socket->ssl); if(ret != 1) { do { err = SSL_get_error(socket->ssl, ret); if(err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) { wi_error_set_ssl_error(); return false; } state = wi_socket_wait_descriptor(socket->sd, 1.0, (err == SSL_ERROR_WANT_READ), (err == SSL_ERROR_WANT_WRITE)); if(state < 0) break; if(state > 0) { ret = SSL_connect(socket->ssl); if(ret == 1) break; } timeout -= 1.0; } while(timeout >= 0.0); if(state < 0) return false; if(timeout <= 0.0) { wi_error_set_errno(ETIMEDOUT); return false; } } } #endif if(blocking) wi_socket_set_blocking(socket, true); socket->direction = WI_SOCKET_READ; return true; }
wi_boolean_t wi_socket_wait(wi_socket_t *socket, wi_time_interval_t timeout) { return wi_socket_wait_descriptor(socket->sd, timeout, (socket->direction & WI_SOCKET_READ), (socket->direction & WI_SOCKET_WRITE)); }
wi_socket_t * wi_socket_accept(wi_socket_t *accept_socket, wi_socket_context_t *context, wi_time_interval_t timeout, wi_address_t **address) { wi_socket_t *socket; #ifdef WI_SSL SSL *ssl = NULL; #endif struct sockaddr_storage ss; socklen_t length; int sd; length = sizeof(ss); sd = accept(accept_socket->sd, (struct sockaddr *) &ss, &length); *address = (length > 0) ? wi_autorelease(wi_address_init_with_sa(wi_address_alloc(), (struct sockaddr *) &ss)) : NULL; if(sd < 0) { wi_error_set_errno(errno); goto err; } #ifdef WI_SSL if(context && context->ssl_ctx) { ssl = SSL_new(context->ssl_ctx); if(!ssl) { wi_error_set_ssl_error(); goto err; } if(SSL_set_fd(ssl, sd) != 1) { wi_error_set_ssl_error(); goto err; } if(!context->certificate && context->dh) { if(SSL_set_tmp_dh(ssl, context->dh) != 1) { wi_error_set_ssl_error(); goto err; } } if(timeout > 0.0) { if(wi_socket_wait_descriptor(sd, timeout, true, false) <= 0) goto err; } if(SSL_accept(ssl) != 1) { wi_error_set_ssl_error(); goto err; } } #endif socket = wi_socket_init_with_descriptor(wi_socket_alloc(), sd); socket->close = true; socket->address = wi_retain(*address); socket->type = accept_socket->type; socket->direction = WI_SOCKET_READ; socket->interactive = accept_socket->interactive; #ifdef WI_SSL socket->ssl = ssl; #endif return wi_autorelease(socket); err: #ifdef WI_SSL if(ssl) SSL_free(ssl); #endif if(sd >= 0) close(sd); return NULL; }
wi_integer_t wi_socket_read_buffer(wi_socket_t *socket, wi_time_interval_t timeout, void *buffer, size_t length) { #ifdef HAVE_OPENSSL_SSL_H wi_socket_state_t state; #endif wi_uinteger_t offset; wi_integer_t bytes; WI_ASSERT(buffer != NULL, "buffer of length %u should not be NULL", length); WI_ASSERT(socket->sd >= 0, "socket %@ should be valid", socket); #ifdef HAVE_OPENSSL_SSL_H if(socket->ssl) { while(true) { if(timeout > 0.0 && SSL_pending(socket->ssl) == 0) { state = wi_socket_wait_descriptor(socket->sd, timeout, true, false); if(state != WI_SOCKET_READY) { if(state == WI_SOCKET_TIMEOUT) wi_error_set_errno(ETIMEDOUT); return -1; } } ERR_clear_error(); bytes = SSL_read(socket->ssl, buffer, length); if(bytes > 0) { break; } else { if(bytes < 0 && SSL_get_error(socket->ssl, bytes) == SSL_ERROR_WANT_READ) continue; wi_error_set_openssl_ssl_error_with_result(socket->ssl, bytes); break; } } ERR_clear_error(); return bytes; } else { #endif offset = 0; while(offset < length) { bytes = _wi_socket_read_buffer(socket, timeout, buffer + offset, length - offset); if(bytes <= 0) return -1; offset += bytes; } return offset; #ifdef HAVE_OPENSSL_SSL_H } #endif return 0; }