mirror of https://github.com/D4-project/d4-core
				
				
				
			
		
			
				
	
	
		
			284 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			284 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C
		
	
	
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <stdint.h>
 | |
| #include <getopt.h>
 | |
| #include <string.h>
 | |
| #include <sys/types.h>
 | |
| #include <sys/stat.h>
 | |
| #include <fcntl.h>
 | |
| #include <unistd.h>
 | |
| #include <errno.h>
 | |
| #include <sys/time.h>
 | |
| 
 | |
| #include "others/uuid/uuid.h"
 | |
| #include "d4.h"
 | |
| //
 | |
| 
 | |
| 
 | |
| void usage(void)
 | |
| {
 | |
|     printf("d4 - d4 client\n");
 | |
|     printf("Read data from the configured <source> and send it to <destination>\n");
 | |
|     printf("\n");
 | |
|     printf("Usage: d4 -c  config_directory\n");
 | |
|     printf("\n");
 | |
| 
 | |
|     printf("Configuration\n\n");
 | |
|     printf("The configuration settings are stored in files in the configuration directory\n");
 | |
|     printf("specified with the -c command line switch.\n\n");
 | |
|     printf("Files in the configuration directory\n");
 | |
|     printf("\n");
 | |
|     printf("key         - is the private HMAC-SHA-256-128 key.\n");
 | |
|     printf("              The HMAC is computed on the header with a HMAC value set to 0\n");
 | |
|     printf("              which is updated later.\n");
 | |
|     printf("snaplen     - the length of bytes that is read from the <source>\n");
 | |
|     printf("version     - the version of the d4 client\n");
 | |
|     printf("type        - the type of data that is send. pcap, netflow, ...\n");
 | |
|     printf("source      - the source where the data is read from\n");
 | |
|     printf("destination - the destination where the data is written to\n");
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Generate a uuid if no one was set.
 | |
|  * If no errors occured textual representation of uuid is stored in the
 | |
|  * configuration array
 | |
|  */
 | |
| void  d4_update_uuid(d4_t* d4)
 | |
| {
 | |
|     uuid_t uuid;
 | |
|     int fd,ret;
 | |
|     char* filename;
 | |
|     char* uuid_text;
 | |
| 
 | |
|     if (d4->conf[UUID][0] == 0){
 | |
|         uuid_generate(uuid);
 | |
|         filename = calloc(1,2*FILENAME_MAX);
 | |
|         uuid_text = calloc(1, SZUUID_TEXT);
 | |
|         if ((filename != NULL) && (uuid != NULL)) {
 | |
|             snprintf(filename, 2*FILENAME_MAX, "%s/%s",d4->confdir, d4params[UUID]);
 | |
|             fd = open(filename, O_CREAT  |  O_WRONLY, S_IRUSR  |S_IWUSR);
 | |
|             if (fd > 0) {
 | |
|                 uuid_unparse(uuid, uuid_text);
 | |
|                 ret =  write(fd, uuid_text, SZUUID_TEXT-1);
 | |
|                 if (ret > 0) {
 | |
|                     memcpy(d4->conf[UUID], uuid_text, SZUUID_TEXT);
 | |
|                 } else {
 | |
|                     d4->errno_copy = errno;
 | |
|                 }
 | |
|                 close(fd);
 | |
|             } else {
 | |
|                 // Cannot open file
 | |
|                 d4->errno_copy = errno;
 | |
|             }
 | |
|         }
 | |
|         /* If there is an error the uuid is not stored and a new one is
 | |
|          * generated for the next boot
 | |
|          */
 | |
|      }
 | |
| }
 | |
| 
 | |
| int d4_check_config(d4_t* d4)
 | |
| {
 | |
|     // TODO implement other sources, file, fifo, unix_socket ...
 | |
|     if (strlen(d4->conf[SOURCE]) >= strlen(STDIN)) {
 | |
|         if (!strncmp(d4->conf[SOURCE],STDIN, strlen(STDIN))) {
 | |
|             d4->source.fd = STDIN_FILENO;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     //TODO implement other destinations file, fifo unix_socket ...
 | |
|     if (strlen(d4->conf[DESTINATION]) >= strlen(STDOUT)) {
 | |
|         if (!strncmp(d4->conf[DESTINATION],STDOUT, strlen(STDOUT))) {
 | |
|             d4->destination.fd = STDOUT_FILENO;
 | |
|         }
 | |
|     }
 | |
|     d4->snaplen  = atoi(d4->conf[SNAPLEN]);
 | |
| 
 | |
|     d4_update_uuid(d4);
 | |
| 
 | |
|     if ((d4->snaplen < 0)  || (d4->snaplen > MAXSNAPLEN)) {
 | |
|         d4->snaplen = 0;
 | |
|     }
 | |
| 
 | |
|     //FIXME Check other parameters
 | |
|     if ((atoi(d4->conf[VERSION])>0) &&   ( d4->destination.fd > 0 ) && ( d4->snaplen >0 )) {
 | |
|         return 1;
 | |
|     }
 | |
|     return -1;
 | |
| }
 | |
| 
 | |
| //Returns -1 on error, 0 otherwise
 | |
| int d4_load_config(d4_t* d4)
 | |
| {
 | |
|     int i;
 | |
|     int fd;
 | |
|     int n;
 | |
|     int j;
 | |
|     char *buf;
 | |
|     buf=calloc(1,2*FILENAME_MAX);
 | |
|     if (buf) {
 | |
|         for (i=0; i < ND4PARAMS; i++) {
 | |
|             snprintf(buf,2*FILENAME_MAX, "%s/%s",d4->confdir, d4params[i]);
 | |
|             fd = open(buf,O_RDONLY);
 | |
|             if (fd > 0) {
 | |
|                 //FIXME error handling
 | |
|                 n = read(fd, d4->conf[i], SZCONFVALUE);
 | |
|                 if (n > 0) {
 | |
|                     //Remove the last new line
 | |
|                     for (j=n; j>=0; --j) {
 | |
|                         if (d4->conf[i][j]== '\n') {
 | |
|                             d4->conf[i][j] = '\0';
 | |
|                             break;
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|                 close(fd);
 | |
|             } else {
 | |
|                 d4->errno_copy = errno;
 | |
|                 INSERT_ERROR("Failed to load %s", d4params[i]);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     return d4_check_config(d4);
 | |
| }
 | |
| 
 | |
| d4_t* d4_init(char* confdir)
 | |
| {
 | |
|     d4_t* out;
 | |
|     int i;
 | |
|     out = calloc(1,sizeof(d4_t));
 | |
|     if (out) {
 | |
|         strncpy(out->confdir, confdir, FILENAME_MAX);
 | |
|     }
 | |
| 
 | |
|     for  (i=0; i< ND4PARAMS; i++) {
 | |
|         bzero(out->conf[i],SZCONFVALUE);
 | |
|     }
 | |
|     // Do other inititalization stuff here
 | |
|     return out;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| void d4_prepare_header(d4_t* d4)
 | |
| {
 | |
|     char uuid_text[24];
 | |
|     uuid_t uuid;
 | |
| 
 | |
|     bzero(&d4->header,sizeof(d4->header));
 | |
|     bzero(&uuid_text, 24);
 | |
|     d4->header.version = atoi(d4->conf[VERSION]);
 | |
|     if (!uuid_parse(d4->conf[UUID],uuid)) {
 | |
|         memcpy(d4->header.uuid, uuid, SZUUID);
 | |
|     }
 | |
|     // If UUID cannot be parsed it is set to 0
 | |
|     d4->header.type = atoi(d4->conf[TYPE]);
 | |
| 
 | |
|     d4->ctx = calloc(sizeof(hmac_sha256_ctx),1);
 | |
|     if (d4->ctx) {
 | |
|         //FIXME check cast of the key
 | |
|         hmac_sha256_init(d4->ctx, (uint8_t*)d4->conf[KEY], strlen(d4->conf[KEY]));
 | |
|     }
 | |
| }
 | |
| 
 | |
| void d4_update_header(d4_t* d4, ssize_t nread) {
 | |
|     struct timeval tv;
 | |
|     bzero(&tv,sizeof(struct timeval));
 | |
|     gettimeofday(&tv,NULL);
 | |
|     d4->header.timestamp = tv.tv_sec;
 | |
|     d4->header.size=nread;
 | |
| }
 | |
| 
 | |
| //Core routine. Transfers data from the source to the destinations
 | |
| void d4_transfert(d4_t* d4)
 | |
| {
 | |
|     ssize_t nread;
 | |
|     ssize_t n;
 | |
|     char* buf;
 | |
|     unsigned char* hmac;
 | |
|     unsigned char* hmaczero;
 | |
| 
 | |
|     buf = calloc(1, d4->snaplen);
 | |
|     hmac = calloc(1,SZHMAC);
 | |
|     hmaczero = calloc(1,SZHMAC);
 | |
|     //TODO error handling -> insert error message
 | |
|     if ((buf == NULL) && (hmac == NULL) && (hmaczero == NULL))
 | |
|         return;
 | |
| 
 | |
|     d4_prepare_header(d4);
 | |
|     while ( 1 ) {
 | |
|         //In case of errors see block of 0 bytes
 | |
|         bzero(buf, d4->snaplen);
 | |
|         nread = read(d4->source.fd, buf, d4->snaplen);
 | |
|         if ( nread > 0 ) {
 | |
|             d4_update_header(d4, nread);
 | |
|             //Do HMAC on header and payload. HMAC field is 0 during computation
 | |
|             if (d4->ctx) {
 | |
|                 hmac_sha256_reinit(d4->ctx);
 | |
| 		hmac_sha256_update(d4->ctx, (const unsigned char*)&d4->header.version, sizeof(uint8_t));
 | |
| 		hmac_sha256_update(d4->ctx, (const unsigned char*)&d4->header.type, sizeof(uint8_t));
 | |
| 		hmac_sha256_update(d4->ctx, (const unsigned char*)&d4->header.uuid, SZUUID);
 | |
| 		hmac_sha256_update(d4->ctx, (const unsigned char*)&d4->header.timestamp, sizeof(uint64_t));
 | |
| 		hmac_sha256_update(d4->ctx, (const unsigned char*) hmaczero, SZHMAC);
 | |
| 		hmac_sha256_update(d4->ctx, (const unsigned char*)&d4->header.size, sizeof(uint32_t));
 | |
| 		hmac_sha256_update(d4->ctx, (const unsigned char*)buf, nread);
 | |
| 		hmac_sha256_final(d4->ctx, hmac, SZHMAC);
 | |
|                 //Add it to the header
 | |
|                 memcpy(d4->header.hmac, hmac, SZHMAC);
 | |
|             }
 | |
|             n = 0;
 | |
|             n+=write(d4->destination.fd, &d4->header.version, sizeof(uint8_t));
 | |
|             n+=write(d4->destination.fd, &d4->header.type, sizeof(uint8_t));
 | |
|             n+=write(d4->destination.fd, &d4->header.uuid, SZUUID);
 | |
|             n+=write(d4->destination.fd, &d4->header.timestamp, sizeof(uint64_t));
 | |
|             n+=write(d4->destination.fd, &d4->header.hmac, SZHMAC);
 | |
|             n+=write(d4->destination.fd, &d4->header.size, sizeof(uint32_t));
 | |
|             n+=write(d4->destination.fd,buf,nread);
 | |
|             if (n != SZD4HDR + nread) {
 | |
|                 fprintf(stderr,"Incomplete header written. abort to let consumer known that the packet is corrupted\n");
 | |
|                 abort();
 | |
|             }
 | |
|         } else{
 | |
|             //FIXME no data available, sleep, abort, retry
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| int main (int argc, char* argv[])
 | |
| {
 | |
|     int opt;
 | |
|     char* confdir;
 | |
|     d4_t* d4;
 | |
| 
 | |
|     confdir=calloc(1,FILENAME_MAX);
 | |
|     if (!confdir)
 | |
|         return EXIT_FAILURE;
 | |
| 
 | |
|     while ((opt = getopt(argc, argv, "c:h")) != -1) {
 | |
|         switch (opt) {
 | |
|             case 'h':
 | |
|                 usage();
 | |
|                 return EXIT_SUCCESS;
 | |
|             case 'c':
 | |
|                 strncpy(confdir, optarg, FILENAME_MAX);
 | |
|                 break;
 | |
|             default:
 | |
|                 fprintf(stderr,"An invalid command line argument was specified\n");
 | |
|         }
 | |
|     }
 | |
|     if (!confdir[0]){
 | |
|         fprintf(stderr,"A config directory must be specified\n");
 | |
|         return EXIT_FAILURE;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     d4 = d4_init(confdir);
 | |
|     free(confdir);
 | |
|     if (d4_load_config(d4)) {
 | |
|         d4_transfert(d4);
 | |
|     }
 | |
| 
 | |
|     return EXIT_SUCCESS;
 | |
| }
 |