Newer
Older
#include <pthread.h>
#include <assert.h>
#include <getopt.h>
#include "common.h"
#include <signal.h>
#include <unistd.h>
#include <openssl/err.h>
#include <stdbool.h>
#include "cJSON.h"
#ifdef DEBUG
#define DEBUG_PRINT(...) do{ fprintf( stdout, __VA_ARGS__ ); } while( false )
#else
#define DEBUG_PRINT(...) do{ } while ( false )
#endif
const char *USAGE="client [-v | -V logfile ] [ -k keylog ] [-c context_list] [-s server] [-m middlebox_list | -M middlebox_list_file]\n"
"\tcontext list must be comma-separated list of context names, defaults to \"header,body\"\n"
"\tserver must be url including port, default 127.0.0.1:" xstr(DEFAULT_SERVER_PORT) "\n"
"\tmiddlebox list must semi-colon delimited urls including port,"
"\tmiddlebox list_file must be a file with middlebox urls on separate lines,"
"\t\tdefault \"127.0.0.1:" xstr(DEFAULT_MBOX_PORT) "\n"
"Input to send to the server is taken from stdin in lines of the form context:command\n";
FILE *vlog;
FILE *fIn;
FILE *fOut;
SSL *pSSL;
int numContexts;
const char *contextDescs[50];
int numMiddleboxes;
const char *MiddleboxURLs[50];
int middleboxReadRights[50][50]; // This is horrible
int middleboxWriteRights[50][50]; // This is horrible and has a silly name
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
const char *serverURL;
char myKeyFile[1024];
char myPassword[1024];
char caFile[1024];
} ClientConfig;
static int
client_ParseArgs(int iArgC, char *papsArgV[], ClientConfig *ptCConf);
void
client_SetDefaults(ClientConfig *ptC)
{
memset(ptC,0,sizeof(*ptC));
ptC->fIn=stdin;
ptC->fOut=stdout;
strcpy(ptC->caFile,"ca.pem");
strcpy(ptC->myKeyFile,"client.pem");
strcpy(ptC->myPassword,"password");
ptC->serverURL="127.0.0.1:" xstr(DEFAULT_SERVER_PORT);
}
extern char *optarg;
//the next two are available but currently ununsed,
//commented out to prevent compiler complaining
//extern int optind;
//extern int optopt;
static int
client_ParseArgs(int iArgC, char *papsArgV[], ClientConfig *ptCConf)
{
int iStatus=0;
char carg=0;
while( iStatus==0 && (carg=getopt(iArgC,papsArgV,ARG_TYPES)) != -1) {
switch(carg) {
case 'v':
case 'V':
if(ptCConf->vlog!=NULL) {
COMMON_LogErrorAndExit(iStatus=INVALID_COMMAND_LINE_ARGS,stderr,"verbose logging specified multiple times\n%s",USAGE);
}
if(carg=='v') {
ptCConf->vlog=stdout;
} else { /*carg=='V'*/
ptCConf->vlog=fopen(optarg,"w");
if(ptCConf->vlog==NULL) {
COMMON_LogErrorAndExit(iStatus=FILE_NOT_WRITABLE,stderr,"Unable to open log file %s",optarg);
}
}
break;
case 's':
/*todo*/
assert(0);
break;
case 'k':
/*todo*/
assert(0);
break;
case 'c':
/*todo*/
assert(0);
break;
case 'm':
/*todo*/
assert(0);
break;
case 'M':
/*todo*/
assert(0);
break;
default:
COMMON_LogErrorAndExit(iStatus=INVALID_COMMAND_LINE_ARGS,stderr,"Unrecognised command line option -%c",carg);
break; /*never reached - keeps compiler happy*/
}
}
return iStatus;
}
ERROR_STATUS
client_CreateContext(SSL_CTX **pptSSLctx,const ClientConfig *ptConf)
{
ERROR_STATUS iStatus;
iStatus=COMMON_InitializeSSLCtx(pptSSLctx,
ptConf->myKeyFile, ptConf->myPassword,
ptConf->caFile,
ID_CLIENT);
return iStatus;
}
ERROR_STATUS
client_Connect(SSL_CTX *ptSSLctx, ClientConfig *ptConf)
{
ERROR_STATUS iStatus=SUCCESS;
int i,j,p;
BIO *ptSBio;
/*step 0 - create SSL structure*/
/*the SSL_CTX already has out keys set up*/
ptConf->pSSL=SSL_new(ptSSLctx);
/*todo - check for NULL return*/
assert(ptConf->pSSL!=NULL);
/*step 1 - create the contexts*/
iStatus=COMMON_InitMulticontextSet(ptConf->pSSL);
assert(iStatus==SUCCESS);
if(ptConf->numContexts==0)
{
DEBUG_PRINT("[CLIENT DEBUG] No contexts specified, adding default\n");
iStatus=COMMON_AppendContext(ptConf->pSSL,"default");
assert(iStatus==SUCCESS);
}
else
{
DEBUG_PRINT("[CLIENT DEBUG] Appending %d context\n", ptConf->numContexts);
/*Iterate through the context descriptions and create each one*/
for(i=0;i!=ptConf->numContexts && iStatus==SUCCESS;i++)
{
DEBUG_PRINT(" [CLIENT DEBUG] Appending context %s\n", ptConf->contextDescs[i]);
iStatus=COMMON_AppendContext(ptConf->pSSL,ptConf->contextDescs[i]);
/*TODO*/
assert(0);
}
/*step 2 - create the middlebox list*/
iStatus=COMMON_InitProxySet(ptConf->pSSL);
assert(iStatus==SUCCESS);
/*step 2.0? - do we need to insert client as middlebox 0? - TODO confirm*/
/*step 2.1 - iterate through middlebox list */
DEBUG_PRINT("[CLIENT DEBUG] Appending %d middleboxes\n", ptConf->numMiddleboxes );
for(i=0;i!=ptConf->numMiddleboxes;i++)
{
DEBUG_PRINT(" [CLIENT DEBUG] Appending middlbox %d (%s)\n", i, ptConf->MiddleboxURLs[i]);
iStatus=COMMON_AppendProxy(ptConf->pSSL,ptConf->MiddleboxURLs[i]);
}
/*step 2.2 - add server url*/
DEBUG_PRINT("[CLIENT DEBUG] Setting server URL { %s }\n", ptConf->serverURL);
iStatus=COMMON_SetServer(ptConf->pSSL,ptConf->serverURL);
DEBUG_PRINT("[CLIENT DEBUG] Setting middlebox access permissions\n");
for(j=0;j!=ptConf->numContexts;j++)
{
DEBUG_PRINT(" [CLIENT DEBUG] Setting permissions for slice %d (%s)\n", ptConf->pSSL->slices[j]->slice_id,ptConf->pSSL->slices[j]->purpose);
//DEBUG_PRINT(" [CLIENT DEBUG] Setting full permissions for server (%d)\n", ptConf->numMiddleboxes-1);
//iStatus=COMMON_SetProxyAccessPermissionByID(ptConf->pSSL,ptConf->pSSL->slices[j]->slice_id, ptConf->numMiddleboxes-1,
// 1,1);
for(i=0;i!=ptConf->numMiddleboxes/*-1*/;i++)
{ /*change to i=1 if client included*/
char *sMBoxUrl=ptConf->pSSL->proxies[i]->address;
char *sPort=strchr(sMBoxUrl,':');
int writePermission = ptConf->middleboxWriteRights[i][j];
int readPermission = ptConf->middleboxReadRights[i][j];
assert(sPort);
sPort++;
int iPort=atoi(sPort);
DEBUG_PRINT(" [CLIENT DEBUG] Setting permissions for middlebox %d (%s) R = %d W = %d\n", i, sMBoxUrl, readPermission, writePermission);
iStatus=COMMON_SetProxyAccessPermissionByID(ptConf->pSSL,ptConf->pSSL->slices[j]->slice_id, i,
#ifdef DEBUG
printf ("----------------------------\n");
for (int q = 0; q < ptConf->pSSL->proxies_len; q++)
{
printf ("Proxy %d: \n", q);
printf (" WriteSliceIDs: \n");
for (int p = 0; p < ptConf->pSSL->proxies[0]->write_slice_ids_len; p++)
{
printf (" [%d]: %d\n", p, ptConf->pSSL->proxies[0]->write_slice_ids[p]);
}
printf (" ReadSliceISs: \n");
for (int p = 0; p < ptConf->pSSL->proxies[0]->read_slice_ids_len; p++)
{
printf (" [%d]: %d\n", p, ptConf->pSSL->proxies[0]->read_slice_ids[p]);
}
}
printf ("----------------------------\n");
#endif
/*step 4? - do we need to create a socket to first middlebox?*/
// TCP Connect
char* sAddress = (char*)malloc(strlen(ptConf->pSSL->proxies[0]->address) + 1); // Large enough for string+\0
memcpy(sAddress, ptConf->pSSL->proxies[0]->address, strlen(ptConf->pSSL->proxies[0]->address) + 1);
char *sHost = strtok(sAddress, ":");
int iPort = atoi(strtok(NULL, ":"));
int iSock;
COMMON_TcpConnect(&iSock, sHost, iPort);
// Connect TCP socket to SSL socket
ptSBio = BIO_new_socket(iSock, BIO_NOCLOSE);
SSL_set_bio(ptConf->pSSL, ptSBio, ptSBio);
/*step 5 - call the connect method*/
/*SPP connect calls this method but also adds some checks,
* in particular it checks that there is at least one context (fine)
* but also checks that there is at least one proxy (which seems
* unnecassary but the server is included so there must be one)
*/
if (SSL_connect(ptConf->pSSL) <= 0) {
iStatus=NETWORK_CONNECT_FAIL;
}
X509* client_cert = SSL_get_certificate(ptConf->pSSL);
printf ("[CLIENT] Client certificate:\n");
COMMON_PrintCertificateDetails(client_cert);
X509_free(client_cert);
X509* server_cert = SSL_get_peer_certificate(ptConf->pSSL);
printf ("[CLIENT] Server certificate:\n");
COMMON_PrintCertificateDetails(server_cert);
X509_free(server_cert);
// MC: Do we really want to return SUCCESS regardless of the value of iStatus?
struct iobuffer {
int iUsed;
union {
unsigned char aucBuffer[1024];
char stext[1024];
};
char z;/*force null termination of string*/
};
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
DEBUG_PRINT ("[CLIENT DEBUG] Client starting\n");
ClientConfig tConfig;
SSL_CTX *ptSSLctx=NULL;
int iStatus;
client_SetDefaults(&tConfig);
char* inputFile = "inputData.json";
DEBUG_PRINT ("[CLIENT DEBUG] Reading input data from %s\n", inputFile);
cJSON* json = COMMON_ReadJSONFile(inputFile);
cJSON* item = NULL;
char* configStr = cJSON_Print(json);
DEBUG_PRINT("%s\n", configStr);
free(configStr);
// TO DO bounds, null or error checking of any kind at all
cJSON* contextNames = cJSON_GetObjectItem(json, "contexts");
int contextCount = 0;
cJSON_ArrayForEach(item, contextNames)
{
tConfig.contextDescs[contextCount] = strdup(item->valuestring);
contextCount++;
}
tConfig.numContexts = contextCount;
cJSON* middleboxes = cJSON_GetObjectItem(json, "middleboxes");
int mboxCount = 0;
cJSON_ArrayForEach(item, middleboxes)
{
tConfig.MiddleboxURLs[mboxCount] = strdup(cJSON_GetObjectItem(item, "url")->valuestring);
cJSON* readAccessItem = NULL;
cJSON_ArrayForEach(readAccessItem, cJSON_GetObjectItem(item, "readAccess"))
{
tConfig.middleboxReadRights[mboxCount][readAccessItem->valueint] = 1;
}
cJSON* writeAccessItem = NULL;
cJSON_ArrayForEach(writeAccessItem, cJSON_GetObjectItem(item, "writeAccess"))
{
tConfig.middleboxWriteRights[mboxCount][writeAccessItem->valueint] = 1;
tConfig.numMiddleboxes = mboxCount;
iStatus=client_ParseArgs(iArgC,papsArgV, &tConfig);
COMMON_CheckLogErrorAndExit(iStatus,stderr,"%s\n",USAGE);
iStatus=client_CreateContext(&ptSSLctx,&tConfig);
iStatus=client_Connect(ptSSLctx,&tConfig);
DEBUG_PRINT("[CLIENT DEBUG] Connected\n");
cJSON* slicedData = cJSON_GetObjectItem(json, "slicedData");
cJSON_ArrayForEach(item, slicedData)
{
cJSON *slice = cJSON_GetObjectItem(item, "slice");
cJSON *text = cJSON_GetObjectItem(item, "data");
if (slice->valueint >= tConfig.numContexts)
{
printf("[CLIENT ERROR] Data specified for slice id %d, which is out of bounds\n", slice->valueint);
exit(-1);
}
tConfig.pSSL->write_slice=SPP_get_slice_by_id(tConfig.pSSL,tConfig.pSSL->slices[slice->valueint]->slice_id);
if (tConfig.pSSL->write_slice == NULL)
{
printf("[CLIENT ERROR] Data specified for slice id %d which is not found\n", slice->valueint);
exit(-1);
}
int iBytesSent=SSL_write(tConfig.pSSL, text->valuestring, strlen(text->valuestring));
printf ("[CLIENT DEBUG] Sent %d bytes { %s }\n", iBytesSent, text->valuestring);
}
cJSON_Delete(json);
cJSON* recvData = cJSON_CreateObject();
cJSON* sliceDataArray = cJSON_AddArrayToObject(recvData, "slices");
SPP_SLICE* slice;
SPP_CTX* ctx;
int slicesSeen = 0;
while ((iBytesRead > 0) && (slicesSeen < tConfig.numContexts))
{
DEBUG_PRINT ("[CLIENT DEBUG] Waiting for response\n");
//iBytesRead = SSL_read(tConfig.pSSL,tInBuff.aucBuffer,sizeof(tInBuff.aucBuffer));
iBytesRead = SPP_read_record(tConfig.pSSL, tInBuff.aucBuffer, sizeof(tInBuff.aucBuffer), &slice, &ctx);
if (iBytesRead > 0)
{
cJSON* jslice = cJSON_CreateObject();
cJSON_AddNumberToObject(jslice, "slice", slice->slice_id );
cJSON_AddStringToObject(jslice, "data", COMMON_MakeNullTerminatedCopy(tInBuff.aucBuffer, iBytesRead));
cJSON_AddItemToArray(sliceDataArray, jslice);
DEBUG_PRINT ("[CLIENT DEBUG] Recv %d bytes [(%s)]\n", iBytesRead, tInBuff.aucBuffer);
}
else
{
DEBUG_PRINT ("[CLIENT DEBUG] SPP_read_record return zero bytes\n");
break;
}
char* filename = COMMON_WriteJSONFile(recvData, "Client");
DEBUG_PRINT("[CLIENT DEBUG] Data written to %s\n", filename);
free(filename);
char* recvDataString = cJSON_Print(recvData);
printf("[CLIENT] Received\n%s\n", recvDataString);
free(recvDataString);