#include #include #include #include "common.h" #include #include #include #include #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"; // TO-DO: Accept input file as command line argument const char *ARG_TYPES="v:V:k:c:s:m:M"; typedef struct ClientConfigST { 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 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]); assert(iStatus==SUCCESS); } } if(iStatus!=SUCCESS) { /*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++) { /*grant client if included*/ /*grant server*/ 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, readPermission, writePermission); } } #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; DEBUG_PRINT ("[CLIENT DEBUG] Connecting to %s\n", sHost); 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); return SUCCESS; // MC: Do we really want to return SUCCESS regardless of the value of iStatus? } int intCount = 0; struct iobuffer { int iUsed; union { unsigned char aucBuffer[1024]; char stext[1024]; }; char z;/*force null termination of string*/ }; int main(const int iArgC, char *papsArgV[]) { 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; } mboxCount++; } 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"); struct iobuffer tInBuff; 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] Sent %d bytes { %s }\n", iBytesSent, text->valuestring); } cJSON_Delete(json); int iBytesRead = 1; 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; } slicesSeen++; } 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); cJSON_Delete(recvData); DEBUG_PRINT ("[CLIENT DEBUG] Exiting\n"); return iStatus; }