Changeset View
Changeset View
Standalone View
Standalone View
contrib/ldns/str2host.c
/* | /* | ||||
* str2host.c | * str2host.c | ||||
* | * | ||||
* conversion routines from the presentation format | * conversion routines from the presentation format | ||||
* to the host format | * to the host format | ||||
* | * | ||||
* a Net::DNS like library for C | * a Net::DNS like library for C | ||||
* | * | ||||
* (c) NLnet Labs, 2004-2006 | * (c) NLnet Labs, 2004-2006 | ||||
* | * | ||||
* See the file LICENSE for the license | * See the file LICENSE for the license | ||||
*/ | */ | ||||
#include <ldns/config.h> | #include <ldns/config.h> | ||||
#include <ldns/ldns.h> | #include <ldns/ldns.h> | ||||
#include <ldns/internal.h> | |||||
#ifdef HAVE_SYS_SOCKET_H | #ifdef HAVE_SYS_SOCKET_H | ||||
#include <sys/socket.h> | #include <sys/socket.h> | ||||
#endif | #endif | ||||
#ifdef HAVE_ARPA_INET_H | #ifdef HAVE_ARPA_INET_H | ||||
#include <arpa/inet.h> | #include <arpa/inet.h> | ||||
#endif | #endif | ||||
#include <time.h> | #include <time.h> | ||||
Show All 28 Lines | if(*end != 0) { | ||||
return *rd?LDNS_STATUS_OK:LDNS_STATUS_MEM_ERR; | return *rd?LDNS_STATUS_OK:LDNS_STATUS_MEM_ERR; | ||||
} | } | ||||
} | } | ||||
ldns_status | ldns_status | ||||
ldns_str2rdf_time(ldns_rdf **rd, const char *time) | ldns_str2rdf_time(ldns_rdf **rd, const char *time) | ||||
{ | { | ||||
/* convert a time YYYYDDMMHHMMSS to wireformat */ | /* convert a time YYYYDDMMHHMMSS to wireformat */ | ||||
uint16_t *r = NULL; | uint32_t *r = NULL; | ||||
struct tm tm; | struct tm tm; | ||||
uint32_t l; | uint32_t l; | ||||
char *end; | char *end; | ||||
/* Try to scan the time... */ | /* Try to scan the time... */ | ||||
r = (uint16_t*)LDNS_MALLOC(uint32_t); | r = (uint32_t *)LDNS_MALLOC(uint32_t); | ||||
if(!r) return LDNS_STATUS_MEM_ERR; | if(!r) return LDNS_STATUS_MEM_ERR; | ||||
memset(&tm, 0, sizeof(tm)); | memset(&tm, 0, sizeof(tm)); | ||||
if (strlen(time) == 14 && | if (strlen(time) == 14 && | ||||
sscanf(time, "%4d%2d%2d%2d%2d%2d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6 | sscanf(time, "%4d%2d%2d%2d%2d%2d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6 | ||||
) { | ) { | ||||
tm.tm_year -= 1900; | tm.tm_year -= 1900; | ||||
▲ Show 20 Lines • Show All 117 Lines • ▼ Show 20 Lines | *rd = ldns_rdf_new_frm_data( | ||||
} | } | ||||
return *rd?LDNS_STATUS_OK:LDNS_STATUS_MEM_ERR; | return *rd?LDNS_STATUS_OK:LDNS_STATUS_MEM_ERR; | ||||
} | } | ||||
ldns_status | ldns_status | ||||
ldns_str2rdf_int32(ldns_rdf **rd, const char *longstr) | ldns_str2rdf_int32(ldns_rdf **rd, const char *longstr) | ||||
{ | { | ||||
char *end; | char *end; | ||||
uint16_t *r = NULL; | uint32_t *r = NULL; | ||||
uint32_t l; | uint32_t l; | ||||
r = (uint16_t*)LDNS_MALLOC(uint32_t); | r = (uint32_t*)LDNS_MALLOC(uint32_t); | ||||
if(!r) return LDNS_STATUS_MEM_ERR; | if(!r) return LDNS_STATUS_MEM_ERR; | ||||
errno = 0; /* must set to zero before call, | errno = 0; /* must set to zero before call, | ||||
note race condition on errno */ | note race condition on errno */ | ||||
if(*longstr == '-') | if(*longstr == '-') | ||||
l = htonl((uint32_t)strtol((char*)longstr, &end, 10)); | l = htonl((uint32_t)strtol((char*)longstr, &end, 10)); | ||||
else l = htonl((uint32_t)strtoul((char*)longstr, &end, 10)); | else l = htonl((uint32_t)strtoul((char*)longstr, &end, 10)); | ||||
if(*end != 0) { | if(*end != 0) { | ||||
▲ Show 20 Lines • Show All 87 Lines • ▼ Show 20 Lines | parse_char(uint8_t *ch_p, const char** str_p) | ||||
default: *ch_p = (uint8_t)*(*str_p)++; | default: *ch_p = (uint8_t)*(*str_p)++; | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* No special care is taken, all dots are translated into | * No special care is taken, all dots are translated into | ||||
* label seperators. | * label separators. | ||||
* Could be made more efficient....we do 3 memcpy's in total... | * Could be made more efficient....we do 3 memcpy's in total... | ||||
*/ | */ | ||||
ldns_status | ldns_status | ||||
ldns_str2rdf_dname(ldns_rdf **d, const char *str) | ldns_str2rdf_dname(ldns_rdf **d, const char *str) | ||||
{ | { | ||||
size_t len; | size_t len; | ||||
const char *s; | const char *s; | ||||
Show All 23 Lines | /* s is on the current character in the string | ||||
* label_len keeps track of the current label's length | * label_len keeps track of the current label's length | ||||
* q builds the dname inside the buf array | * q builds the dname inside the buf array | ||||
*/ | */ | ||||
len = 0; | len = 0; | ||||
q = buf+1; | q = buf+1; | ||||
pq = buf; | pq = buf; | ||||
label_len = 0; | label_len = 0; | ||||
for (s = str; *s; s++, q++) { | for (s = str; *s; s++, q++) { | ||||
if (q > buf + LDNS_MAX_DOMAINLEN) { | if (q >= buf + LDNS_MAX_DOMAINLEN) { | ||||
return LDNS_STATUS_DOMAINNAME_OVERFLOW; | return LDNS_STATUS_DOMAINNAME_OVERFLOW; | ||||
} | } | ||||
*q = 0; | *q = 0; | ||||
switch (*s) { | switch (*s) { | ||||
case '.': | case '.': | ||||
if (label_len > LDNS_MAX_LABELLEN) { | if (label_len > LDNS_MAX_LABELLEN) { | ||||
return LDNS_STATUS_LABEL_OVERFLOW; | return LDNS_STATUS_LABEL_OVERFLOW; | ||||
} | } | ||||
Show All 17 Lines | for (s = str; *s; s++, q++) { | ||||
default: | default: | ||||
*q = (uint8_t)*s; | *q = (uint8_t)*s; | ||||
label_len++; | label_len++; | ||||
} | } | ||||
} | } | ||||
/* add root label if last char was not '.' */ | /* add root label if last char was not '.' */ | ||||
if (!ldns_dname_str_absolute(str)) { | if (!ldns_dname_str_absolute(str)) { | ||||
if (q > buf + LDNS_MAX_DOMAINLEN) { | if (q >= buf + LDNS_MAX_DOMAINLEN) { | ||||
return LDNS_STATUS_DOMAINNAME_OVERFLOW; | return LDNS_STATUS_DOMAINNAME_OVERFLOW; | ||||
} | } | ||||
if (label_len > LDNS_MAX_LABELLEN) { | if (label_len > LDNS_MAX_LABELLEN) { | ||||
return LDNS_STATUS_LABEL_OVERFLOW; | return LDNS_STATUS_LABEL_OVERFLOW; | ||||
} | } | ||||
if (label_len == 0) { /* label_len 0 but not . at end? */ | if (label_len == 0) { /* label_len 0 but not . at end? */ | ||||
return LDNS_STATUS_EMPTY_LABEL; | return LDNS_STATUS_EMPTY_LABEL; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | ldns_str2rdf_str(ldns_rdf **rd, const char *str) | ||||
while (parse_char(&ch, &str)) { | while (parse_char(&ch, &str)) { | ||||
if (dp - data >= 255) { | if (dp - data >= 255) { | ||||
LDNS_FREE(data); | LDNS_FREE(data); | ||||
return LDNS_STATUS_INVALID_STR; | return LDNS_STATUS_INVALID_STR; | ||||
} | } | ||||
*++dp = ch; | *++dp = ch; | ||||
} | } | ||||
if (! str) { | if (! str) { | ||||
LDNS_FREE(data); | |||||
return LDNS_STATUS_SYNTAX_BAD_ESCAPE; | return LDNS_STATUS_SYNTAX_BAD_ESCAPE; | ||||
} | } | ||||
length = (size_t)(dp - data); | length = (size_t)(dp - data); | ||||
/* Fix last length byte */ | /* Fix last length byte */ | ||||
data[0] = (uint8_t)length; | data[0] = (uint8_t)length; | ||||
/* Lose the overmeasure */ | /* Lose the overmeasure */ | ||||
data = LDNS_XREALLOC(dp = data, uint8_t, length + 1); | data = LDNS_XREALLOC(dp = data, uint8_t, length + 1); | ||||
▲ Show 20 Lines • Show All 123 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
ldns_status | ldns_status | ||||
ldns_str2rdf_b64(ldns_rdf **rd, const char *str) | ldns_str2rdf_b64(ldns_rdf **rd, const char *str) | ||||
{ | { | ||||
uint8_t *buffer; | uint8_t *buffer; | ||||
int16_t i; | int16_t i; | ||||
if ((*str == '-' || *str == '0') && str[1] == '\0') { | |||||
*rd = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_B64, 0, NULL); | |||||
return *rd ? LDNS_STATUS_OK : LDNS_STATUS_MEM_ERR; | |||||
} | |||||
buffer = LDNS_XMALLOC(uint8_t, ldns_b64_ntop_calculate_size(strlen(str))); | buffer = LDNS_XMALLOC(uint8_t, ldns_b64_ntop_calculate_size(strlen(str))); | ||||
if(!buffer) { | if(!buffer) { | ||||
return LDNS_STATUS_MEM_ERR; | return LDNS_STATUS_MEM_ERR; | ||||
} | } | ||||
i = (uint16_t)ldns_b64_pton((const char*)str, buffer, | i = (uint16_t)ldns_b64_pton((const char*)str, buffer, | ||||
ldns_b64_ntop_calculate_size(strlen(str))); | ldns_b64_ntop_calculate_size(strlen(str))); | ||||
if (-1 == i) { | if (-1 == i) { | ||||
Show All 9 Lines | |||||
} | } | ||||
ldns_status | ldns_status | ||||
ldns_str2rdf_b32_ext(ldns_rdf **rd, const char *str) | ldns_str2rdf_b32_ext(ldns_rdf **rd, const char *str) | ||||
{ | { | ||||
uint8_t *buffer; | uint8_t *buffer; | ||||
int i; | int i; | ||||
/* first byte contains length of actual b32 data */ | /* first byte contains length of actual b32 data */ | ||||
uint8_t len = ldns_b32_pton_calculate_size(strlen(str)); | size_t slen = strlen(str); | ||||
size_t len = ldns_b32_pton_calculate_size(slen); | |||||
if (len > 255) { | |||||
return LDNS_STATUS_INVALID_B32_EXT; | |||||
} | |||||
buffer = LDNS_XMALLOC(uint8_t, len + 1); | buffer = LDNS_XMALLOC(uint8_t, len + 1); | ||||
if(!buffer) { | if(!buffer) { | ||||
return LDNS_STATUS_MEM_ERR; | return LDNS_STATUS_MEM_ERR; | ||||
} | } | ||||
buffer[0] = len; | buffer[0] = len; | ||||
i = ldns_b32_pton_extended_hex((const char*)str, strlen(str), buffer + 1, | i = ldns_b32_pton_extended_hex((const char*)str, slen, buffer + 1, | ||||
ldns_b32_ntop_calculate_size(strlen(str))); | ldns_b32_ntop_calculate_size(slen)); | ||||
if (i < 0) { | if (i < 0) { | ||||
LDNS_FREE(buffer); | LDNS_FREE(buffer); | ||||
return LDNS_STATUS_INVALID_B32_EXT; | return LDNS_STATUS_INVALID_B32_EXT; | ||||
} else { | } else { | ||||
*rd = ldns_rdf_new_frm_data( | *rd = ldns_rdf_new_frm_data( | ||||
LDNS_RDF_TYPE_B32_EXT, (uint16_t) i + 1, buffer); | LDNS_RDF_TYPE_B32_EXT, (uint16_t) i + 1, buffer); | ||||
} | } | ||||
LDNS_FREE(buffer); | LDNS_FREE(buffer); | ||||
▲ Show 20 Lines • Show All 235 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
static int | static int | ||||
loc_parse_cm(char* my_str, char** endstr, uint8_t* m, uint8_t* e) | loc_parse_cm(char* my_str, char** endstr, uint8_t* m, uint8_t* e) | ||||
{ | { | ||||
/* read <digits>[.<digits>][mM] */ | /* read <digits>[.<digits>][mM] */ | ||||
/* into mantissa exponent format for LOC type */ | /* into mantissa exponent format for LOC type */ | ||||
uint32_t meters = 0, cm = 0, val; | uint32_t meters = 0, cm = 0, val; | ||||
char* cm_endstr; | |||||
while (isblank((unsigned char)*my_str)) { | while (isblank((unsigned char)*my_str)) { | ||||
my_str++; | my_str++; | ||||
} | } | ||||
meters = (uint32_t)strtol(my_str, &my_str, 10); | meters = (uint32_t)strtol(my_str, &my_str, 10); | ||||
if (*my_str == '.') { | if (*my_str == '.') { | ||||
my_str++; | my_str++; | ||||
cm = (uint32_t)strtol(my_str, &my_str, 10); | cm = (uint32_t)strtol(my_str, &cm_endstr, 10); | ||||
if (cm_endstr - my_str == 1) cm *= 10; | |||||
my_str = cm_endstr; | |||||
} | } | ||||
if (meters >= 1) { | if (meters >= 1) { | ||||
*e = 2; | *e = 2; | ||||
val = meters; | val = meters; | ||||
} else { | } else { | ||||
*e = 0; | *e = 0; | ||||
val = cm; | val = cm; | ||||
} | } | ||||
Show All 24 Lines | ldns_str2rdf_loc(ldns_rdf **rd, const char *str) | ||||
uint32_t h = 0; | uint32_t h = 0; | ||||
uint32_t m = 0; | uint32_t m = 0; | ||||
uint8_t size_b = 1, size_e = 2; | uint8_t size_b = 1, size_e = 2; | ||||
uint8_t horiz_pre_b = 1, horiz_pre_e = 6; | uint8_t horiz_pre_b = 1, horiz_pre_e = 6; | ||||
uint8_t vert_pre_b = 1, vert_pre_e = 3; | uint8_t vert_pre_b = 1, vert_pre_e = 3; | ||||
double s = 0.0; | double s = 0.0; | ||||
bool northerness; | bool northern_hemisphere; | ||||
bool easterness; | bool eastern_hemisphere; | ||||
char *my_str = (char *) str; | char *my_str = (char *) str; | ||||
/* only support version 0 */ | /* only support version 0 */ | ||||
if (isdigit((int) *my_str)) { | if (isdigit((int) *my_str)) { | ||||
h = (uint32_t) strtol(my_str, &my_str, 10); | h = (uint32_t) strtol(my_str, &my_str, 10); | ||||
} else { | } else { | ||||
return LDNS_STATUS_INVALID_STR; | return LDNS_STATUS_INVALID_STR; | ||||
Show All 19 Lines | if (isdigit((int) *my_str)) { | ||||
s = strtod(my_str, &my_str); | s = strtod(my_str, &my_str); | ||||
} | } | ||||
north: | north: | ||||
while (isblank((int) *my_str)) { | while (isblank((int) *my_str)) { | ||||
my_str++; | my_str++; | ||||
} | } | ||||
if (*my_str == 'N') { | if (*my_str == 'N') { | ||||
northerness = true; | northern_hemisphere = true; | ||||
} else if (*my_str == 'S') { | } else if (*my_str == 'S') { | ||||
northerness = false; | northern_hemisphere = false; | ||||
} else { | } else { | ||||
return LDNS_STATUS_INVALID_STR; | return LDNS_STATUS_INVALID_STR; | ||||
} | } | ||||
my_str++; | my_str++; | ||||
/* store number */ | /* store number */ | ||||
s = 1000.0 * s; | s = 1000.0 * s; | ||||
/* add a little to make floor in conversion a round */ | /* add a little to make floor in conversion a round */ | ||||
s += 0.0005; | s += 0.0005; | ||||
latitude = (uint32_t) s; | latitude = (uint32_t) s; | ||||
latitude += 1000 * 60 * m; | latitude += 1000 * 60 * m; | ||||
latitude += 1000 * 60 * 60 * h; | latitude += 1000 * 60 * 60 * h; | ||||
if (northerness) { | if (northern_hemisphere) { | ||||
latitude = equator + latitude; | latitude = equator + latitude; | ||||
} else { | } else { | ||||
latitude = equator - latitude; | latitude = equator - latitude; | ||||
} | } | ||||
while (isblank((unsigned char)*my_str)) { | while (isblank((unsigned char)*my_str)) { | ||||
my_str++; | my_str++; | ||||
} | } | ||||
Show All 24 Lines | north: | ||||
} | } | ||||
east: | east: | ||||
while (isblank((unsigned char)*my_str)) { | while (isblank((unsigned char)*my_str)) { | ||||
my_str++; | my_str++; | ||||
} | } | ||||
if (*my_str == 'E') { | if (*my_str == 'E') { | ||||
easterness = true; | eastern_hemisphere = true; | ||||
} else if (*my_str == 'W') { | } else if (*my_str == 'W') { | ||||
easterness = false; | eastern_hemisphere = false; | ||||
} else { | } else { | ||||
return LDNS_STATUS_INVALID_STR; | return LDNS_STATUS_INVALID_STR; | ||||
} | } | ||||
my_str++; | my_str++; | ||||
/* store number */ | /* store number */ | ||||
s *= 1000.0; | s *= 1000.0; | ||||
/* add a little to make floor in conversion a round */ | /* add a little to make floor in conversion a round */ | ||||
s += 0.0005; | s += 0.0005; | ||||
longitude = (uint32_t) s; | longitude = (uint32_t) s; | ||||
longitude += 1000 * 60 * m; | longitude += 1000 * 60 * m; | ||||
longitude += 1000 * 60 * 60 * h; | longitude += 1000 * 60 * 60 * h; | ||||
if (easterness) { | if (eastern_hemisphere) { | ||||
longitude += equator; | longitude += equator; | ||||
} else { | } else { | ||||
longitude = equator - longitude; | longitude = equator - longitude; | ||||
} | } | ||||
altitude = (uint32_t)(strtod(my_str, &my_str)*100.0 + | altitude = (uint32_t)(strtod(my_str, &my_str)*100.0 + | ||||
10000000.0 + 0.5); | 10000000.0 + 0.5); | ||||
if (*my_str == 'm' || *my_str == 'M') { | if (*my_str == 'm' || *my_str == 'M') { | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | ldns_str2rdf_wks(ldns_rdf **rd, const char *str) | ||||
struct protoent *proto = NULL; | struct protoent *proto = NULL; | ||||
struct servent *serv = NULL; | struct servent *serv = NULL; | ||||
int serv_port; | int serv_port; | ||||
ldns_buffer *str_buf; | ldns_buffer *str_buf; | ||||
char *proto_str = NULL; | char *proto_str = NULL; | ||||
char *lc_proto_str = NULL; | |||||
char *token; | char *token; | ||||
char *lc_token; | |||||
char *c; | |||||
if(strlen(str) == 0) | if(strlen(str) == 0) | ||||
token = LDNS_XMALLOC(char, 50); | token = LDNS_XMALLOC(char, 50); | ||||
else token = LDNS_XMALLOC(char, strlen(str)+2); | else token = LDNS_XMALLOC(char, strlen(str)+2); | ||||
if(!token) return LDNS_STATUS_MEM_ERR; | if(!token) return LDNS_STATUS_MEM_ERR; | ||||
str_buf = LDNS_MALLOC(ldns_buffer); | str_buf = LDNS_MALLOC(ldns_buffer); | ||||
if(!str_buf) {LDNS_FREE(token); return LDNS_STATUS_MEM_ERR;} | if(!str_buf) {LDNS_FREE(token); return LDNS_STATUS_MEM_ERR;} | ||||
ldns_buffer_new_frm_data(str_buf, (char *)str, strlen(str)); | ldns_buffer_new_frm_data(str_buf, (char *)str, strlen(str)); | ||||
if(ldns_buffer_status(str_buf) != LDNS_STATUS_OK) { | if(ldns_buffer_status(str_buf) != LDNS_STATUS_OK) { | ||||
LDNS_FREE(str_buf); | LDNS_FREE(str_buf); | ||||
LDNS_FREE(token); | LDNS_FREE(token); | ||||
return LDNS_STATUS_MEM_ERR; | return LDNS_STATUS_MEM_ERR; | ||||
} | } | ||||
while(ldns_bget_token(str_buf, token, "\t\n ", strlen(str)) > 0) { | while(ldns_bget_token(str_buf, token, "\t\n ", strlen(str)) > 0) { | ||||
if (!proto_str) { | if (!proto_str) { | ||||
proto_str = strdup(token); | proto_str = strdup(token); | ||||
if (!proto_str) { | lc_proto_str = strdup(token); | ||||
for (c = lc_proto_str; *c; c++) { | |||||
*c = tolower((unsigned char)*c); | |||||
} | |||||
if (!proto_str || !lc_proto_str) { | |||||
free(proto_str); | |||||
free(lc_proto_str); | |||||
LDNS_FREE(bitmap); | LDNS_FREE(bitmap); | ||||
LDNS_FREE(token); | LDNS_FREE(token); | ||||
ldns_buffer_free(str_buf); | ldns_buffer_free(str_buf); | ||||
return LDNS_STATUS_INVALID_STR; | return LDNS_STATUS_INVALID_STR; | ||||
} | } | ||||
} else { | } else { | ||||
serv = getservbyname(token, proto_str); | serv = getservbyname(token, proto_str); | ||||
if (!serv) { | |||||
serv = getservbyname(token, lc_proto_str); | |||||
} | |||||
if (!serv && (lc_token = strdup(token))) { | |||||
for (c = lc_token; *c; c++) { | |||||
*c = tolower((unsigned char)*c); | |||||
} | |||||
serv = getservbyname(lc_token, proto_str); | |||||
if (!serv) { | |||||
serv = getservbyname(lc_token, lc_proto_str); | |||||
} | |||||
free(lc_token); | |||||
} | |||||
if (serv) { | if (serv) { | ||||
serv_port = (int) ntohs((uint16_t) serv->s_port); | serv_port = (int) ntohs((uint16_t) serv->s_port); | ||||
} else { | } else { | ||||
serv_port = atoi(token); | serv_port = atoi(token); | ||||
} | } | ||||
if (serv_port < 0 || serv_port > 65535) { | |||||
LDNS_FREE(bitmap); | |||||
LDNS_FREE(token); | |||||
ldns_buffer_free(str_buf); | |||||
free(proto_str); | |||||
free(lc_proto_str); | |||||
return LDNS_STATUS_INVALID_STR; | |||||
} | |||||
if (serv_port / 8 >= bm_len) { | if (serv_port / 8 >= bm_len) { | ||||
uint8_t *b2 = LDNS_XREALLOC(bitmap, uint8_t, (serv_port / 8) + 1); | uint8_t *b2 = LDNS_XREALLOC(bitmap, uint8_t, (serv_port / 8) + 1); | ||||
if(!b2) { | if(!b2) { | ||||
LDNS_FREE(bitmap); | LDNS_FREE(bitmap); | ||||
LDNS_FREE(token); | LDNS_FREE(token); | ||||
ldns_buffer_free(str_buf); | ldns_buffer_free(str_buf); | ||||
free(proto_str); | free(proto_str); | ||||
free(lc_proto_str); | |||||
return LDNS_STATUS_INVALID_STR; | return LDNS_STATUS_INVALID_STR; | ||||
} | } | ||||
bitmap = b2; | bitmap = b2; | ||||
/* set to zero to be sure */ | /* set to zero to be sure */ | ||||
for (; bm_len <= serv_port / 8; bm_len++) { | for (; bm_len <= serv_port / 8; bm_len++) { | ||||
bitmap[bm_len] = 0; | bitmap[bm_len] = 0; | ||||
} | } | ||||
} | } | ||||
ldns_set_bit(bitmap + (serv_port / 8), 7 - (serv_port % 8), true); | ldns_set_bit(bitmap + (serv_port / 8), 7 - (serv_port % 8), true); | ||||
} | } | ||||
} | } | ||||
if (!proto_str || !bitmap) { | if (!proto_str || !bitmap) { | ||||
LDNS_FREE(bitmap); | LDNS_FREE(bitmap); | ||||
LDNS_FREE(token); | LDNS_FREE(token); | ||||
ldns_buffer_free(str_buf); | ldns_buffer_free(str_buf); | ||||
free(proto_str); | free(proto_str); | ||||
free(lc_proto_str); | |||||
return LDNS_STATUS_INVALID_STR; | return LDNS_STATUS_INVALID_STR; | ||||
} | } | ||||
data = LDNS_XMALLOC(uint8_t, bm_len + 1); | data = LDNS_XMALLOC(uint8_t, bm_len + 1); | ||||
if(!data) { | if(!data) { | ||||
LDNS_FREE(token); | LDNS_FREE(token); | ||||
ldns_buffer_free(str_buf); | ldns_buffer_free(str_buf); | ||||
LDNS_FREE(bitmap); | LDNS_FREE(bitmap); | ||||
free(proto_str); | free(proto_str); | ||||
free(lc_proto_str); | |||||
return LDNS_STATUS_INVALID_STR; | return LDNS_STATUS_INVALID_STR; | ||||
} | } | ||||
if (proto_str) | if (proto_str) | ||||
proto = getprotobyname(proto_str); | proto = getprotobyname(proto_str); | ||||
if (!proto) { | |||||
proto = getprotobyname(lc_proto_str); | |||||
} | |||||
if (proto) { | if (proto) { | ||||
data[0] = (uint8_t) proto->p_proto; | data[0] = (uint8_t) proto->p_proto; | ||||
} else if (proto_str) { | } else if (proto_str) { | ||||
data[0] = (uint8_t) atoi(proto_str); | data[0] = (uint8_t) atoi(proto_str); | ||||
} | } | ||||
memcpy(data + 1, bitmap, (size_t) bm_len); | memcpy(data + 1, bitmap, (size_t) bm_len); | ||||
*rd = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_WKS, (uint16_t) (bm_len + 1), data); | *rd = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_WKS, (uint16_t) (bm_len + 1), data); | ||||
LDNS_FREE(data); | LDNS_FREE(data); | ||||
LDNS_FREE(token); | LDNS_FREE(token); | ||||
ldns_buffer_free(str_buf); | ldns_buffer_free(str_buf); | ||||
LDNS_FREE(bitmap); | LDNS_FREE(bitmap); | ||||
free(proto_str); | free(proto_str); | ||||
free(lc_proto_str); | |||||
#ifdef HAVE_ENDSERVENT | #ifdef HAVE_ENDSERVENT | ||||
endservent(); | endservent(); | ||||
#endif | #endif | ||||
#ifdef HAVE_ENDPROTOENT | #ifdef HAVE_ENDPROTOENT | ||||
endprotoent(); | endprotoent(); | ||||
#endif | #endif | ||||
if(!*rd) return LDNS_STATUS_MEM_ERR; | if(!*rd) return LDNS_STATUS_MEM_ERR; | ||||
▲ Show 20 Lines • Show All 114 Lines • ▼ Show 20 Lines | ldns_str2rdf_ipseckey(ldns_rdf **rd, const char *str) | ||||
} | } | ||||
if (gateway_type == 1) { | if (gateway_type == 1) { | ||||
status = ldns_str2rdf_a(&gateway_rdf, gateway); | status = ldns_str2rdf_a(&gateway_rdf, gateway); | ||||
} else if (gateway_type == 2) { | } else if (gateway_type == 2) { | ||||
status = ldns_str2rdf_aaaa(&gateway_rdf, gateway); | status = ldns_str2rdf_aaaa(&gateway_rdf, gateway); | ||||
} else if (gateway_type == 3) { | } else if (gateway_type == 3) { | ||||
status = ldns_str2rdf_dname(&gateway_rdf, gateway); | status = ldns_str2rdf_dname(&gateway_rdf, gateway); | ||||
} else if (gateway_type > 3) { | |||||
status = LDNS_STATUS_INVALID_STR; | |||||
} | } | ||||
if (status != LDNS_STATUS_OK) { | if (status != LDNS_STATUS_OK) { | ||||
if (gateway) | if (gateway) | ||||
LDNS_FREE(gateway); | LDNS_FREE(gateway); | ||||
if (publickey) | if (publickey) | ||||
LDNS_FREE(publickey); | LDNS_FREE(publickey); | ||||
LDNS_FREE(token); | LDNS_FREE(token); | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | ldns_str2rdf_ipseckey(ldns_rdf **rd, const char *str) | ||||
*rd = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_IPSECKEY, (uint16_t) ipseckey_len, data); | *rd = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_IPSECKEY, (uint16_t) ipseckey_len, data); | ||||
if (gateway) | if (gateway) | ||||
LDNS_FREE(gateway); | LDNS_FREE(gateway); | ||||
if (publickey) | if (publickey) | ||||
LDNS_FREE(publickey); | LDNS_FREE(publickey); | ||||
LDNS_FREE(token); | LDNS_FREE(token); | ||||
ldns_buffer_free(str_buf); | ldns_buffer_free(str_buf); | ||||
ldns_rdf_free(gateway_rdf); | ldns_rdf_deep_free(gateway_rdf); | ||||
ldns_rdf_free(publickey_rdf); | ldns_rdf_deep_free(publickey_rdf); | ||||
LDNS_FREE(data); | LDNS_FREE(data); | ||||
if(!*rd) return LDNS_STATUS_MEM_ERR; | if(!*rd) return LDNS_STATUS_MEM_ERR; | ||||
return LDNS_STATUS_OK; | return LDNS_STATUS_OK; | ||||
} | } | ||||
ldns_status | ldns_status | ||||
ldns_str2rdf_ilnp64(ldns_rdf **rd, const char *str) | ldns_str2rdf_ilnp64(ldns_rdf **rd, const char *str) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 110 Lines • ▼ Show 20 Lines | ldns_str2rdf_long_str(ldns_rdf **rd, const char *str) | ||||
while (parse_char(&ch, &str)) { | while (parse_char(&ch, &str)) { | ||||
*dp++ = ch; | *dp++ = ch; | ||||
if (dp - data > LDNS_MAX_RDFLEN) { | if (dp - data > LDNS_MAX_RDFLEN) { | ||||
LDNS_FREE(data); | LDNS_FREE(data); | ||||
return LDNS_STATUS_INVALID_STR; | return LDNS_STATUS_INVALID_STR; | ||||
} | } | ||||
} | } | ||||
if (! str) { | if (! str) { | ||||
LDNS_FREE(data); | |||||
return LDNS_STATUS_SYNTAX_BAD_ESCAPE; | return LDNS_STATUS_SYNTAX_BAD_ESCAPE; | ||||
} | } | ||||
length = (size_t)(dp - data); | if (!(length = (size_t)(dp - data))) { | ||||
/* An empty string is a data buffer of 0 bytes. The rdf for | |||||
* this long string has to have length 0 and point to NULL. | |||||
*/ | |||||
LDNS_FREE(data); | |||||
data = NULL; | |||||
} else { | |||||
/* Lose the overmeasure */ | /* Lose the overmeasure */ | ||||
data = LDNS_XREALLOC(dp = data, uint8_t, length); | data = LDNS_XREALLOC(dp = data, uint8_t, length); | ||||
if (! data) { | if (! data) { | ||||
LDNS_FREE(dp); | LDNS_FREE(dp); | ||||
return LDNS_STATUS_MEM_ERR; | return LDNS_STATUS_MEM_ERR; | ||||
} | } | ||||
} | |||||
/* Create rdf */ | /* Create rdf */ | ||||
*rd = ldns_rdf_new(LDNS_RDF_TYPE_LONG_STR, length, data); | *rd = ldns_rdf_new(LDNS_RDF_TYPE_LONG_STR, length, data); | ||||
if (! *rd) { | if (! *rd) { | ||||
LDNS_FREE(data); | LDNS_FREE(data); | ||||
return LDNS_STATUS_MEM_ERR; | return LDNS_STATUS_MEM_ERR; | ||||
} | } | ||||
return LDNS_STATUS_OK; | return LDNS_STATUS_OK; | ||||
} | } | ||||
ldns_status | ldns_status | ||||
ldns_str2rdf_hip(ldns_rdf **rd, const char *str) | ldns_str2rdf_hip(ldns_rdf **rd, const char *str) | ||||
{ | { | ||||
const char *hit = strchr(str, ' ') + 1; | const char *hit = str == NULL ? NULL : strchr(str, ' '); | ||||
const char *pk = hit == NULL ? NULL : strchr(hit, ' ') + 1; | const char *pk = hit == NULL ? NULL : strchr(hit + 1, ' '); | ||||
size_t hit_size = hit == NULL ? 0 | size_t hit_size = hit == NULL ? 0 | ||||
: pk == NULL ? strlen(hit) : (size_t) (pk - hit) - 1; | : pk == NULL ? strlen(hit + 1) : (size_t) (pk - hit) - 1; | ||||
size_t pk_size = pk == NULL ? 0 : strlen(pk); | size_t pk_size = pk == NULL ? 0 : strlen(pk + 1); | ||||
size_t hit_wire_size = (hit_size + 1) / 2; | size_t hit_wire_size = (hit_size + 1) / 2; | ||||
size_t pk_wire_size = ldns_b64_pton_calculate_size(pk_size); | size_t pk_wire_size = ldns_b64_pton_calculate_size(pk_size); | ||||
size_t rdf_size = 4 + hit_wire_size + pk_wire_size; | size_t rdf_size = 4 + hit_wire_size + pk_wire_size; | ||||
char *endptr; /* utility var for strtol usage */ | char *endptr; /* utility var for strtol usage */ | ||||
int algorithm = strtol(str, &endptr, 10); | int algorithm = str == NULL ? 0 : strtol(str, &endptr, 10); | ||||
uint8_t *data, *dp; | uint8_t *data, *dp; | ||||
int hi, lo, written; | int hi, lo, written; | ||||
if (hit_size == 0 || pk_size == 0 || (hit_size + 1) / 2 > 255 | if (hit_size == 0 || pk_size == 0 || (hit_size + 1) / 2 > 255 | ||||
|| rdf_size > LDNS_MAX_RDFLEN | || rdf_size > LDNS_MAX_RDFLEN | ||||
|| algorithm < 0 || algorithm > 255 | || algorithm < 0 || algorithm > 255 | ||||
|| (errno != 0 && algorithm == 0) /* out of range */ | || (errno != 0 && algorithm == 0) /* out of range */ | ||||
|| endptr == str /* no digits */) { | || endptr == str /* no digits */) { | ||||
return LDNS_STATUS_SYNTAX_ERR; | return LDNS_STATUS_SYNTAX_ERR; | ||||
} | } | ||||
hit += 1; | |||||
pk += 1; | |||||
if ((data = LDNS_XMALLOC(uint8_t, rdf_size)) == NULL) { | if ((data = LDNS_XMALLOC(uint8_t, rdf_size)) == NULL) { | ||||
return LDNS_STATUS_MEM_ERR; | return LDNS_STATUS_MEM_ERR; | ||||
} | } | ||||
/* From RFC 5205 section 5. HIP RR Storage Format: | /* From RFC 5205 section 5. HIP RR Storage Format: | ||||
************************************************* | ************************************************* | ||||
0 1 2 3 | 0 1 2 3 | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | ldns_str2rdf_hip(ldns_rdf **rd, const char *str) | ||||
/* Create rdf */ | /* Create rdf */ | ||||
if (! (*rd = ldns_rdf_new(LDNS_RDF_TYPE_HIP, rdf_size, data))) { | if (! (*rd = ldns_rdf_new(LDNS_RDF_TYPE_HIP, rdf_size, data))) { | ||||
LDNS_FREE(data); | LDNS_FREE(data); | ||||
return LDNS_STATUS_MEM_ERR; | return LDNS_STATUS_MEM_ERR; | ||||
} | } | ||||
return LDNS_STATUS_OK; | return LDNS_STATUS_OK; | ||||
} | } | ||||
/* Implementation mimics ldns_str2rdf_ipseckey */ | |||||
ldns_status | |||||
ldns_str2rdf_amtrelay(ldns_rdf **rd, const char *str) | |||||
{ | |||||
/* From draft-ietf-mboned-driad-amt-discovery | |||||
* Section 4.2. AMTRELAY RData Format | |||||
************************************************* | |||||
0 1 2 3 | |||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 | |||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |||||
| precedence |D| type | | | |||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | |||||
~ relay ~ | |||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ | |||||
uint8_t precedence = 0; | |||||
uint8_t relay_type = 0; | |||||
uint8_t discovery_optional = 0; | |||||
char* relay = NULL; | |||||
uint8_t *data; | |||||
ldns_buffer *str_buf; | |||||
char *token; | |||||
int token_count = 0; | |||||
int amtrelay_len = 0; | |||||
ldns_rdf* relay_rdf = NULL; | |||||
ldns_status status = LDNS_STATUS_OK; | |||||
if(strlen(str) == 0) | |||||
token = LDNS_XMALLOC(char, 256); | |||||
else token = LDNS_XMALLOC(char, strlen(str)+2); | |||||
if(!token) return LDNS_STATUS_MEM_ERR; | |||||
str_buf = LDNS_MALLOC(ldns_buffer); | |||||
if(!str_buf) {LDNS_FREE(token); return LDNS_STATUS_MEM_ERR;} | |||||
ldns_buffer_new_frm_data(str_buf, (char *)str, strlen(str)); | |||||
if(ldns_buffer_status(str_buf) != LDNS_STATUS_OK) { | |||||
LDNS_FREE(str_buf); | |||||
LDNS_FREE(token); | |||||
return LDNS_STATUS_MEM_ERR; | |||||
} | |||||
while(ldns_bget_token(str_buf, token, "\t\n ", strlen(str)) > 0) { | |||||
switch (token_count) { | |||||
case 0: | |||||
precedence = (uint8_t)atoi(token); | |||||
break; | |||||
case 1: | |||||
discovery_optional = (uint8_t)atoi(token); | |||||
if (discovery_optional != 0 && | |||||
discovery_optional != 1) { | |||||
LDNS_FREE(relay); | |||||
LDNS_FREE(token); | |||||
ldns_buffer_free(str_buf); | |||||
return LDNS_STATUS_INVALID_STR; | |||||
} | |||||
break; | |||||
case 2: | |||||
relay_type = (uint8_t)atoi(token); | |||||
break; | |||||
case 3: | |||||
relay = strdup(token); | |||||
if (!relay || (relay_type == 0 && | |||||
(token[0] != '.' || token[1] != '\0'))) { | |||||
LDNS_FREE(relay); | |||||
LDNS_FREE(token); | |||||
ldns_buffer_free(str_buf); | |||||
return LDNS_STATUS_INVALID_STR; | |||||
} | |||||
break; | |||||
default: | |||||
LDNS_FREE(token); | |||||
ldns_buffer_free(str_buf); | |||||
return LDNS_STATUS_INVALID_STR; | |||||
break; | |||||
} | |||||
token_count++; | |||||
} | |||||
if (!relay && relay_type > 0) { | |||||
if (relay) | |||||
LDNS_FREE(relay); | |||||
LDNS_FREE(token); | |||||
ldns_buffer_free(str_buf); | |||||
return LDNS_STATUS_INVALID_STR; | |||||
} | |||||
if (relay_type == 1) { | |||||
status = ldns_str2rdf_a(&relay_rdf, relay); | |||||
} else if (relay_type == 2) { | |||||
status = ldns_str2rdf_aaaa(&relay_rdf, relay); | |||||
} else if (relay_type == 3) { | |||||
status = ldns_str2rdf_dname(&relay_rdf, relay); | |||||
} else if (relay_type > 3) { | |||||
status = LDNS_STATUS_INVALID_STR; | |||||
} | |||||
if (status != LDNS_STATUS_OK) { | |||||
if (relay) | |||||
LDNS_FREE(relay); | |||||
LDNS_FREE(token); | |||||
ldns_buffer_free(str_buf); | |||||
return LDNS_STATUS_INVALID_STR; | |||||
} | |||||
/* now copy all into one amtrelay rdf */ | |||||
if (relay_type) | |||||
amtrelay_len = 2 + (int)ldns_rdf_size(relay_rdf); | |||||
else | |||||
amtrelay_len = 2; | |||||
data = LDNS_XMALLOC(uint8_t, amtrelay_len); | |||||
if(!data) { | |||||
if (relay) | |||||
LDNS_FREE(relay); | |||||
LDNS_FREE(token); | |||||
ldns_buffer_free(str_buf); | |||||
if (relay_rdf) ldns_rdf_free(relay_rdf); | |||||
return LDNS_STATUS_MEM_ERR; | |||||
} | |||||
data[0] = precedence; | |||||
data[1] = relay_type; | |||||
data[1] |= (discovery_optional << 7); | |||||
if (relay_type) { | |||||
memcpy(data + 2, | |||||
ldns_rdf_data(relay_rdf), ldns_rdf_size(relay_rdf)); | |||||
} | |||||
*rd = ldns_rdf_new_frm_data( LDNS_RDF_TYPE_AMTRELAY | |||||
, (uint16_t) amtrelay_len, data); | |||||
if (relay) | |||||
LDNS_FREE(relay); | |||||
LDNS_FREE(token); | |||||
ldns_buffer_free(str_buf); | |||||
ldns_rdf_free(relay_rdf); | |||||
LDNS_FREE(data); | |||||
if(!*rd) return LDNS_STATUS_MEM_ERR; | |||||
return LDNS_STATUS_OK; | |||||
} | |||||
#ifdef RRTYPE_SVCB_HTTPS | |||||
static int | |||||
network_uint16_cmp(const void *a, const void *b) | |||||
{ | |||||
return ((int)ldns_read_uint16(a)) - ((int)ldns_read_uint16(b)); | |||||
} | |||||
static ldns_status parse_svcparam_key(const char **s, ldns_svcparam_key *key); | |||||
static ldns_status | |||||
parse_svcparam_mandatory(const char **s, uint8_t **dp, uint8_t *eod) | |||||
{ | |||||
bool quoted = false; | |||||
uint8_t *keys = *dp; | |||||
int prev_key; | |||||
if (**s == '"') { | |||||
*s += 1; | |||||
quoted = true; | |||||
} | |||||
for (;;) { | |||||
ldns_status st; | |||||
ldns_svcparam_key key; | |||||
if ((st = parse_svcparam_key(s, &key))) | |||||
return st; | |||||
if (*dp + 2 > eod) | |||||
return LDNS_STATUS_RDATA_OVERFLOW; | |||||
ldns_write_uint16(*dp, key); | |||||
*dp += 2; | |||||
if (**s == ',') | |||||
*s += 1; | |||||
else | |||||
break; | |||||
} | |||||
if (quoted) { | |||||
if (**s != '"') | |||||
return LDNS_STATUS_INVALID_STR; | |||||
*s += 1; | |||||
} | |||||
if (*dp - keys == 0) | |||||
return LDNS_STATUS_SYNTAX_SVCPARAM_VALUE_ERR; | |||||
if (**s && !isspace((unsigned char)**s)) | |||||
return LDNS_STATUS_SYNTAX_SVCPARAM_VALUE_ERR; | |||||
/* In draft-ietf-dnsop-svcb-https-02 Section 7: | |||||
* | |||||
* In wire format, the keys are represented by their numeric | |||||
* values in network byte order, concatenated in ascending order. | |||||
*/ | |||||
qsort(keys, (*dp - keys) / 2, 2, network_uint16_cmp); | |||||
/* In draft-ietf-dnsop-svcb-https-02 Section 7: | |||||
* | |||||
* Keys ...<snip>... MUST NOT appear more than once. | |||||
*/ | |||||
prev_key = -1; | |||||
while (keys < *dp) { | |||||
uint16_t key = ldns_read_uint16(keys); | |||||
if (key == prev_key) { | |||||
/* "Be conservative in what you send, | |||||
* be liberal in what you accept" | |||||
* | |||||
* Instead of | |||||
* `return LDNS_STATUS_SVCPARAM_KEY_MORE_THAN_ONCE;`, | |||||
* | |||||
* we eliminate the double occurrence. | |||||
*/ | |||||
memmove(keys - 2, keys, *dp - keys); | |||||
*dp -= 2; | |||||
} else { | |||||
prev_key = key; | |||||
keys += 2; | |||||
} | |||||
} | |||||
return LDNS_STATUS_OK; | |||||
} | |||||
INLINE bool parse_escape2(uint8_t *ch_p, const char** str_p) | |||||
{ *str_p += 1; return parse_escape(ch_p, str_p); } | |||||
static ldns_status | |||||
parse_svcparam_alpn(const char **s, uint8_t **dp, uint8_t *eod) | |||||
{ | |||||
uint8_t *val; | |||||
size_t len; | |||||
if (*dp + 1 > eod) | |||||
return LDNS_STATUS_RDATA_OVERFLOW; | |||||
*dp += 1; | |||||
val = *dp; | |||||
if (**s == '"') { | |||||
*s += 1; | |||||
while (**s != '"') { | |||||
if (**s == 0) | |||||
return LDNS_STATUS_INVALID_STR; | |||||
else if (**s == ',') { | |||||
len = *dp - val; | |||||
if (len == 0 || len > 255) | |||||
return LDNS_STATUS_INVALID_STR; | |||||
val[-1] = len; | |||||
if (*dp + 1 > eod) | |||||
return LDNS_STATUS_RDATA_OVERFLOW; | |||||
*dp += 1; | |||||
val = *dp; | |||||
*s += 1; | |||||
} else if (*dp + 1 > eod) | |||||
return LDNS_STATUS_RDATA_OVERFLOW; | |||||
else if (**s != '\\') | |||||
*(*dp)++ = (uint8_t)*(*s)++; | |||||
else if (!parse_escape2(*dp, s)) | |||||
return LDNS_STATUS_SYNTAX_BAD_ESCAPE; | |||||
else | |||||
*dp += 1; | |||||
} | |||||
*s += 1; | |||||
} else while (**s && !isspace((unsigned char)**s)) { | |||||
if (**s == ',') { | |||||
len = *dp - val; | |||||
if (len == 0 || len > 255) | |||||
return LDNS_STATUS_INVALID_STR; | |||||
val[-1] = len; | |||||
if (*dp + 1 > eod) | |||||
return LDNS_STATUS_RDATA_OVERFLOW; | |||||
*dp += 1; | |||||
val = *dp; | |||||
*s += 1; | |||||
} else if (*dp + 1 > eod) | |||||
return LDNS_STATUS_RDATA_OVERFLOW; | |||||
else if (**s != '\\') | |||||
*(*dp)++ = (uint8_t)*(*s)++; | |||||
else if (!parse_escape2(*dp, s)) | |||||
return LDNS_STATUS_SYNTAX_BAD_ESCAPE; | |||||
else | |||||
*dp += 1; | |||||
} | |||||
len = *dp - val; | |||||
if (len == 0 || len > 255) | |||||
return LDNS_STATUS_INVALID_STR; | |||||
val[-1] = len; | |||||
return **s && !isspace((unsigned char)**s) | |||||
? LDNS_STATUS_SYNTAX_SVCPARAM_VALUE_ERR | |||||
: LDNS_STATUS_OK; | |||||
} | |||||
static ldns_status | |||||
parse_svcparam_value(const char **s, uint8_t **dp, uint8_t *eod) | |||||
{ | |||||
if (**s == '"') { | |||||
*s += 1; | |||||
while (**s != '"') { | |||||
if (**s == 0) | |||||
return LDNS_STATUS_INVALID_STR; | |||||
else if (*dp + 1 > eod) | |||||
return LDNS_STATUS_RDATA_OVERFLOW; | |||||
else if (**s != '\\') | |||||
*(*dp)++ = (uint8_t)*(*s)++; | |||||
else if (!parse_escape2(*dp, s)) | |||||
return LDNS_STATUS_SYNTAX_BAD_ESCAPE; | |||||
else | |||||
*dp += 1; | |||||
} | |||||
*s += 1; | |||||
} else while (**s && !isspace((unsigned char)**s)) { | |||||
if (*dp + 1 > eod) | |||||
return LDNS_STATUS_RDATA_OVERFLOW; | |||||
else if (**s != '\\') | |||||
*(*dp)++ = (uint8_t)*(*s)++; | |||||
else if (!parse_escape2(*dp, s)) | |||||
return LDNS_STATUS_SYNTAX_BAD_ESCAPE; | |||||
else | |||||
*dp += 1; | |||||
} | |||||
return **s && !isspace((unsigned char)**s) | |||||
? LDNS_STATUS_SYNTAX_SVCPARAM_VALUE_ERR | |||||
: LDNS_STATUS_OK; | |||||
} | |||||
static ldns_status | |||||
parse_svcparam_port(const char **s, uint8_t **dp, uint8_t *eod) | |||||
{ | |||||
uint8_t *val = *dp; | |||||
ldns_status st; | |||||
size_t len; | |||||
char num_str[6]; | |||||
char *endptr; | |||||
unsigned long int num; | |||||
if ((st = parse_svcparam_value(s, dp, eod))) | |||||
return st; | |||||
len = *dp - val; | |||||
if (len == 0 || len > 5) | |||||
return LDNS_STATUS_SYNTAX_SVCPARAM_VALUE_ERR; | |||||
memcpy(num_str, val, len); | |||||
num_str[len] = 0; | |||||
num = strtoul(num_str, &endptr, 10); | |||||
if (*endptr) | |||||
return LDNS_STATUS_SYNTAX_SVCPARAM_VALUE_ERR; | |||||
ldns_write_uint16(val, num); | |||||
*dp = val + 2; | |||||
return LDNS_STATUS_OK; | |||||
} | |||||
static ldns_status | |||||
parse_svcparam_ipv4hint(const char **s, uint8_t **dp, uint8_t *eod) | |||||
{ | |||||
bool quoted = false; | |||||
if (**s == '"') { | |||||
*s += 1; | |||||
quoted = true; | |||||
} | |||||
for (;;) { | |||||
const char *ipv4_start = *s; | |||||
char ipv4_str[16]; | |||||
size_t len; | |||||
while (isdigit((unsigned char)**s) || **s == '.') | |||||
*s += 1; | |||||
len = *s - ipv4_start; | |||||
if (len == 0 || len > 15) | |||||
return LDNS_STATUS_SYNTAX_SVCPARAM_VALUE_ERR; | |||||
if (*dp + 4 > eod) | |||||
return LDNS_STATUS_RDATA_OVERFLOW; | |||||
memcpy(ipv4_str, ipv4_start, len); | |||||
ipv4_str[len] = 0; | |||||
if (inet_pton(AF_INET, ipv4_str, *dp) != 1) | |||||
return LDNS_STATUS_SYNTAX_SVCPARAM_VALUE_ERR; | |||||
*dp += 4; | |||||
if (**s == ',') | |||||
*s += 1; | |||||
else | |||||
break; | |||||
} | |||||
if (quoted) { | |||||
if (**s != '"') | |||||
return LDNS_STATUS_INVALID_STR; | |||||
*s += 1; | |||||
} | |||||
return **s && !isspace((unsigned char)**s) | |||||
? LDNS_STATUS_SYNTAX_SVCPARAM_VALUE_ERR | |||||
: LDNS_STATUS_OK; | |||||
} | |||||
static ldns_status | |||||
parse_svcparam_ech(const char **s, uint8_t **dp, uint8_t *eod) | |||||
{ | |||||
bool quoted = false; | |||||
const char *b64_str; | |||||
size_t len, pad, out_len; | |||||
char in_buf[4096]; | |||||
char *in = in_buf; | |||||
int out; | |||||
if (**s == '"') { | |||||
*s += 1; | |||||
quoted = true; | |||||
} | |||||
b64_str = *s; | |||||
while (isalnum((unsigned char)**s) || **s == '+' | |||||
|| **s == '/' | |||||
|| **s == '=') | |||||
*s += 1; | |||||
len = *s - b64_str; | |||||
pad = len % 4; | |||||
pad = pad ? 4 - pad : 0; | |||||
if (len == 0 || pad == 3) | |||||
return LDNS_STATUS_SYNTAX_SVCPARAM_VALUE_ERR; | |||||
if (quoted) { | |||||
if (**s != '"') | |||||
return LDNS_STATUS_INVALID_STR; | |||||
*s += 1; | |||||
} | |||||
if (**s && !isspace((unsigned char)**s)) | |||||
return LDNS_STATUS_SYNTAX_SVCPARAM_VALUE_ERR; | |||||
out_len = ldns_b64_pton_calculate_size(len); | |||||
if (*dp + out_len > eod) | |||||
return LDNS_STATUS_RDATA_OVERFLOW; | |||||
if (len + pad > sizeof(in_buf) - 1 | |||||
&& !(in = LDNS_XMALLOC(char, len + pad + 1))) | |||||
return LDNS_STATUS_MEM_ERR; | |||||
memcpy(in, b64_str, len); | |||||
while (pad--) | |||||
in[len++] = '='; | |||||
in[len] = 0; | |||||
out = ldns_b64_pton(in, *dp, out_len); | |||||
if (in != in_buf) | |||||
LDNS_FREE(in); | |||||
if (out <= 0) | |||||
return LDNS_STATUS_SYNTAX_SVCPARAM_VALUE_ERR; | |||||
*dp += out; | |||||
return LDNS_STATUS_OK; | |||||
} | |||||
static ldns_status | |||||
parse_svcparam_ipv6hint(const char **s, uint8_t **dp, uint8_t *eod) | |||||
{ | |||||
bool quoted = false; | |||||
if (**s == '"') { | |||||
*s += 1; | |||||
quoted = true; | |||||
} | |||||
for (;;) { | |||||
const char *ipv6_start = *s; | |||||
char ipv6_str[INET6_ADDRSTRLEN]; | |||||
size_t len; | |||||
while (isxdigit((unsigned char)**s) || **s == ':' || **s == '.') | |||||
*s += 1; | |||||
len = *s - ipv6_start; | |||||
if (len == 0 || len > INET6_ADDRSTRLEN) | |||||
return LDNS_STATUS_SYNTAX_SVCPARAM_VALUE_ERR; | |||||
if (*dp + 16 > eod) | |||||
return LDNS_STATUS_RDATA_OVERFLOW; | |||||
memcpy(ipv6_str, ipv6_start, len); | |||||
ipv6_str[len] = 0; | |||||
if (inet_pton(AF_INET6, ipv6_str, *dp) != 1) | |||||
return LDNS_STATUS_SYNTAX_SVCPARAM_VALUE_ERR; | |||||
*dp += 16; | |||||
if (**s == ',') | |||||
*s += 1; | |||||
else | |||||
break; | |||||
} | |||||
if (quoted) { | |||||
if (**s != '"') | |||||
return LDNS_STATUS_INVALID_STR; | |||||
*s += 1; | |||||
} | |||||
return **s && !isspace((unsigned char)**s) | |||||
? LDNS_STATUS_SYNTAX_SVCPARAM_VALUE_ERR | |||||
: LDNS_STATUS_OK; | |||||
} | |||||
struct struct_svcparam_key_def { | |||||
const char *str; | |||||
size_t len; | |||||
}; | |||||
typedef struct struct_svcparam_key_def svcparam_key_def; | |||||
static svcparam_key_def svcparam_key_defs[] = { { "mandatory" , 9 } | |||||
, { "alpn" , 4 } | |||||
, { "no-default-alpn", 15 } | |||||
, { "port" , 4 } | |||||
, { "ipv4hint" , 8 } | |||||
, { "ech" , 3 } | |||||
, { "ipv6hint" , 8 } | |||||
, { "dohpath" , 7 } }; | |||||
static const size_t svcparam_key_defs_len = sizeof(svcparam_key_defs) | |||||
/ sizeof(svcparam_key_def); | |||||
/* svcparam_key2buffer_str() should actually be in host2str.c, but we need the | |||||
* svcparam_key_defs for it and it is not an exposed symbol anyway. | |||||
*/ | |||||
ldns_status svcparam_key2buffer_str(ldns_buffer *output, uint16_t key) | |||||
{ | |||||
if (key <= LDNS_SVCPARAM_KEY_LAST_KEY) | |||||
ldns_buffer_write_string(output, svcparam_key_defs[key].str); | |||||
else | |||||
ldns_buffer_printf(output, "key%d", (int)key); | |||||
return ldns_buffer_status(output); | |||||
} | |||||
static ldns_status | |||||
parse_svcparam_key(const char **s, ldns_svcparam_key *key) | |||||
{ | |||||
size_t i, len; | |||||
const char *key_str = *s; | |||||
char num_str[6]; | |||||
char *endptr; | |||||
unsigned long int num; | |||||
/* parse key */ | |||||
while (islower((unsigned char)**s) || isdigit((unsigned char)**s) | |||||
|| **s == '-') | |||||
*s += 1; | |||||
len = *s - key_str; | |||||
for (i = 0; i < svcparam_key_defs_len; i++) { | |||||
if (len == svcparam_key_defs[i].len | |||||
&& !strncmp(key_str, svcparam_key_defs[i].str, len)) { | |||||
*key = i; | |||||
return LDNS_STATUS_OK; | |||||
} | |||||
} | |||||
/* Also allow "echconfig" from earlier draft versions. */ | |||||
if (len == 9 && !strncmp(key_str, "echconfig", 9)) { | |||||
*key = LDNS_SVCPARAM_KEY_ECH; | |||||
return LDNS_STATUS_OK; | |||||
} | |||||
if (len < 4 || len > 8 || strncmp(key_str, "key", 3)) | |||||
return LDNS_STATUS_SYNTAX_SVCPARAM_KEY_ERR; | |||||
memcpy(num_str, key_str + 3, len - 3); | |||||
num_str[len - 3] = 0; | |||||
num = strtoul(num_str, &endptr, 10); | |||||
if (*endptr || num > 65535) | |||||
return LDNS_STATUS_SYNTAX_SVCPARAM_KEY_ERR; | |||||
/* key65535 is Reserved to be an ("Invalid key"), though there is no | |||||
* physiological reason to deny usage. We restrict ourselves to the | |||||
* anatomical limitations only to maximize serviceability. | |||||
* ``` | |||||
* if (num == 65535) | |||||
* return LDNS_STATUS_RESERVED_SVCPARAM_KEY; | |||||
* ``` | |||||
*/ | |||||
*key = num; | |||||
return LDNS_STATUS_OK; | |||||
} | |||||
static ldns_status | |||||
parse_svcparam(const char **s, uint8_t **dp, uint8_t *eod) | |||||
{ | |||||
ldns_svcparam_key key; | |||||
ldns_status st; | |||||
uint8_t *val; | |||||
if (*dp + 4 > eod) | |||||
return LDNS_STATUS_RDATA_OVERFLOW; | |||||
if ((st = parse_svcparam_key(s, &key))) | |||||
return st; | |||||
ldns_write_uint16(*dp, key); | |||||
ldns_write_uint16(*dp + 2, 0); | |||||
*dp += 4; | |||||
if (isspace((unsigned char)**s) || !**s) | |||||
return LDNS_STATUS_OK; | |||||
else if (**s != '=') | |||||
return LDNS_STATUS_SYNTAX_ERR; | |||||
*s += 1; | |||||
val = *dp; | |||||
switch(key) { | |||||
case LDNS_SVCPARAM_KEY_MANDATORY: | |||||
st = parse_svcparam_mandatory(s, dp, eod); | |||||
break; | |||||
case LDNS_SVCPARAM_KEY_ALPN: | |||||
st = parse_svcparam_alpn(s, dp, eod); | |||||
break; | |||||
case LDNS_SVCPARAM_KEY_NO_DEFAULT_ALPN: | |||||
return LDNS_STATUS_NO_SVCPARAM_VALUE_EXPECTED; | |||||
case LDNS_SVCPARAM_KEY_PORT: | |||||
st = parse_svcparam_port(s, dp, eod); | |||||
break; | |||||
case LDNS_SVCPARAM_KEY_IPV4HINT: | |||||
st = parse_svcparam_ipv4hint(s, dp, eod); | |||||
break; | |||||
case LDNS_SVCPARAM_KEY_ECH: | |||||
st = parse_svcparam_ech(s, dp, eod); | |||||
break; | |||||
case LDNS_SVCPARAM_KEY_IPV6HINT: | |||||
st = parse_svcparam_ipv6hint(s, dp, eod); | |||||
break; | |||||
default: | |||||
st = parse_svcparam_value(s, dp, eod); | |||||
break; | |||||
} | |||||
if (st) | |||||
return st; | |||||
ldns_write_uint16(val - 2, *dp - val); | |||||
return LDNS_STATUS_OK; | |||||
} | |||||
static int | |||||
svcparam_ptr_cmp(const void *a, const void *b) | |||||
{ | |||||
uint8_t *x = *(uint8_t **)a , *y = *(uint8_t **)b; | |||||
uint16_t x_type = ldns_read_uint16(x), y_type = ldns_read_uint16(y); | |||||
uint16_t x_len , y_len; | |||||
if (x_type != y_type) | |||||
return x_type > y_type ? 1 : -1; | |||||
x_len = ldns_read_uint16(x + 2); | |||||
y_len = ldns_read_uint16(y + 2); | |||||
return x_len != y_len | |||||
? (x_len > y_len ? 1 : -1) | |||||
: (x_len == 0 ? 0 : memcmp(x + 4, y + 4, x_len)); | |||||
} | |||||
ldns_status | |||||
ldns_str2rdf_svcparams(ldns_rdf **rd, const char *str) | |||||
{ | |||||
uint8_t *data, *dp, *eod, *p, *new_data; | |||||
ldns_status st = LDNS_STATUS_OK; | |||||
size_t length, i; | |||||
size_t nparams = 0; | |||||
uint8_t **svcparams; | |||||
int prev_key; | |||||
if (!rd || !str) | |||||
return LDNS_STATUS_NULL; | |||||
length = strlen(str); | |||||
/* Worst case space requirement. We'll realloc to actual size later. */ | |||||
if (!(dp = data = LDNS_XMALLOC(uint8_t, length * 4))) | |||||
return LDNS_STATUS_MEM_ERR; | |||||
eod = data + length * 4; | |||||
/* Fill data with parsed bytes */ | |||||
for (;;) { | |||||
while (isspace((unsigned char)*str)) | |||||
str += 1; | |||||
if(!*str) | |||||
break; | |||||
if ((st = parse_svcparam(&str, &dp, eod))) { | |||||
LDNS_FREE(data); | |||||
return st; | |||||
} | |||||
nparams += 1; | |||||
} | |||||
/* draft-ietf-dnsop-svcb-https-02 in Section 2.2: | |||||
* | |||||
* SvcParamKeys SHALL appear in increasing numeric order | |||||
* | |||||
* A svcparams array (with pointers to the individual key, value pairs) | |||||
* is created to qsort the pairs in increasing numeric order. | |||||
*/ | |||||
if (!(svcparams = LDNS_XMALLOC(uint8_t *, nparams))) { | |||||
LDNS_FREE(data); | |||||
return LDNS_STATUS_MEM_ERR; | |||||
} | |||||
for ( p = data, i = 0 | |||||
; p < dp && i < nparams | |||||
; p += 4 + ldns_read_uint16(p + 2)) | |||||
svcparams[i++] = p; | |||||
qsort(svcparams, i, sizeof(uint8_t *), svcparam_ptr_cmp); | |||||
/* Write out the (key, value) pairs to a newly allocated data in | |||||
* sorted order. | |||||
*/ | |||||
length = dp - data; | |||||
if (!(new_data = LDNS_XMALLOC(uint8_t, length))) { | |||||
LDNS_FREE(data); | |||||
LDNS_FREE(svcparams); | |||||
return LDNS_STATUS_MEM_ERR; | |||||
} | |||||
prev_key = -1; | |||||
for ( p = new_data, i = 0 | |||||
; p < new_data + length && i < nparams | |||||
; p += 4 + ldns_read_uint16(p + 2), i += 1) { | |||||
uint16_t key = ldns_read_uint16(svcparams[i]); | |||||
/* In draft-ietf-dnsop-svcb-https-02 Section 2.1: | |||||
* | |||||
* SvcParams ...<snip>... keys MUST NOT be repeated. | |||||
* | |||||
* ldns will not impose this limitation on the library user, | |||||
* but we can merge completely equal repetitions into one. | |||||
* So, not doing | |||||
* ``` | |||||
* if (key == prev_key) | |||||
* return LDNS_STATUS_SVCPARAM_KEY_MORE_THAN_ONCE; | |||||
* ``` | |||||
* but instead: | |||||
*/ | |||||
if (key == prev_key && ldns_read_uint16(svcparams[i] + 2) | |||||
== ldns_read_uint16(svcparams[i - 1] + 2) | |||||
&& 0 == memcmp( svcparams[i ] + 4 | |||||
, svcparams[i - 1] + 4 | |||||
, ldns_read_uint16(svcparams[i] + 2))) { | |||||
p -= 4 + ldns_read_uint16(svcparams[i] + 2); | |||||
continue; | |||||
} | |||||
memcpy(p, svcparams[i], 4 + ldns_read_uint16(svcparams[i] + 2)); | |||||
prev_key = key; | |||||
} | |||||
LDNS_FREE(data); | |||||
LDNS_FREE(svcparams); | |||||
/* Create rdf */ | |||||
*rd = ldns_rdf_new(LDNS_RDF_TYPE_SVCPARAMS, p - new_data, new_data); | |||||
if (! *rd) { | |||||
LDNS_FREE(new_data); | |||||
return LDNS_STATUS_MEM_ERR; | |||||
} | |||||
return LDNS_STATUS_OK; | |||||
} | |||||
#else /* #ifdef RRTYPE_SVCB_HTTPS */ | |||||
ldns_status | |||||
ldns_str2rdf_svcparams(ldns_rdf **rd, const char *str) | |||||
{ | |||||
(void)rd; (void)str; | |||||
return LDNS_STATUS_NOT_IMPL; | |||||
} | |||||
#endif /* #ifdef RRTYPE_SVCB_HTTPS */ |