ruby-libstorj 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.gitmodules +3 -0
- data/.rspec +1 -0
- data/Gemfile +23 -0
- data/Gemfile.lock +111 -0
- data/Guardfile +21 -0
- data/LICENSE +502 -0
- data/README.md +262 -0
- data/Rakefile +76 -0
- data/ext/libstorj/.gitignore +47 -0
- data/ext/libstorj/.travis.yml +27 -0
- data/ext/libstorj/Doxyfile +2427 -0
- data/ext/libstorj/LICENSE +502 -0
- data/ext/libstorj/Makefile.am +6 -0
- data/ext/libstorj/README.md +198 -0
- data/ext/libstorj/autogen.sh +3 -0
- data/ext/libstorj/configure.ac +64 -0
- data/ext/libstorj/depends/Makefile +153 -0
- data/ext/libstorj/depends/config.guess +1462 -0
- data/ext/libstorj/depends/config.sub +1823 -0
- data/ext/libstorj/depends/extract-osx-sdk.sh +33 -0
- data/ext/libstorj/depends/packages/cctools.mk +7 -0
- data/ext/libstorj/depends/packages/clang.mk +7 -0
- data/ext/libstorj/depends/packages/gmp.mk +23 -0
- data/ext/libstorj/depends/packages/gnutls.mk +25 -0
- data/ext/libstorj/depends/packages/json-c.mk +7 -0
- data/ext/libstorj/depends/packages/libcurl.mk +39 -0
- data/ext/libstorj/depends/packages/libmicrohttpd.mk +7 -0
- data/ext/libstorj/depends/packages/libuv.mk +7 -0
- data/ext/libstorj/depends/packages/nettle.mk +30 -0
- data/ext/libstorj/libstorj.pc.in +11 -0
- data/ext/libstorj/src/Makefile.am +23 -0
- data/ext/libstorj/src/bip39.c +233 -0
- data/ext/libstorj/src/bip39.h +64 -0
- data/ext/libstorj/src/bip39_english.h +2074 -0
- data/ext/libstorj/src/cli.c +1494 -0
- data/ext/libstorj/src/crypto.c +525 -0
- data/ext/libstorj/src/crypto.h +178 -0
- data/ext/libstorj/src/downloader.c +1923 -0
- data/ext/libstorj/src/downloader.h +163 -0
- data/ext/libstorj/src/http.c +688 -0
- data/ext/libstorj/src/http.h +175 -0
- data/ext/libstorj/src/rs.c +962 -0
- data/ext/libstorj/src/rs.h +99 -0
- data/ext/libstorj/src/storj.c +1523 -0
- data/ext/libstorj/src/storj.h +1014 -0
- data/ext/libstorj/src/uploader.c +2736 -0
- data/ext/libstorj/src/uploader.h +181 -0
- data/ext/libstorj/src/utils.c +336 -0
- data/ext/libstorj/src/utils.h +65 -0
- data/ext/libstorj/test/Makefile.am +27 -0
- data/ext/libstorj/test/mockbridge.c +260 -0
- data/ext/libstorj/test/mockbridge.json +687 -0
- data/ext/libstorj/test/mockbridgeinfo.json +1836 -0
- data/ext/libstorj/test/mockfarmer.c +358 -0
- data/ext/libstorj/test/storjtests.h +41 -0
- data/ext/libstorj/test/tests.c +1617 -0
- data/ext/libstorj/test/tests_rs.c +869 -0
- data/ext/ruby-libstorj/extconf.rb +8 -0
- data/ext/ruby-libstorj/ruby-libstorj.cc +17 -0
- data/lib/ruby-libstorj.rb +1 -0
- data/lib/ruby-libstorj/arg_forwarding_task.rb +58 -0
- data/lib/ruby-libstorj/env.rb +178 -0
- data/lib/ruby-libstorj/ext/bucket.rb +71 -0
- data/lib/ruby-libstorj/ext/create_bucket_request.rb +53 -0
- data/lib/ruby-libstorj/ext/curl_code.rb +139 -0
- data/lib/ruby-libstorj/ext/ext.rb +71 -0
- data/lib/ruby-libstorj/ext/file.rb +84 -0
- data/lib/ruby-libstorj/ext/get_bucket_request.rb +45 -0
- data/lib/ruby-libstorj/ext/json_request.rb +51 -0
- data/lib/ruby-libstorj/ext/list_files_request.rb +63 -0
- data/lib/ruby-libstorj/ext/types.rb +226 -0
- data/lib/ruby-libstorj/ext/upload_options.rb +38 -0
- data/lib/ruby-libstorj/libstorj.rb +22 -0
- data/lib/ruby-libstorj/mixins/storj.rb +27 -0
- data/lib/ruby-libstorj/struct.rb +42 -0
- data/ruby-libstorj.gemspec +57 -0
- data/spec/helpers/options.yml.example +22 -0
- data/spec/helpers/shared_rake_examples.rb +132 -0
- data/spec/helpers/storj_options.rb +96 -0
- data/spec/helpers/upload.data +3 -0
- data/spec/helpers/upload.data.sha256 +1 -0
- data/spec/libstorj_spec.rb +0 -0
- data/spec/ruby-libstorj/arg_forwarding_task_spec.rb +311 -0
- data/spec/ruby-libstorj/env_spec.rb +353 -0
- data/spec/ruby-libstorj/ext_spec.rb +75 -0
- data/spec/ruby-libstorj/json_request_spec.rb +13 -0
- data/spec/ruby-libstorj/libstorj_spec.rb +81 -0
- data/spec/ruby-libstorj/struct_spec.rb +64 -0
- data/spec/spec_helper.rb +113 -0
- metadata +136 -0
@@ -0,0 +1,163 @@
|
|
1
|
+
/**
|
2
|
+
* @file downloader.h
|
3
|
+
* @brief Storj download methods and definitions.
|
4
|
+
*
|
5
|
+
* Structures and functions useful for downloading files.
|
6
|
+
*/
|
7
|
+
#ifndef STORJ_DOWNLOADER_H
|
8
|
+
#define STORJ_DOWNLOADER_H
|
9
|
+
|
10
|
+
#include "storj.h"
|
11
|
+
#include "http.h"
|
12
|
+
#include "utils.h"
|
13
|
+
#include "crypto.h"
|
14
|
+
#include "rs.h"
|
15
|
+
|
16
|
+
#define STORJ_DOWNLOAD_CONCURRENCY 24
|
17
|
+
#define STORJ_DOWNLOAD_WRITESYNC_CONCURRENCY 4
|
18
|
+
#define STORJ_DEFAULT_MIRRORS 5
|
19
|
+
#define STORJ_MAX_REPORT_TRIES 2
|
20
|
+
#define STORJ_MAX_TOKEN_TRIES 6
|
21
|
+
#define STORJ_MAX_POINTER_TRIES 6
|
22
|
+
#define STORJ_MAX_INFO_TRIES 6
|
23
|
+
|
24
|
+
/** @brief Enumerable that defines that status of a pointer
|
25
|
+
*
|
26
|
+
* A pointer will begin as created, and move forward until an error
|
27
|
+
* occurs, in which case it will start moving backwards from the error
|
28
|
+
* state until it has been replaced and reset back to created. This process
|
29
|
+
* can continue until success.
|
30
|
+
*/
|
31
|
+
typedef enum {
|
32
|
+
POINTER_BEING_REPLACED = -3,
|
33
|
+
POINTER_ERROR_REPORTED = -2,
|
34
|
+
POINTER_ERROR = -1,
|
35
|
+
POINTER_CREATED = 0,
|
36
|
+
POINTER_BEING_DOWNLOADED = 1,
|
37
|
+
POINTER_DOWNLOADED = 2,
|
38
|
+
POINTER_MISSING = 3,
|
39
|
+
POINTER_FINISHED = 4
|
40
|
+
} storj_pointer_status_t;
|
41
|
+
|
42
|
+
/** @brief A structure for sharing data with worker threads for writing
|
43
|
+
* a shard to a file decriptor.
|
44
|
+
*/
|
45
|
+
typedef struct {
|
46
|
+
char *shard_data;
|
47
|
+
ssize_t shard_total_bytes;
|
48
|
+
int error_status;
|
49
|
+
FILE *destination;
|
50
|
+
uint32_t pointer_index;
|
51
|
+
/* state should not be modified in worker threads */
|
52
|
+
storj_download_state_t *state;
|
53
|
+
} shard_request_write_t;
|
54
|
+
|
55
|
+
/** @brief A structure for repairing shards from parity shards */
|
56
|
+
typedef struct {
|
57
|
+
int fd;
|
58
|
+
uint64_t filesize;
|
59
|
+
uint64_t data_filesize;
|
60
|
+
uint32_t data_shards;
|
61
|
+
uint32_t parity_shards;
|
62
|
+
uint64_t shard_size;
|
63
|
+
uint8_t *decrypt_key;
|
64
|
+
uint8_t *decrypt_ctr;
|
65
|
+
uint8_t *zilch;
|
66
|
+
bool has_missing;
|
67
|
+
/* state should not be modified in worker threads */
|
68
|
+
storj_download_state_t *state;
|
69
|
+
int error_status;
|
70
|
+
} file_request_recover_t;
|
71
|
+
|
72
|
+
/** @brief A structure for sharing data with worker threads for downloading
|
73
|
+
* shards from farmers.
|
74
|
+
*/
|
75
|
+
typedef struct {
|
76
|
+
storj_http_options_t *http_options;
|
77
|
+
char *farmer_id;
|
78
|
+
char *farmer_proto;
|
79
|
+
char *farmer_host;
|
80
|
+
int farmer_port;
|
81
|
+
char *shard_hash;
|
82
|
+
uint32_t pointer_index;
|
83
|
+
char *token;
|
84
|
+
uint64_t start;
|
85
|
+
uint64_t end;
|
86
|
+
uint64_t shard_total_bytes;
|
87
|
+
uv_async_t progress_handle;
|
88
|
+
uint64_t byte_position;
|
89
|
+
/* state should not be modified in worker threads */
|
90
|
+
storj_download_state_t *state;
|
91
|
+
int error_status;
|
92
|
+
bool *canceled;
|
93
|
+
} shard_request_download_t;
|
94
|
+
|
95
|
+
/** @brief A structure for sharing data with worker threads for sending
|
96
|
+
* exchange reports to the bridge.
|
97
|
+
*/
|
98
|
+
typedef struct {
|
99
|
+
uint32_t pointer_index;
|
100
|
+
storj_http_options_t *http_options;
|
101
|
+
storj_bridge_options_t *options;
|
102
|
+
int status_code;
|
103
|
+
storj_exchange_report_t *report;
|
104
|
+
/* state should not be modified in worker threads */
|
105
|
+
storj_download_state_t *state;
|
106
|
+
} shard_send_report_t;
|
107
|
+
|
108
|
+
typedef struct {
|
109
|
+
storj_http_options_t *http_options;
|
110
|
+
storj_bridge_options_t *options;
|
111
|
+
int status_code;
|
112
|
+
const char *bucket_id;
|
113
|
+
const char *file_id;
|
114
|
+
int error_status;
|
115
|
+
storj_file_meta_t *info;
|
116
|
+
/* state should not be modified in worker threads */
|
117
|
+
storj_download_state_t *state;
|
118
|
+
} file_info_request_t;
|
119
|
+
|
120
|
+
/** @brief A structure for sharing data with worker threads for replacing a
|
121
|
+
* pointer with a new farmer.
|
122
|
+
*/
|
123
|
+
typedef struct {
|
124
|
+
storj_http_options_t *http_options;
|
125
|
+
storj_bridge_options_t *options;
|
126
|
+
uint32_t pointer_index;
|
127
|
+
const char *bucket_id;
|
128
|
+
const char *file_id;
|
129
|
+
char *excluded_farmer_ids;
|
130
|
+
/* state should not be modified in worker threads */
|
131
|
+
storj_download_state_t *state;
|
132
|
+
struct json_object *response;
|
133
|
+
int error_status;
|
134
|
+
int status_code;
|
135
|
+
} json_request_replace_pointer_t;
|
136
|
+
|
137
|
+
/** @brief A structure for sharing data with worker threads for making JSON
|
138
|
+
* requests with the bridge.
|
139
|
+
*/
|
140
|
+
typedef struct {
|
141
|
+
storj_http_options_t *http_options;
|
142
|
+
storj_bridge_options_t *options;
|
143
|
+
char *method;
|
144
|
+
char *path;
|
145
|
+
bool auth;
|
146
|
+
struct json_object *body;
|
147
|
+
struct json_object *response;
|
148
|
+
/* state should not be modified in worker threads */
|
149
|
+
storj_download_state_t *state;
|
150
|
+
int status_code;
|
151
|
+
} json_request_download_t;
|
152
|
+
|
153
|
+
/** @brief A method that determines the next work necessary to download a file
|
154
|
+
*
|
155
|
+
* This method is called after each individual work is complete, and will
|
156
|
+
* determine and queue the next set of work that needs to be completed. Once
|
157
|
+
* the file is completely downloaded, it will call the finished callback.
|
158
|
+
*
|
159
|
+
* This method should only be called with in the main loop thread.
|
160
|
+
*/
|
161
|
+
static void queue_next_work(storj_download_state_t *state);
|
162
|
+
|
163
|
+
#endif /* STORJ_DOWNLOADER_H */
|
@@ -0,0 +1,688 @@
|
|
1
|
+
#include "http.h"
|
2
|
+
|
3
|
+
static size_t body_ignore_receive(void *buffer, size_t size, size_t nmemb,
|
4
|
+
void *userp)
|
5
|
+
{
|
6
|
+
size_t buflen = size * nmemb;
|
7
|
+
return buflen;
|
8
|
+
}
|
9
|
+
|
10
|
+
static size_t body_shard_send(void *buffer, size_t size, size_t nmemb,
|
11
|
+
void *userp)
|
12
|
+
{
|
13
|
+
shard_body_send_t *body = userp;
|
14
|
+
|
15
|
+
if (*body->canceled) {
|
16
|
+
return CURL_READFUNC_ABORT;
|
17
|
+
}
|
18
|
+
|
19
|
+
size_t read_bytes = 0;
|
20
|
+
size_t buflen = size * nmemb / AES_BLOCK_SIZE * AES_BLOCK_SIZE;
|
21
|
+
uint8_t clr_txt[buflen];
|
22
|
+
memset_zero(clr_txt, buflen);
|
23
|
+
|
24
|
+
if (buflen > 0) {
|
25
|
+
if (body->remain < buflen) {
|
26
|
+
buflen = body->remain;
|
27
|
+
}
|
28
|
+
|
29
|
+
// Read shard data from file
|
30
|
+
read_bytes = pread(fileno(body->fd), clr_txt, buflen, body->offset + body->total_sent);
|
31
|
+
if (read_bytes == -1) {
|
32
|
+
body->error_code = errno;
|
33
|
+
return CURL_READFUNC_ABORT;
|
34
|
+
}
|
35
|
+
|
36
|
+
if (body->ctx != NULL) {
|
37
|
+
ctr_crypt(body->ctx->ctx, (nettle_cipher_func *)aes256_encrypt,
|
38
|
+
AES_BLOCK_SIZE, body->ctx->encryption_ctr, read_bytes,
|
39
|
+
(uint8_t *)buffer, (uint8_t *)clr_txt);
|
40
|
+
} else {
|
41
|
+
memcpy(buffer, clr_txt, read_bytes);
|
42
|
+
}
|
43
|
+
|
44
|
+
if (ferror(body->fd)) {
|
45
|
+
return CURL_READFUNC_ABORT;
|
46
|
+
}
|
47
|
+
|
48
|
+
body->total_sent += read_bytes;
|
49
|
+
body->bytes_since_progress += read_bytes;
|
50
|
+
|
51
|
+
body->remain -= read_bytes;
|
52
|
+
|
53
|
+
memset_zero(clr_txt, buflen);
|
54
|
+
}
|
55
|
+
|
56
|
+
// give progress updates at set interval
|
57
|
+
if (body->progress_handle && read_bytes > 0 &&
|
58
|
+
(body->bytes_since_progress > SHARD_PROGRESS_INTERVAL ||
|
59
|
+
body->remain == 0)) {
|
60
|
+
|
61
|
+
shard_upload_progress_t *progress = body->progress_handle->data;
|
62
|
+
progress->bytes = body->total_sent;
|
63
|
+
uv_async_send(body->progress_handle);
|
64
|
+
|
65
|
+
body->bytes_since_progress = 0;
|
66
|
+
}
|
67
|
+
|
68
|
+
return read_bytes;
|
69
|
+
}
|
70
|
+
|
71
|
+
int put_shard(storj_http_options_t *http_options,
|
72
|
+
char *farmer_id,
|
73
|
+
char *proto,
|
74
|
+
char *host,
|
75
|
+
int port,
|
76
|
+
char *shard_hash,
|
77
|
+
uint64_t shard_total_bytes,
|
78
|
+
FILE *original_file,
|
79
|
+
uint64_t file_position,
|
80
|
+
storj_encryption_ctx_t *ctx,
|
81
|
+
char *token,
|
82
|
+
int *status_code,
|
83
|
+
int *read_code,
|
84
|
+
uv_async_t *progress_handle,
|
85
|
+
bool *canceled)
|
86
|
+
{
|
87
|
+
int return_code = 0;
|
88
|
+
|
89
|
+
CURL *curl = curl_easy_init();
|
90
|
+
if (!curl) {
|
91
|
+
return 1;
|
92
|
+
}
|
93
|
+
|
94
|
+
char query_args[80];
|
95
|
+
snprintf(query_args, 80, "?token=%s", token);
|
96
|
+
|
97
|
+
int url_len = strlen(proto) + 3 + strlen(host) + 1 + 10 + 8
|
98
|
+
+ strlen(shard_hash) + strlen(query_args);
|
99
|
+
char *url = calloc(url_len + 1, sizeof(char));
|
100
|
+
if (!url) {
|
101
|
+
return 1;
|
102
|
+
}
|
103
|
+
|
104
|
+
snprintf(url, url_len, "%s://%s:%i/shards/%s%s", proto, host, port,
|
105
|
+
shard_hash, query_args);
|
106
|
+
|
107
|
+
curl_easy_setopt(curl, CURLOPT_URL, url);
|
108
|
+
|
109
|
+
curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT,
|
110
|
+
http_options->low_speed_limit);
|
111
|
+
|
112
|
+
curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME,
|
113
|
+
http_options->low_speed_time);
|
114
|
+
|
115
|
+
if (http_options->user_agent) {
|
116
|
+
curl_easy_setopt(curl, CURLOPT_USERAGENT, http_options->user_agent);
|
117
|
+
}
|
118
|
+
|
119
|
+
if (http_options->proxy_url) {
|
120
|
+
curl_easy_setopt(curl, CURLOPT_PROXY, http_options->proxy_url);
|
121
|
+
}
|
122
|
+
|
123
|
+
if (http_options->cainfo_path) {
|
124
|
+
curl_easy_setopt(curl, CURLOPT_CAINFO, http_options->cainfo_path);
|
125
|
+
}
|
126
|
+
|
127
|
+
curl_easy_setopt(curl, CURLOPT_POST, 1);
|
128
|
+
|
129
|
+
struct curl_slist *header_list = NULL;
|
130
|
+
header_list = curl_slist_append(header_list, "Content-Type: application/octet-stream");
|
131
|
+
|
132
|
+
char *header = calloc(17 + 40 + 1, sizeof(char));
|
133
|
+
if (!header) {
|
134
|
+
return 1;
|
135
|
+
}
|
136
|
+
strcat(header, "x-storj-node-id: ");
|
137
|
+
strncat(header, farmer_id, 40);
|
138
|
+
header_list = curl_slist_append(header_list, header);
|
139
|
+
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list);
|
140
|
+
|
141
|
+
shard_body_send_t *shard_body = NULL;
|
142
|
+
|
143
|
+
|
144
|
+
if (original_file && shard_total_bytes) {
|
145
|
+
|
146
|
+
shard_body = malloc(sizeof(shard_body_send_t));
|
147
|
+
if (!shard_body) {
|
148
|
+
return 1;
|
149
|
+
}
|
150
|
+
|
151
|
+
shard_body->fd = original_file;
|
152
|
+
shard_body->offset = file_position;
|
153
|
+
shard_body->ctx = ctx;
|
154
|
+
shard_body->length = shard_total_bytes;
|
155
|
+
shard_body->remain = shard_total_bytes;
|
156
|
+
shard_body->total_sent = 0;
|
157
|
+
shard_body->bytes_since_progress = 0;
|
158
|
+
shard_body->progress_handle = progress_handle;
|
159
|
+
shard_body->canceled = canceled;
|
160
|
+
shard_body->error_code = 0;
|
161
|
+
|
162
|
+
curl_easy_setopt(curl, CURLOPT_READFUNCTION, body_shard_send);
|
163
|
+
curl_easy_setopt(curl, CURLOPT_READDATA, (void *)shard_body);
|
164
|
+
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, (uint64_t)shard_total_bytes);
|
165
|
+
}
|
166
|
+
|
167
|
+
// Ignore any data sent back, we only need to know the status code
|
168
|
+
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, body_ignore_receive);
|
169
|
+
|
170
|
+
int req = curl_easy_perform(curl);
|
171
|
+
|
172
|
+
curl_slist_free_all(header_list);
|
173
|
+
free(header);
|
174
|
+
|
175
|
+
if (*canceled) {
|
176
|
+
return_code = 1;
|
177
|
+
goto clean_up;
|
178
|
+
}
|
179
|
+
|
180
|
+
if (req != CURLE_OK) {
|
181
|
+
return_code = req;
|
182
|
+
goto clean_up;
|
183
|
+
}
|
184
|
+
|
185
|
+
// set the status code
|
186
|
+
if (shard_body && shard_total_bytes) {
|
187
|
+
*read_code = shard_body->error_code;
|
188
|
+
}
|
189
|
+
|
190
|
+
long int _status_code;
|
191
|
+
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &_status_code);
|
192
|
+
*status_code = (int)_status_code;
|
193
|
+
|
194
|
+
// check that total bytes have been sent
|
195
|
+
if (shard_body->total_sent != shard_total_bytes) {
|
196
|
+
return_code = 1;
|
197
|
+
goto clean_up;
|
198
|
+
}
|
199
|
+
|
200
|
+
clean_up:
|
201
|
+
|
202
|
+
// clean up memory
|
203
|
+
if (shard_body) {
|
204
|
+
free(shard_body);
|
205
|
+
}
|
206
|
+
free(url);
|
207
|
+
curl_easy_cleanup(curl);
|
208
|
+
|
209
|
+
return return_code;
|
210
|
+
}
|
211
|
+
|
212
|
+
static size_t body_shard_receive(void *buffer, size_t size, size_t nmemb,
|
213
|
+
void *userp)
|
214
|
+
{
|
215
|
+
size_t buflen = size * nmemb;
|
216
|
+
shard_body_receive_t *body = (shard_body_receive_t *)userp;
|
217
|
+
|
218
|
+
if (*body->canceled) {
|
219
|
+
return CURL_READFUNC_ABORT;
|
220
|
+
}
|
221
|
+
|
222
|
+
if (body->length + body->tail_position + buflen > body->shard_total_bytes) {
|
223
|
+
return CURL_READFUNC_ABORT;
|
224
|
+
}
|
225
|
+
|
226
|
+
// Resize the buffer if necessary
|
227
|
+
if (body->tail_position + buflen > body->tail_length) {
|
228
|
+
body->tail_length = (body->tail_position + buflen) * 2;
|
229
|
+
body->tail = realloc(body->tail, body->tail_length);
|
230
|
+
|
231
|
+
if (!body->tail) {
|
232
|
+
return CURL_READFUNC_ABORT;
|
233
|
+
}
|
234
|
+
}
|
235
|
+
|
236
|
+
// Copy buffer to tail
|
237
|
+
memcpy(body->tail + body->tail_position, buffer, buflen);
|
238
|
+
|
239
|
+
size_t writelen = body->tail_position + buflen;
|
240
|
+
if (body->length + writelen != body->shard_total_bytes) {
|
241
|
+
writelen = (writelen / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
|
242
|
+
}
|
243
|
+
|
244
|
+
// Update the hash
|
245
|
+
sha256_update(body->sha256_ctx, writelen, (uint8_t *)body->tail);
|
246
|
+
|
247
|
+
// Write directly to the file at the correct position
|
248
|
+
if (writelen == pwrite(fileno(body->destination),
|
249
|
+
body->tail,
|
250
|
+
writelen,
|
251
|
+
body->file_position)) {
|
252
|
+
|
253
|
+
if (writelen == -1) {
|
254
|
+
body->error_code = errno;
|
255
|
+
return CURL_READFUNC_ABORT;
|
256
|
+
}
|
257
|
+
|
258
|
+
body->file_position += writelen;
|
259
|
+
} else {
|
260
|
+
// TODO handle error
|
261
|
+
return CURL_READFUNC_ABORT;
|
262
|
+
}
|
263
|
+
|
264
|
+
body->length += writelen;
|
265
|
+
body->bytes_since_progress += writelen;
|
266
|
+
|
267
|
+
// Move any remaining data to the beginning and mark position
|
268
|
+
size_t tailing_size = body->tail_position + buflen - writelen;
|
269
|
+
if (tailing_size > 0) {
|
270
|
+
uint8_t tmp[tailing_size];
|
271
|
+
memcpy(&tmp, body->tail + writelen, tailing_size);
|
272
|
+
memcpy(body->tail, &tmp, tailing_size);
|
273
|
+
body->tail_position = tailing_size;
|
274
|
+
} else {
|
275
|
+
body->tail_position = 0;
|
276
|
+
}
|
277
|
+
|
278
|
+
// Give progress updates at set interval
|
279
|
+
if (body->progress_handle &&
|
280
|
+
body->bytes_since_progress > SHARD_PROGRESS_INTERVAL) {
|
281
|
+
shard_download_progress_t *progress = body->progress_handle->data;
|
282
|
+
progress->bytes = body->length;
|
283
|
+
uv_async_send(body->progress_handle);
|
284
|
+
body->bytes_since_progress = 0;
|
285
|
+
}
|
286
|
+
|
287
|
+
return buflen;
|
288
|
+
}
|
289
|
+
|
290
|
+
/* shard_data must be allocated for shard_total_bytes */
|
291
|
+
int fetch_shard(storj_http_options_t *http_options,
|
292
|
+
char *farmer_id,
|
293
|
+
char *proto,
|
294
|
+
char *host,
|
295
|
+
int port,
|
296
|
+
char *shard_hash,
|
297
|
+
uint64_t shard_total_bytes,
|
298
|
+
char *token,
|
299
|
+
FILE *destination,
|
300
|
+
uint64_t file_position,
|
301
|
+
int *status_code,
|
302
|
+
int *write_code,
|
303
|
+
uv_async_t *progress_handle,
|
304
|
+
bool *canceled)
|
305
|
+
{
|
306
|
+
CURL *curl = curl_easy_init();
|
307
|
+
if (!curl) {
|
308
|
+
return 1;
|
309
|
+
}
|
310
|
+
|
311
|
+
if (http_options->user_agent) {
|
312
|
+
curl_easy_setopt(curl, CURLOPT_USERAGENT, http_options->user_agent);
|
313
|
+
}
|
314
|
+
|
315
|
+
if (http_options->proxy_url) {
|
316
|
+
curl_easy_setopt(curl, CURLOPT_PROXY, http_options->proxy_url);
|
317
|
+
}
|
318
|
+
|
319
|
+
if (http_options->cainfo_path) {
|
320
|
+
curl_easy_setopt(curl, CURLOPT_CAINFO, http_options->cainfo_path);
|
321
|
+
}
|
322
|
+
|
323
|
+
char query_args[80];
|
324
|
+
snprintf(query_args, 80, "?token=%s", token);
|
325
|
+
int url_len = strlen(proto) + 3 + strlen(host) + 1 + 10
|
326
|
+
+ 8 + strlen(shard_hash) + strlen(query_args);
|
327
|
+
char *url = calloc(url_len + 1, sizeof(char));
|
328
|
+
if (!url) {
|
329
|
+
return 1;
|
330
|
+
}
|
331
|
+
snprintf(url, url_len, "%s://%s:%i/shards/%s%s", proto, host, port,
|
332
|
+
shard_hash, query_args);
|
333
|
+
|
334
|
+
curl_easy_setopt(curl, CURLOPT_URL, url);
|
335
|
+
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
|
336
|
+
|
337
|
+
curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT,
|
338
|
+
http_options->low_speed_limit);
|
339
|
+
|
340
|
+
curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME,
|
341
|
+
http_options->low_speed_time);
|
342
|
+
|
343
|
+
// Set the node id header
|
344
|
+
struct curl_slist *node_chunk = NULL;
|
345
|
+
char *header = calloc(17 + 40 + 1, sizeof(char));
|
346
|
+
if (!header) {
|
347
|
+
return 1;
|
348
|
+
}
|
349
|
+
strcat(header, "x-storj-node-id: ");
|
350
|
+
strncat(header, farmer_id, 40);
|
351
|
+
node_chunk = curl_slist_append(node_chunk, header);
|
352
|
+
free(header);
|
353
|
+
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, node_chunk);
|
354
|
+
|
355
|
+
// Set the body handler
|
356
|
+
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, body_shard_receive);
|
357
|
+
shard_body_receive_t *body = malloc(sizeof(shard_body_receive_t));
|
358
|
+
if (!body) {
|
359
|
+
return 1;
|
360
|
+
}
|
361
|
+
|
362
|
+
body->tail = malloc(BUFSIZ);
|
363
|
+
body->tail_length = BUFSIZ;
|
364
|
+
body->tail_position = 0;
|
365
|
+
body->length = 0;
|
366
|
+
body->progress_handle = progress_handle;
|
367
|
+
body->shard_total_bytes = shard_total_bytes;
|
368
|
+
body->bytes_since_progress = 0;
|
369
|
+
body->canceled = canceled;
|
370
|
+
body->sha256_ctx = malloc(sizeof(struct sha256_ctx));
|
371
|
+
body->error_code = 0;
|
372
|
+
if (!body->sha256_ctx) {
|
373
|
+
return 1;
|
374
|
+
}
|
375
|
+
sha256_init(body->sha256_ctx);
|
376
|
+
|
377
|
+
body->destination = destination;
|
378
|
+
body->file_position = file_position;
|
379
|
+
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)body);
|
380
|
+
|
381
|
+
int req = curl_easy_perform(curl);
|
382
|
+
|
383
|
+
curl_slist_free_all(node_chunk);
|
384
|
+
free(body->tail);
|
385
|
+
|
386
|
+
// set the status code
|
387
|
+
if (body) {
|
388
|
+
*write_code = body->error_code;
|
389
|
+
}
|
390
|
+
|
391
|
+
|
392
|
+
int error_code = 0;
|
393
|
+
if (req != CURLE_OK) {
|
394
|
+
// TODO include the actual http error code
|
395
|
+
error_code = STORJ_FARMER_REQUEST_ERROR;
|
396
|
+
}
|
397
|
+
|
398
|
+
// set the status code
|
399
|
+
long int _status_code;
|
400
|
+
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &_status_code);
|
401
|
+
*status_code = (int)_status_code;
|
402
|
+
|
403
|
+
curl_easy_cleanup(curl);
|
404
|
+
|
405
|
+
free(url);
|
406
|
+
|
407
|
+
if (error_code) {
|
408
|
+
free(body->sha256_ctx);
|
409
|
+
free(body);
|
410
|
+
return error_code;
|
411
|
+
}
|
412
|
+
|
413
|
+
if (body->length != shard_total_bytes) {
|
414
|
+
free(body->sha256_ctx);
|
415
|
+
free(body);
|
416
|
+
return STORJ_FARMER_INTEGRITY_ERROR;
|
417
|
+
}
|
418
|
+
|
419
|
+
uint8_t *hash_sha256 = calloc(SHA256_DIGEST_SIZE, sizeof(uint8_t));
|
420
|
+
if (!hash_sha256) {
|
421
|
+
return 1;
|
422
|
+
}
|
423
|
+
sha256_digest(body->sha256_ctx, SHA256_DIGEST_SIZE, hash_sha256);
|
424
|
+
|
425
|
+
struct ripemd160_ctx rctx;
|
426
|
+
ripemd160_init(&rctx);
|
427
|
+
ripemd160_update(&rctx, SHA256_DIGEST_SIZE, hash_sha256);
|
428
|
+
|
429
|
+
free(hash_sha256);
|
430
|
+
|
431
|
+
uint8_t *hash_rmd160 = calloc(RIPEMD160_DIGEST_SIZE + 1, sizeof(uint8_t));
|
432
|
+
if (!hash_rmd160) {
|
433
|
+
return 1;
|
434
|
+
}
|
435
|
+
ripemd160_digest(&rctx, RIPEMD160_DIGEST_SIZE, hash_rmd160);
|
436
|
+
|
437
|
+
char *hash = calloc(RIPEMD160_DIGEST_SIZE * 2 + 1, sizeof(char));
|
438
|
+
if (!hash) {
|
439
|
+
return 1;
|
440
|
+
}
|
441
|
+
for (unsigned i = 0; i < RIPEMD160_DIGEST_SIZE; i++) {
|
442
|
+
sprintf(&hash[i*2], "%02x", hash_rmd160[i]);
|
443
|
+
}
|
444
|
+
|
445
|
+
free(body->sha256_ctx);
|
446
|
+
free(body);
|
447
|
+
free(hash_rmd160);
|
448
|
+
|
449
|
+
if (strcmp(shard_hash, hash) != 0) {
|
450
|
+
error_code = STORJ_FARMER_INTEGRITY_ERROR;
|
451
|
+
}
|
452
|
+
|
453
|
+
free(hash);
|
454
|
+
|
455
|
+
if (error_code) {
|
456
|
+
return error_code;
|
457
|
+
}
|
458
|
+
|
459
|
+
// final progress update
|
460
|
+
if (progress_handle) {
|
461
|
+
shard_download_progress_t *progress = progress_handle->data;
|
462
|
+
progress->bytes = shard_total_bytes;
|
463
|
+
uv_async_send(progress_handle);
|
464
|
+
}
|
465
|
+
|
466
|
+
return 0;
|
467
|
+
}
|
468
|
+
|
469
|
+
static size_t body_json_send(void *buffer, size_t size, size_t nmemb,
|
470
|
+
void *userp)
|
471
|
+
{
|
472
|
+
http_body_send_t *body = (http_body_send_t *)userp;
|
473
|
+
|
474
|
+
size_t buflen = size * nmemb;
|
475
|
+
|
476
|
+
if (buflen > 0) {
|
477
|
+
if (body->remain < buflen) {
|
478
|
+
buflen = body->remain;
|
479
|
+
}
|
480
|
+
memcpy(buffer, body->pnt, buflen);
|
481
|
+
|
482
|
+
body->pnt += buflen;
|
483
|
+
body->remain -= buflen;
|
484
|
+
}
|
485
|
+
|
486
|
+
return buflen;
|
487
|
+
}
|
488
|
+
|
489
|
+
static size_t body_json_receive(void *buffer, size_t size, size_t nmemb,
|
490
|
+
void *userp)
|
491
|
+
{
|
492
|
+
size_t buflen = size * nmemb;
|
493
|
+
http_body_receive_t *body = (http_body_receive_t *)userp;
|
494
|
+
|
495
|
+
body->data = realloc(body->data, body->length + buflen + 1);
|
496
|
+
if (body->data == NULL) {
|
497
|
+
return 0;
|
498
|
+
}
|
499
|
+
|
500
|
+
memcpy(&(body->data[body->length]), buffer, buflen);
|
501
|
+
|
502
|
+
body->length += buflen;
|
503
|
+
body->data[body->length] = 0;
|
504
|
+
|
505
|
+
return buflen;
|
506
|
+
}
|
507
|
+
|
508
|
+
int fetch_json(storj_http_options_t *http_options,
|
509
|
+
storj_bridge_options_t *options,
|
510
|
+
char *method,
|
511
|
+
char *path,
|
512
|
+
struct json_object *request_body,
|
513
|
+
bool auth,
|
514
|
+
struct json_object **response,
|
515
|
+
int *status_code)
|
516
|
+
{
|
517
|
+
CURL *curl = curl_easy_init();
|
518
|
+
if (!curl) {
|
519
|
+
return 1;
|
520
|
+
}
|
521
|
+
|
522
|
+
// curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
|
523
|
+
|
524
|
+
char *user_pass = NULL;
|
525
|
+
|
526
|
+
// Set the url
|
527
|
+
int url_len = strlen(options->proto) + 3 + strlen(options->host) +
|
528
|
+
1 + 10 + strlen(path);
|
529
|
+
char *url = calloc(url_len + 1, sizeof(char));
|
530
|
+
if (!url) {
|
531
|
+
return 1;
|
532
|
+
}
|
533
|
+
|
534
|
+
snprintf(url, url_len, "%s://%s:%i%s", options->proto, options->host,
|
535
|
+
options->port, path);
|
536
|
+
curl_easy_setopt(curl, CURLOPT_URL, url);
|
537
|
+
|
538
|
+
// Set the user agent
|
539
|
+
if (http_options->user_agent) {
|
540
|
+
curl_easy_setopt(curl, CURLOPT_USERAGENT, http_options->user_agent);
|
541
|
+
}
|
542
|
+
|
543
|
+
// Set the HTTP method
|
544
|
+
if (0 == strcmp(method, "PUT")) {
|
545
|
+
curl_easy_setopt(curl, CURLOPT_PUT, 1);
|
546
|
+
} else if (0 == strcmp(method, "POST")) {
|
547
|
+
curl_easy_setopt(curl, CURLOPT_POST, 1);
|
548
|
+
} else if (0 == strcmp(method, "GET")) {
|
549
|
+
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
|
550
|
+
} else if (0 == strcmp(method, "DELETE")) {
|
551
|
+
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
|
552
|
+
} else {
|
553
|
+
return 1;
|
554
|
+
}
|
555
|
+
|
556
|
+
// Set the proxy
|
557
|
+
if (http_options->proxy_url) {
|
558
|
+
curl_easy_setopt(curl, CURLOPT_PROXY, http_options->proxy_url);
|
559
|
+
}
|
560
|
+
|
561
|
+
// Set the path to the Certificate Authority (CA) bundle
|
562
|
+
if (http_options->cainfo_path) {
|
563
|
+
curl_easy_setopt(curl, CURLOPT_CAINFO, http_options->cainfo_path);
|
564
|
+
}
|
565
|
+
|
566
|
+
// Set the timeout
|
567
|
+
curl_easy_setopt(curl, CURLOPT_TIMEOUT, http_options->timeout);
|
568
|
+
|
569
|
+
// Setup the body handler
|
570
|
+
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, body_json_receive);
|
571
|
+
http_body_receive_t *body = malloc(sizeof(http_body_receive_t));
|
572
|
+
if (!body) {
|
573
|
+
return 1;
|
574
|
+
}
|
575
|
+
body->data = NULL;
|
576
|
+
body->length = 0;
|
577
|
+
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)body);
|
578
|
+
|
579
|
+
// Include authentication headers if info is provided
|
580
|
+
if (auth && options->user && options->pass) {
|
581
|
+
|
582
|
+
// Hash password
|
583
|
+
uint8_t *pass_hash = calloc(SHA256_DIGEST_SIZE, sizeof(uint8_t));
|
584
|
+
if (!pass_hash) {
|
585
|
+
return 1;
|
586
|
+
}
|
587
|
+
char *pass = calloc(SHA256_DIGEST_SIZE * 2 + 1, sizeof(char));
|
588
|
+
if (!pass) {
|
589
|
+
return 1;
|
590
|
+
}
|
591
|
+
struct sha256_ctx ctx;
|
592
|
+
sha256_init(&ctx);
|
593
|
+
sha256_update(&ctx, strlen(options->pass), (uint8_t *)options->pass);
|
594
|
+
sha256_digest(&ctx, SHA256_DIGEST_SIZE, pass_hash);
|
595
|
+
for (unsigned i = 0; i < SHA256_DIGEST_SIZE; i++) {
|
596
|
+
sprintf(&pass[i*2], "%02x", pass_hash[i]);
|
597
|
+
}
|
598
|
+
|
599
|
+
free(pass_hash);
|
600
|
+
|
601
|
+
int user_pass_len = strlen(options->user) + 1 + strlen(pass);
|
602
|
+
user_pass = calloc(user_pass_len + 1, sizeof(char));
|
603
|
+
if (!user_pass) {
|
604
|
+
return 1;
|
605
|
+
}
|
606
|
+
strcat(user_pass, options->user);
|
607
|
+
strcat(user_pass, ":");
|
608
|
+
strcat(user_pass, pass);
|
609
|
+
|
610
|
+
free(pass);
|
611
|
+
|
612
|
+
curl_easy_setopt(curl, CURLOPT_USERPWD, user_pass);
|
613
|
+
|
614
|
+
}
|
615
|
+
|
616
|
+
struct curl_slist *header_list = NULL;
|
617
|
+
|
618
|
+
// Include body if request body json is provided
|
619
|
+
http_body_send_t *post_body = NULL;
|
620
|
+
const char *req_buf = NULL;
|
621
|
+
if (request_body) {
|
622
|
+
req_buf = json_object_to_json_string(request_body);
|
623
|
+
|
624
|
+
header_list = curl_slist_append(header_list,
|
625
|
+
"Content-Type: application/json");
|
626
|
+
|
627
|
+
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list);
|
628
|
+
|
629
|
+
post_body = malloc(sizeof(http_body_send_t));
|
630
|
+
if (!post_body) {
|
631
|
+
return 1;
|
632
|
+
}
|
633
|
+
post_body->pnt = (char *)req_buf;
|
634
|
+
post_body->remain = strlen(req_buf);
|
635
|
+
|
636
|
+
curl_easy_setopt(curl, CURLOPT_READFUNCTION, body_json_send);
|
637
|
+
curl_easy_setopt(curl, CURLOPT_READDATA, (void *)post_body);
|
638
|
+
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, (uint64_t)strlen(req_buf));
|
639
|
+
} else {
|
640
|
+
header_list = curl_slist_append(header_list, "Content-Length: 0");
|
641
|
+
header_list = curl_slist_append(header_list, "Content-Type: application/json");
|
642
|
+
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list);
|
643
|
+
}
|
644
|
+
|
645
|
+
int ret = 0;
|
646
|
+
int req = curl_easy_perform(curl);
|
647
|
+
|
648
|
+
free(url);
|
649
|
+
|
650
|
+
if (header_list) {
|
651
|
+
curl_slist_free_all(header_list);
|
652
|
+
}
|
653
|
+
|
654
|
+
if (post_body) {
|
655
|
+
free(post_body);
|
656
|
+
}
|
657
|
+
|
658
|
+
if (user_pass) {
|
659
|
+
free(user_pass);
|
660
|
+
}
|
661
|
+
|
662
|
+
*response = NULL;
|
663
|
+
|
664
|
+
if (req != CURLE_OK) {
|
665
|
+
ret = req;
|
666
|
+
goto cleanup;
|
667
|
+
}
|
668
|
+
|
669
|
+
// set the status code
|
670
|
+
long int _status_code;
|
671
|
+
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &_status_code);
|
672
|
+
*status_code = (int)_status_code;
|
673
|
+
|
674
|
+
if (body->data && body->length > 0) {
|
675
|
+
*response = json_tokener_parse((char *)body->data);
|
676
|
+
}
|
677
|
+
|
678
|
+
cleanup:
|
679
|
+
curl_easy_cleanup(curl);
|
680
|
+
if (body->data) {
|
681
|
+
free(body->data);
|
682
|
+
}
|
683
|
+
if (body) {
|
684
|
+
free(body);
|
685
|
+
}
|
686
|
+
|
687
|
+
return ret;
|
688
|
+
}
|