-
Notifications
You must be signed in to change notification settings - Fork 0
/
xcache.cc
400 lines (372 loc) · 12 KB
/
xcache.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
#include "localconfig.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
extern "C" {
#include "picoquic.h"
#include "picosocks.h"
#include "util.h"
};
#include "xiaapi.hpp"
#include "dagaddr.hpp"
#define SERVER_CERT_FILE "certs/cert.pem"
#define SERVER_KEY_FILE "certs/key.pem"
#define CONFFILE "local.conf"
#define XCACHE_AID "XCACHE_AID"
#define TEST_CID "TEST_CID"
void print_address(struct sockaddr* address, char* label)
{
char hostname[256];
if(address->sa_family == AF_XIA) {
sockaddr_x* addr = (sockaddr_x*) address;
Graph dag(addr);
std::cout << std::string(label) << " "
<< dag.dag_string() << std::endl;
} else {
std::cout << "Invalid address - expected XIA" << std::endl;
}
return;
}
typedef struct {
int stream_open; // Assuming just one stream for now
int received_so_far; // Number of bytes received in that one stream
uint8_t* data;
size_t datalen;
size_t sent_offset;
NodePtr xid;
} callback_context_t;
int buildDataToSend(callback_context_t* ctx, size_t datalen)
{
ctx->data = (uint8_t*) malloc(datalen);
if (ctx->data == NULL) {
return -1;
}
memset(ctx->data, 0xf5, datalen);
return 0;
}
void cleanupData(uint8_t* data)
{
if (data) {
free (data);
}
}
static int sendData(picoquic_cnx_t* connection,
uint64_t stream_id, callback_context_t* ctx)
{
if (ctx->data == NULL) {
if ( buildDataToSend(ctx, 8192) ) {
printf("ERROR creating data buffer to send\n");
return -1;
}
ctx->datalen = 8192;
ctx->sent_offset = 0;
}
if(ctx->sent_offset == 0) {
int ret = picoquic_add_to_stream(connection, stream_id,
ctx->data, ctx->datalen, 1);
//if(picoquic_add_to_stream(connection, stream_id,
//ctx->data, ctx->datalen, 1)) {
if(ret != 0) {
printf("ERROR: queuing data to send. Returned %d\n", ret);
return -1;
}
ctx->sent_offset = ctx->datalen;
}
}
static int server_callback(picoquic_cnx_t* connection,
uint64_t stream_id, uint8_t* bytes, size_t length,
picoquic_call_back_event_t event, void* ctx)
{
printf("ServerCallback: stream: %lu, len: %zu, event: %d\n",
stream_id, length, event);
callback_context_t* context = (callback_context_t*)ctx;
switch(event) {
case picoquic_callback_ready:
printf("ServerCallback: Ready\n");
printf("ServerCallback: Sending chunk: %s\n",
context->xid->to_string().c_str());
//sendData(connection, stream_id, context);
break;
case picoquic_callback_almost_ready:
printf("ServerCallback: AlmostReady\n");
printf("ServerCallback: Will send chunk: %s\n",
context->xid->to_string().c_str());
//sendData(connection, stream_id, context);
break;
// Handle the connection related events
case picoquic_callback_close:
printf("ServerCallback: Close\n");
case picoquic_callback_application_close:
printf("ServerCallback: ApplicationClose\n");
case picoquic_callback_stateless_reset:
printf("ServerCallback: StatelessReset\n");
if(context != NULL) {
// Free the context memory and set it NULL for callback
delete context;
picoquic_set_callback(connection, server_callback, NULL);
printf("ServerCallback: need to free context\n");
}
return 0;
// Handle the stream related events
case picoquic_callback_prepare_to_send:
// Unexpected call
printf("ServerCallback: PrepareToSend\n");
return -1;
case picoquic_callback_stop_sending:
printf("ServerCallback: StopSending: resetting stream\n");
picoquic_reset_stream(connection, stream_id, 0);
return 0;
case picoquic_callback_stream_reset:
printf("ServerCallback: StreamReset: resetting stream\n");
picoquic_reset_stream(connection, stream_id, 0);
return 0;
case picoquic_callback_stream_gap:
printf("ServerCallback: StreamGap\n");
// This is not supported by picoquic yet
picoquic_reset_stream(connection, stream_id,
PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION);
return 0;
case picoquic_callback_stream_data:
printf("ServerCallback: StreamData\n");
case picoquic_callback_stream_fin:
printf("ServerCallback: StreamFin\n");
if(event == picoquic_callback_stream_fin && length == 0) {
printf("ServerCallback: StreamFin - resetting!\n");
picoquic_reset_stream(connection, stream_id,
PICOQUIC_TRANSPORT_STREAM_STATE_ERROR);
return 0;
}
// If there's to context, create one
if(!context) {
printf("ServerCallback: ERROR callback without context\n");
break;
} else {
if(length > 0) {
//printf("ERROR: data from client but none expected\n");
char data[length+1];
memcpy(data, bytes, length);
data[length] = 0;
printf("ServerCallback: Client sent: %s\n", data);
context->received_so_far += length;
// Send it back to client and FIN the stream
//(void)picoquic_add_to_stream(connection, stream_id,
//(uint8_t*)data, length, 1);
sendData(connection, stream_id, context);
}
}
if(event != picoquic_callback_stream_data) {
printf("ServerCallback: StreamFin\n");
printf("ServerCallback: Client sent %d bytes before ending\n",
context->received_so_far);
}
break;
};
return 0;
}
int main()
{
int retval = -1;
int state = 0;
FILE* logfile = NULL;
uint64_t current_time;
picoquic_quic_t* server = NULL;
int64_t delay_max = 10000000; // max wait 10 sec.
sockaddr_x addr_from;
sockaddr_x addr_local;
unsigned long to_interface = 0; // our interface
uint8_t buffer[1536]; // buffer to receive packets
int bytes_recv; // size of packet received
picoquic_cnx_t* newest_cnx = NULL;
picoquic_cnx_t* next_connection = NULL;
uint8_t send_buffer[1536];
size_t send_length = 0;
unsigned char received_ecn;
GraphPtr my_addr;
GraphPtr dummy_cid_addr;
int sockfd = -1;
auto conf = LocalConfig::get_instance(CONFFILE);
auto xcache_aid = conf.get(XCACHE_AID);
auto test_cid = conf.get(TEST_CID);
if (xcache_aid.size() == 0) {
printf("ERROR: XCACHE_AID entry missing in %s\n", CONFFILE);
return -1;
}
if (test_cid.size() == 0) {
printf("ERROR: TEST_CID entry missing in %s\n", CONFFILE);
return -1;
}
// We give a fictitious AID for now, and get a dag in my_addr
sockfd = picoquic_xia_open_server_socket(xcache_aid.c_str(), my_addr);
if(sockfd == -1) {
printf("ERROR creating xia server socket\n");
return -1;
} else {
printf("SUCCESS creating xia server socket\n");
}
state = 1; // server socket now exists
// Ask router to send requests for our dummy CID to us
if(picoquic_xia_serve_cid(sockfd, test_cid.c_str(), dummy_cid_addr)) {
printf("ERROR setting up routes for our dummy CID\n");
return -1;
}
// Get the server certificate
char server_cert_file[512];
if(picoquic_get_input_path(server_cert_file, sizeof(server_cert_file),
NULL, SERVER_CERT_FILE)) {
printf("ERROR finding server certificate\n");
goto server_done;
}
// Get the server crypto key
char server_key_file[512];
if(picoquic_get_input_path(server_key_file, sizeof(server_key_file),
NULL, SERVER_KEY_FILE)) {
printf("ERROR finding server key file\n");
goto server_done;
}
// Create QUIC instance
current_time = picoquic_current_time();
server = picoquic_create(8, // number of connections
server_cert_file,
server_key_file,
NULL, // cert_root_file_name
NULL, // Appl. Layer Protocol Negotiation (ALPN)
server_callback, // Stream data callback
NULL, // Stream data context - assigned in callback
NULL, // Connection ID callback
NULL, // Connection ID callback context
NULL, // reset seed
current_time,
NULL, // p_simulated_time
NULL, // ticket_file_name
NULL, // ticket_encryption_key
0 // ticket encryption key length
);
if(server == NULL) {
printf("ERROR creating QUIC instance for server\n");
goto server_done;
}
state = 2;
// Open a log file
logfile = fopen("server.log", "w");
if(logfile == NULL) {
printf("ERROR creating log file\n");
goto server_done;
}
state = 3;
PICOQUIC_SET_LOG(server, logfile);
// Wait for packets
while(1) {
int64_t delta_t = picoquic_get_next_wake_delay(server, current_time,
delay_max);
bytes_recv = picoquic_xia_select(sockfd, &addr_from,
&addr_local, buffer, sizeof(buffer),
delta_t,
¤t_time);
if(bytes_recv < 0) {
printf("Server: ERROR selecting on client requests\n");
goto server_done;
}
uint64_t loop_time;
if(bytes_recv > 0) {
// Process the incoming packet via QUIC server
printf("Server: got %d bytes from client\n", bytes_recv);
Graph sender_addr(&addr_from);
Graph our_addr(&addr_local);
printf("Server: sender: %s\n", sender_addr.dag_string().c_str());
printf("Server: us: %s\n", our_addr.dag_string().c_str());
//char label[] = "Server: client addr:";
//print_address((struct sockaddr*)&addr_from, label);
(void)picoquic_incoming_packet(server, buffer,
(size_t)bytes_recv, (struct sockaddr*)&addr_from,
(struct sockaddr*)&addr_local, to_interface,
received_ecn,
current_time);
//printf("Server: processed incoming packet through QUIC\n");
//print_address((struct sockaddr*)&addr_from, label);
//char label2[] = "Server: server addr:";
//print_address((struct sockaddr*)&addr_local, label2);
// If we don't have a list of server connections, get it
if(newest_cnx == NULL
|| newest_cnx != picoquic_get_first_cnx(server)) {
printf("Server: New connection\n");
newest_cnx = picoquic_get_first_cnx(server);
if(newest_cnx == NULL) {
printf("ERROR: No connection found!\n");
goto server_done;
}
// Let's create a context with intent XID
auto ctx = new callback_context_t();
ctx->xid.reset(new Node(our_addr.intent_CID_str()));
picoquic_set_callback(newest_cnx, server_callback, ctx);
printf("Server: Connection state = %d\n",
picoquic_get_cnx_state(newest_cnx));
}
}
loop_time = current_time;
// Send stateless packets
picoquic_stateless_packet_t* sp;
while((sp = picoquic_dequeue_stateless_packet(server)) != NULL) {
printf("Server: found a stateless packet to send\n");
if(sp->addr_to.ss_family != AF_XIA) {
std::cout << "ERROR: Non XIA stateless packet" << std::endl;
break;
}
// send out any outstanding stateless packets
printf("Server: sending stateless packet out on network\n");
picoquic_xia_sendmsg(sockfd, sp->bytes, sp->length,
&sp->addr_to, &sp->addr_local);
picoquic_delete_stateless_packet(sp);
}
// Send outgoing packets for all connections
while((next_connection = picoquic_get_earliest_cnx_to_wake(server,
loop_time)) != NULL) {
int peer_addr_len = sizeof(sockaddr_x);
int local_addr_len = sizeof(sockaddr_x);
// Ask QUIC to prepare a packet to send out on this connection
//
// TODO: HACK!!! peer and local addr pointers sent as
// sockaddr_storage so underlying code won't complain.
// Fix would require changes to picoquic which we want to avoid
int rc = picoquic_prepare_packet(next_connection, current_time,
send_buffer, sizeof(send_buffer), &send_length,
(struct sockaddr_storage*) &addr_from, &peer_addr_len,
(struct sockaddr_storage*) &addr_local, &local_addr_len);
if(rc == PICOQUIC_ERROR_DISCONNECTED) {
// Connections list is empty, if this was the last connection
if(next_connection == newest_cnx) {
newest_cnx = NULL;
}
printf("Server: Disconnected!\n");
picoquic_delete_cnx(next_connection);
// All connections ended, break out of outgoing packets loop
break;
}
if(rc == 0) {
if(send_length > 0) {
printf("Server: sending %ld byte packet\n", send_length);
(void)picoquic_xia_sendmsg(sockfd,
send_buffer, send_length,
&addr_from, &addr_local);
}
} else {
printf("Server: Exiting outgoing pkts loop. rc=%d\n", rc);
break;
}
}
}
// Server ended cleanly, change return code to success
retval = 0;
server_done:
switch(state) {
case 3: // close the log file
fclose(logfile);
case 2: // cleanup QUIC instance
picoquic_free(server);
case 1: // cleanup server sockets
if(sockfd != -1) {
close(sockfd);
}
};
return retval;
}