From 545c440973f9676efe7cb01edf9ec47b61023513 Mon Sep 17 00:00:00 2001 From: "John Hodge (sonata)" Date: Sun, 15 Feb 2015 20:38:39 +0800 Subject: [PATCH] Usermode/libnet - Starting work on DNS resolver --- Usermode/Libraries/libnet.so_src/Makefile | 2 +- Usermode/Libraries/libnet.so_src/address.c | 2 +- Usermode/Libraries/libnet.so_src/dns.c | 245 ++++++++++++++++++ .../Libraries/libnet.so_src/include/dns.h | 41 +++ .../Libraries/libnet.so_src/include_exp/net.h | 27 +- Usermode/Libraries/libnet.so_src/socket.c | 35 ++- 6 files changed, 341 insertions(+), 11 deletions(-) create mode 100644 Usermode/Libraries/libnet.so_src/dns.c create mode 100644 Usermode/Libraries/libnet.so_src/include/dns.h diff --git a/Usermode/Libraries/libnet.so_src/Makefile b/Usermode/Libraries/libnet.so_src/Makefile index 29abaaa0..8bbf4035 100644 --- a/Usermode/Libraries/libnet.so_src/Makefile +++ b/Usermode/Libraries/libnet.so_src/Makefile @@ -6,7 +6,7 @@ CPPFLAGS += CFLAGS += -Wall LDFLAGS += -lc -soname libnet.so -OBJ = main.o address.o socket.o +OBJ = main.o address.o socket.o dns.o BIN = libnet.so include ../Makefile.tpl diff --git a/Usermode/Libraries/libnet.so_src/address.c b/Usermode/Libraries/libnet.so_src/address.c index 8887362d..d103e64b 100644 --- a/Usermode/Libraries/libnet.so_src/address.c +++ b/Usermode/Libraries/libnet.so_src/address.c @@ -7,7 +7,7 @@ */ #include #include -//#include +#include // sprintf #include #define DEBUG 0 diff --git a/Usermode/Libraries/libnet.so_src/dns.c b/Usermode/Libraries/libnet.so_src/dns.c new file mode 100644 index 00000000..43213a48 --- /dev/null +++ b/Usermode/Libraries/libnet.so_src/dns.c @@ -0,0 +1,245 @@ +/* + * Acess2 Networking Toolkit + * By John Hodge (thePowersGang) + * + * dns.c + * - Hostname<->Address resolution + */ +#include // size_t / NULL +#include // uint*_t +#include // memcpy, strchrnul +#include +#include +#include "include/dns.h" + +// === PROTOTYPES === +size_t DNS_EncodeName(void *buf, const char *dotted_name); +int DNS_DecodeName(char dotted_name[256], const void *buf, size_t space); +int DNS_int_ParseRR(const void *buf, size_t space, char* name_p, enum eTypes* type_p, enum eClass* class_p, uint32_t* ttl_p, size_t* rdlength_p); +static uint16_t get16(const void *buf); +static size_t put16(void *buf, uint16_t val); + + +// === CODE === +int DNS_Query(int ServerAType, const void *ServerAddr, const char *name, enum eTypes type, enum eClass class, handle_record_t* handle_record, void *info) +{ + int namelen = DNS_EncodeName(NULL, name); + assert(namelen < 256); + size_t pos = 0; + char packet[ 512 ]; + assert( (6*2) + (namelen + 2*2) < 512 ); + // - Header + pos += put16(packet + pos, 0xAC00); // Identifier (arbitary) + pos += put16(packet + pos, (0 << 0) | (0 << 1) ); // Op : Query, Standard, no other flags + pos += put16(packet + pos, 1); // QDCount + pos += put16(packet + pos, 0); // ANCount + pos += put16(packet + pos, 0); // NSCount + pos += put16(packet + pos, 0); // ARCount + // - Question + pos += DNS_EncodeName(packet + pos, name); + pos += put16(packet + pos, type); // QType + pos += put16(packet + pos, class); // QClass + + assert(pos <= sizeof(packet)); + + // Send and wait for reply + // - Lock + // > TODO: Lock DNS queries + // - Send + int sock = Net_OpenSocket_UDP(ServerAType, ServerAddr, 53, 0); + if( sock < 0 ) { + // Connection failed + // TODO: Correctly report this failure with a useful error code + return 1; + } + int rv = _SysWrite(sock, packet, pos); + if( rv != pos ) { + // TODO: Error reporting + _SysClose(sock); + return 1; + } + // - Wait + int return_len = 0; + do { + return_len = _SysRead(sock, packet, sizeof(packet)); + } while( return_len == 0 ); + if( return_len < 0 ) { + // TODO: Error reporting + _SysClose(sock); + return 1; + } + _SysClose(sock); + // - Release + // > TODO: Lock DNS queries + + // For each response in the answer (and additional) sections, call the passed callback + char rr_name[256]; + unsigned int qd_count = get16(packet + 4); + unsigned int an_count = get16(packet + 6); + unsigned int ns_count = get16(packet + 8); + unsigned int ar_count = get16(packet + 10); + pos = 6*2; + // TODO: Can I safely assert / fail if qd_count is non-zero? + // - Questions, ignored + for( unsigned int i = 0; i < qd_count; i ++ ) { + pos += DNS_DecodeName(NULL, packet + pos, return_len - pos); + pos += 2*2; + } + // - Answers, pass on to handler + for( unsigned int i = 0; i < an_count; i ++ ) + { + enum eTypes type; + enum eClass class; + uint32_t ttl; + size_t rdlength; + int rv = DNS_int_ParseRR(packet + pos, return_len - pos, rr_name, &type, &class, &ttl, &rdlength); + if( rv < 0 ) { + return 1; + } + pos += rv; + + handle_record(info, rr_name, type, class, ttl, rdlength, packet + pos); + } + // Authority Records (should all be NS records) + for( unsigned int i = 0; i < ns_count; i ++ ) + { + size_t rdlength; + int rv = DNS_int_ParseRR(packet + pos, return_len - pos, rr_name, NULL, NULL, NULL, &rdlength); + if( rv < 0 ) { + return 1; + } + pos += rv; + } + // - Additional records, pass to handler + for( unsigned int i = 0; i < ar_count; i ++ ) + { + enum eTypes type; + enum eClass class; + uint32_t ttl; + size_t rdlength; + int rv = DNS_int_ParseRR(packet + pos, return_len - pos, rr_name, &type, &class, &ttl, &rdlength); + if( rv < 0 ) { + return 1; + } + pos += rv; + + handle_record(info, rr_name, type, class, ttl, rdlength, packet + pos); + } + + return 0; +} + +/// Encode a dotted name as a DNS name +size_t DNS_EncodeName(void *buf, const char *dotted_name) +{ + size_t ret = 0; + const char *str = dotted_name; + uint8_t *buf8 = buf; + while( *str ) + { + const char *next = strchr(str, '.'); + size_t seg_len = (next ? next - str : strlen(str)); + if( seg_len > 63 ) { + // Oops, too long (truncate) + seg_len = 63; + } + if( seg_len == 0 && next != NULL ) { + // '..' encountered, invalid (skip) + str = next+1; + continue ; + } + + if( buf8 ) + { + buf8[ret] = seg_len; + memcpy(buf8+ret+1, str, seg_len); + } + ret += 1 + seg_len; + + if( next == NULL ) { + // No trailing '.', assume it's there? Yes, need to be NUL terminated + if(buf8) buf8[ret] = 0; + ret ++; + break; + } + else { + str = next + 1; + } + } + return ret; +} + +// Decode a name (including trailing . for root) +int DNS_DecodeName(char dotted_name[256], const void *buf, size_t space) +{ + int consumed = 0; + int out_pos = 0; + const uint8_t *buf8 = buf; + while( *buf8 && space > 0 ) + { + if( consumed + 1 > space ) return -1; + uint8_t seg_len = *buf8; + buf8 ++; + consumed ++; + // Protocol violation (overflowed end of buffer) + if( consumed + seg_len > space ) + return -1; + // Protocol violation (segment too long) + if( seg_len >= 64 ) + return -1; + // Protocol violation (name was too long) + if( out_pos + seg_len + 1 > sizeof(dotted_name)-1 ) + return -1; + + // Read segment + memcpy(dotted_name + out_pos, buf8, seg_len); + buf8 += seg_len; + consumed += seg_len; + + // Place '.' + dotted_name[out_pos+seg_len+1] = '.'; + // Increment output counter + out_pos += seg_len + 1; + } + + dotted_name[out_pos] = '\0'; + return consumed; +} + +// Parse a Resource Record +int DNS_int_ParseRR(const void *buf, size_t space, char* name_p, enum eTypes* type_p, enum eClass* class_p, uint32_t* ttl_p, size_t* rdlength_p) +{ + const uint8_t *buf8 = buf; + size_t consumed = 0; + + // 1. Name + int rv = DNS_DecodeName(name_p, buf8, space); + if(rv < 0) return -1; + + buf8 += rv, consumed += rv; + + if( type_p ) + *type_p = get16(buf8); + buf8 += 2, consumed += 2; + + if( class_p ) + *class_p = get16(buf8); + buf8 += 2, consumed += 2; + + return consumed; +} + +static uint16_t get16(const void *buf) { + const uint8_t* buf8 = buf; + uint16_t rv = 0; + rv |= buf8[0]; + rv |= (uint16_t)buf8[1] << 8; + return rv; +} +static size_t put16(void *buf, uint16_t val) { + uint8_t* buf8 = buf; + buf8[0] = val & 0xFF; + buf8[1] = val >> 8; + return 2; +} + diff --git a/Usermode/Libraries/libnet.so_src/include/dns.h b/Usermode/Libraries/libnet.so_src/include/dns.h new file mode 100644 index 00000000..1f2dd61d --- /dev/null +++ b/Usermode/Libraries/libnet.so_src/include/dns.h @@ -0,0 +1,41 @@ +/* + */ +#ifndef _DNS_H_ +#define _DNS_H_ + +enum eTypes +{ + TYPE_A = 1, + TYPE_NS = 2, + TYPE_CNAME = 5, + TYPE_SOA = 6, + TYPE_NULL = 10, + TYPE_PTR = 12, + TYPE_HINFO = 13, + TYPE_MX = 15, + TYPE_TXT = 16, + QTYPE_STAR = 255, +}; + +enum eClass +{ + CLASS_IN, + CLASS_CH, // "Chaos" + CLASS_STAR = 255, +}; + +/** + * \brief Handler for a DNS record obtained by DNS_Query + * \param info Value passed as the last argument to DNS_Query + * \param name NUL-terminated name associated with the returned record + * \param type Record type (may not be equal to requested) + * \param class Record class (may not be equal to requested) + * \param rdlength Length of data pointed to by 'rdata' + * \param rdata Record data + */ +typedef void handle_record_t(void *info, const char *name, enum eTypes type, enum eClass class, unsigned int ttl, size_t rdlength, const void *rdata); + +int DNS_Query(int ServerAType, const void *ServerAddr, const char *name, enum eTypes type, enum eClass class, handle_record_t* handle_record, void *info); + +#endif + diff --git a/Usermode/Libraries/libnet.so_src/include_exp/net.h b/Usermode/Libraries/libnet.so_src/include_exp/net.h index 5fe4ba10..f02c0a43 100644 --- a/Usermode/Libraries/libnet.so_src/include_exp/net.h +++ b/Usermode/Libraries/libnet.so_src/include_exp/net.h @@ -53,8 +53,31 @@ extern char *Net_GetInterface(int AddrType, void *Addr); * Opens a file using /Devices/ip/routes/@:/ * */ -extern int Net_OpenSocket(int AddrType, void *Addr, const char *SocketName); +extern int Net_OpenSocket(int AddrType, const void *Addr, const char *SocketName); -extern int Net_OpenSocket_TCPC(int AddrType, void *Addr, int Port); +extern int Net_OpenSocket_TCPC(int AddrType, const void *Addr, int Port); + +extern int Net_OpenSocket_UDP(int AddrType, const void *Addr, int RAddr, int LAddr); + + +/** + * \name Hostnames + * \brief Handling of hostname resolution + * \{ + */ + +/** + * \brief Returns an address for the specified hostname + * \note Picks randomly if multiple addresses are present + */ +extern int Net_Lookup_AnyAddr(const char *Name, int AddrType, void *Addr); + +/** + */ +extern int Net_Lookup_Name(int AddrType, const void *Addr, char *Dest[256]); + +/** + * \} + */ #endif diff --git a/Usermode/Libraries/libnet.so_src/socket.c b/Usermode/Libraries/libnet.so_src/socket.c index 85f7e54f..68335acc 100644 --- a/Usermode/Libraries/libnet.so_src/socket.c +++ b/Usermode/Libraries/libnet.so_src/socket.c @@ -10,15 +10,23 @@ #include #include -int Net_OpenSocket(int AddrType, void *Addr, const char *Filename) +enum { + UDP_IOCTL_GETSETLPORT = 4, + UDP_IOCTL_GETSETRPORT, + UDP_IOCTL_GETSETRMASK, + UDP_IOCTL_SETRADDR, +}; + +int Net_OpenSocket(int AddrType, const void *Addr, const char *Filename) { int addrLen = Net_GetAddressSize(AddrType); - int i; - uint8_t *addrBuffer = Addr; char hexAddr[addrLen*2+1]; - for( i = 0; i < addrLen; i ++ ) - sprintf(hexAddr+i*2, "%02x", addrBuffer[i]); + { + const uint8_t *addrBuffer = Addr; + for( unsigned int i = 0; i < addrLen; i ++ ) + sprintf(hexAddr+i*2, "%02x", addrBuffer[i]); + } if(Filename) { @@ -37,14 +45,27 @@ int Net_OpenSocket(int AddrType, void *Addr, const char *Filename) } } -int Net_OpenSocket_TCPC(int AddrType, void *Addr, int Port) +int Net_OpenSocket_TCPC(int AddrType, const void *Addr, int Port) { int fd = Net_OpenSocket(AddrType, Addr, "tcpc"); if( fd == -1 ) return -1; _SysIOCtl(fd, 5, &Port); // Remote Port - _SysIOCtl(fd, 6, Addr); // Remote address + _SysIOCtl(fd, 6, (void*)Addr); // Remote address (kernel shouldn't modify) _SysIOCtl(fd, 7, NULL); // connect return fd; } +int Net_OpenSocket_UDP(int AddrType, const void *Addr, int RPort, int LPort) +{ + int fd = Net_OpenSocket(AddrType, Addr, "udp"); + if( fd == -1 ) return -1; + + _SysIOCtl(fd, UDP_IOCTL_GETSETLPORT, &LPort); // Remote Port + int maskbits = Net_GetAddressSize(AddrType) * 8; + _SysIOCtl(fd, UDP_IOCTL_GETSETRPORT, &RPort); + _SysIOCtl(fd, UDP_IOCTL_GETSETRMASK, &maskbits); + _SysIOCtl(fd, UDP_IOCTL_SETRADDR, (void*)Addr); // Remote address (kernel shouldn't modify) + return fd; +} + -- 2.20.1