From 35c378db771ea870c4a94ffc2331b13b3522cb3c Mon Sep 17 00:00:00 2001 From: Jonas Gunz Date: Tue, 21 Sep 2021 00:25:05 +0200 Subject: add record parser --- src/record.c | 193 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/record.h | 19 ++++++ src/zonefile.c | 46 ++++++++++++-- src/zonefile.h | 1 + 4 files changed, 254 insertions(+), 5 deletions(-) create mode 100644 src/record.c create mode 100644 src/record.h diff --git a/src/record.c b/src/record.c new file mode 100644 index 0000000..7ba40a3 --- /dev/null +++ b/src/record.c @@ -0,0 +1,193 @@ +/* + * src/record.c + * (c) 2021 Jonas Gunz + * License: MIT + */ + +/* https://datatracker.ietf.org/doc/html/rfc1035#section-3.3 */ + +#include "record.h" + +/* + * Prototypes for rdata from string functions + * Arguments: + * _str: String representing rdata + * _rdata: A buffer containing the raw rdata will be alloced here + * Return: Length of alloced _rdata buffer, <0 on error + */ + +/* Obsolete record types. Will throw error. */ +static ssize_t record_rdata_obsolete(char* _str, void** _rdata); + +/* Unimplemented record types. Will throw error. */ +static ssize_t record_rdata_not_implemented(char* _str, void** _rdata); + +/* rdata that does not need conversion from string form */ +static ssize_t record_rdata_verbatim(char* _str, void** _rdata); + +/* IPv4 Addresses */ +static ssize_t record_rdata_a(char* _str, void** _rdata); + +/* Start of authority */ +static ssize_t record_rdata_soa(char* _str, void** _rdata); + +static const char* const record_types[] = { + "A", + "NS", + "MD", + "MF", + "CNAME", + "SOA", + "MB", + "MG", + "MR", + "NULL", + "WKS", + "PTR", + "HINFO", + "MINFO", + "MX", + "TXT" +}; +static const uint16_t record_types_len = sizeof(record_types) / sizeof(char*); + +static ssize_t (*record_rdata_creator[])(char*, void**) = { + &record_rdata_a, /* A */ + &record_rdata_verbatim, /* NS */ + &record_rdata_obsolete, /* MD */ + &record_rdata_obsolete, /* MF */ + &record_rdata_verbatim, /* CNAME */ + &record_rdata_soa, /* SOA */ + + &record_rdata_not_implemented, + &record_rdata_not_implemented, + &record_rdata_not_implemented, + &record_rdata_not_implemented, + &record_rdata_not_implemented, + &record_rdata_not_implemented, + &record_rdata_not_implemented, + &record_rdata_not_implemented, + &record_rdata_not_implemented, + &record_rdata_not_implemented, +}; + +static const char* const record_classes[] = { + "IN", + "CS", + "CH", + "HS" +}; +static const uint16_t record_classes_len = sizeof(record_classes) / sizeof(char*); + +/* Implementation of RDATA cobverters */ + +static ssize_t record_rdata_obsolete(char* _str, void** _rdata) { + LOGPRINTF(_LOG_ERROR, "Record type not obsolete"); + return -1; +} + +static ssize_t record_rdata_not_implemented(char* _str, void** _rdata) { + LOGPRINTF(_LOG_ERROR, "Record type not implemented"); + return -1; +} + +static ssize_t record_rdata_verbatim(char* _str, void** _rdata) { + size_t len; + + if ( !_str || !_rdata ) + return -1; + + len = strlen(_str) + 1; /* Including \0 */ + + *_rdata = malloc(len); + + if ( !*_rdata ) + return -1; + + strncpy(*_rdata, _str, len); + + return (signed) len; +} + +static ssize_t record_rdata_a(char* _str, void** _rdata) { + char* tok; + char* str; + char* end; + size_t len; + int i; + + if ( !_str || !_rdata ) + return -1; + + len = strlen(_str) + 1; + str = malloc(len); + strncpy(str, _str, len); + + *_rdata = malloc(4); + if ( !*_rdata ) + return -1; + + tok = strtok(str, "."); + if( !tok ) + goto err; + + ((uint8_t*)(*_rdata))[3] = (uint8_t) strtol(tok, &end, 10); + + if( *end != '\0' ) + goto err; + + for( i=2; i>=0; i-- ) { + tok = strtok(NULL, "."); + + if( !tok ) + goto err; + + ((uint8_t*)(*_rdata))[i] = (uint8_t) strtol(tok, &end, 10); + + if( *end != '\0' ) + goto err; + } + + free(str); + return 4; +err: + free(str); + return -1; +} + +static ssize_t record_rdata_soa(char* _str, void** _rdata) { + LOGPRINTF(_LOG_ERROR, "Record type not implemented"); + return -1; +} + + +/* Other methods */ + +static uint16_t record_match_from_array(char* _str, const char* const _arr[], uint16_t _len) { + uint16_t i; + + for( i=0; i<_len; i++ ){ + if ( strcasecmp( _str, _arr[i] ) == 0 ) + return i+1; /* Indices start with 1 */ + } + + return 0; +} + +uint16_t record_class_from_str(char* _str) { + return record_match_from_array(_str, record_classes, record_classes_len); +} + +uint16_t record_type_from_str(char* _str) { + return record_match_from_array(_str, record_types, record_types_len); +} + +ssize_t record_rdata_from_str(void** _rdata, char *_str, uint16_t _rdtype) { + if ( _rdtype >= record_types_len ) + return -1; + + if ( !_rdata || !_str ) + return -1; + + return (*record_rdata_creator)(_str, _rdata); +} diff --git a/src/record.h b/src/record.h new file mode 100644 index 0000000..ef41ecb --- /dev/null +++ b/src/record.h @@ -0,0 +1,19 @@ +/* + * src/record.h + * (c) 2021 Jonas Gunz + * License: MIT + */ + +#pragma once + +#include +#include +#include + +#include "log.h" + +uint16_t record_class_from_str(char* _str); + +uint16_t record_type_from_str(char* _str); + +ssize_t record_rdata_from_str(void** _rdata, char *_str, uint16_t _rdtype); diff --git a/src/zonefile.c b/src/zonefile.c index 07e89d7..9104ad5 100644 --- a/src/zonefile.c +++ b/src/zonefile.c @@ -34,6 +34,9 @@ int zonefile_parse_line(database_t *_database, char *_line) { uint32_t ttl; uint16_t type, class; void* data; + ssize_t data_len; + + void* db_entry; memset(&parts, 0, sizeof(parts)); @@ -48,8 +51,8 @@ int zonefile_parse_line(database_t *_database, char *_line) { LOGPRINTF(_LOG_ERROR, "FQDN Contains invalid char at pos %i", ret); return -1; } - qname = malloc( (unsigned)fqdn_len+1 ); - if ( fqdn_to_qname(parts[0], fqdn_len, qname, fqdn_len+1) < 0) { + qname = malloc( (unsigned)fqdn_len+2 ); + if ( fqdn_to_qname(parts[0], fqdn_len, qname, fqdn_len+2) < 0) { LOGPRINTF(_LOG_ERROR, "Failed to convert to QNAME. This is a bug."); return -1; } @@ -59,9 +62,42 @@ int zonefile_parse_line(database_t *_database, char *_line) { return -1; } - DEBUG("value %s", parts[4]); + if ( (class = record_class_from_str(parts[2])) == 0 ) { + LOGPRINTF(_LOG_ERROR, "Invalid class %s", parts[2]); + return -1; + } + + if ( (type = record_type_from_str(parts[3])) == 0 ) { + LOGPRINTF(_LOG_ERROR, "Invalid record type %s", parts[3]); + return -1; + } + + if ( (data_len = record_rdata_from_str(&data, parts[4], type)) <= 0 ) { + LOGPRINTF(_LOG_ERROR, "Invalid rdata %s", parts[4]); + return -1; + } + + db_entry = malloc((unsigned)data_len + 6); + if ( !db_entry ) + goto err; + + DEBUG("Found %s record at %s", parts[3], parts[0]); + + *((uint32_t*)db_entry) = ttl; + *((uint16_t*)db_entry+4) = (uint16_t) data_len; + strncpy(db_entry+6, data, (unsigned)data_len); + + /* Error Here */ + DEBUG("LEN: %u, %i", *( (uint16_t*)(db_entry + 4) ), data_len); + + /* This breaks abstraction boundries! */ + ret = tree_insert( &_database->zone[class-1][type-1], qname, db_entry ); + + free(data); return 0; +err: + free(data); return -1; } @@ -87,8 +123,8 @@ int zonefile_to_database (database_t *_database, char* _file) { DEBUG("line %u, length %li, allocated %lu", line_cnt, line_len, llen); /* getline includes the line break. ONLY UNIX ENDINGS!! */ - if( line[line_len - 2] == '\n' ) - line[line_len - 2] = '\0'; + if( line[line_len - 1] == '\n' ) + line[line_len - 1] = '\0'; if ( zonefile_parse_line(_database, line) < 0) { LOGPRINTF(_LOG_ERROR, "Error is in line %u", line_cnt) diff --git a/src/zonefile.h b/src/zonefile.h index 4dcd6ef..deebcf9 100644 --- a/src/zonefile.h +++ b/src/zonefile.h @@ -12,6 +12,7 @@ #include "database.h" #include "log.h" +#include "record.h" /** * NOT COMPATIBLE WITH STANDARD ZONEFILES! -- cgit v1.2.3