monster update: add tai, taia, iopause, case, and ipv6 enhanced dns

master
leitner 22 years ago
parent dd2d9119fc
commit f921b8cbb0

@ -3,13 +3,14 @@ LIBDIR=${prefix}/lib
INCLUDEDIR=${prefix}/include
MAN3DIR=${prefix}/man/man3
all: t byte.a fmt.a scan.a str.a uint.a open.a stralloc.a unix.a socket.a buffer.a mmap.a libowfat.a
all: t byte.a fmt.a scan.a str.a uint.a open.a stralloc.a unix.a socket.a \
buffer.a mmap.a taia.a tai.a dns.a case.a libowfat.a
VPATH=str:byte:fmt:scan:uint:open:stralloc:unix:socket:buffer:mmap:textcode
VPATH=str:byte:fmt:scan:uint:open:stralloc:unix:socket:buffer:mmap:textcode:taia:tai:dns:case
# comment out the following line if you don't want to build with the
# diet libc (http://www.fefe.de/dietlibc/).
DIET=diet -Os
DIET=/opt/diet/bin/diet -Os
CC=gcc
CFLAGS=-I. -pipe -Wall -O2 -fomit-frame-pointer
#CFLAGS=-pipe -Os -march=pentiumpro -mcpu=pentiumpro -fomit-frame-pointer -fschedule-insns2 -Wall
@ -26,6 +27,10 @@ SOCKET_OBJS=$(patsubst socket/%.c,%.o,$(wildcard socket/*.c))
BUFFER_OBJS=$(patsubst buffer/%.c,%.o,$(wildcard buffer/*.c))
MMAP_OBJS=$(patsubst mmap/%.c,%.o,$(wildcard mmap/*.c))
TEXTCODE_OBJS=$(patsubst textcode/%.c,%.o,$(wildcard textcode/*.c))
TAI_OBJS=$(patsubst tai/%.c,%.o,$(wildcard tai/*.c))
TAIA_OBJS=$(patsubst taia/%.c,%.o,$(wildcard taia/*.c))
DNS_OBJS=$(patsubst dns/%.c,%.o,$(wildcard dns/*.c))
CASE_OBJS=$(patsubst case/%.c,%.o,$(wildcard case/*.c))
$(BYTE_OBJS): byte.h
$(FMT_OBJS): fmt.h
@ -37,6 +42,14 @@ $(SOCKET_OBJS): socket.h
$(BUFFER_OBJS): buffer.h
$(MMAP_OBJS): mmap.h
$(TEXTCODE_OBJS): textcode.h
$(TAI_OBJS): tai.h uint64.h
$(TAIA_OBJS): taia.h tai.h uint64.h
$(DNS_OBJS): dns.h stralloc.h taia.h tai.h uint64.h iopause.h
$(CASE_OBJS): case.h
iopause.o: select.h
openreadclose.o readclose.o: readclose.h
dns_rcip.o dns_rcrw.o openreadclose.o: openreadclose.h
byte.a: $(BYTE_OBJS)
fmt.a: $(FMT_OBJS)
@ -50,10 +63,15 @@ socket.a: $(SOCKET_OBJS)
buffer.a: $(BUFFER_OBJS)
mmap.a: $(MMAP_OBJS)
textcode.a: $(TEXTCODE_OBJS)
taia.a: $(TAIA_OBJS)
tai.a: $(TAI_OBJS)
dns.a: $(DNS_OBJS)
case.a: $(CASE_OBJS)
libowfat.a: $(BYTE_OBJS) $(FMT_OBJS) $(SCAN_OBJS) $(STR_OBJS) \
$(UINT_OBJS) $(OPEN_OBJS) $(STRA_OBJS) $(UNIX_OBJS) $(SOCKET_OBJS) \
$(BUFFER_OBJS) $(MMAP_OBJS) $(TEXTCODE_OBJS)
libowfat.a: $(DNS_OBJS) $(BYTE_OBJS) $(FMT_OBJS) $(SCAN_OBJS) \
$(STR_OBJS) $(UINT_OBJS) $(OPEN_OBJS) $(STRA_OBJS) $(UNIX_OBJS) \
$(SOCKET_OBJS) $(BUFFER_OBJS) $(MMAP_OBJS) $(TEXTCODE_OBJS) \
$(TAIA_OBJS) $(TAI_OBJS) $(CASE_OBJS)
%.o: %.c
$(DIET) $(CC) -c $< -o $@ $(CFLAGS)
@ -67,9 +85,12 @@ t: t.o libowfat.a
.PHONY: clean tar install rename
clean:
rm -f *.o *.a *.da *.bbg *.bb core t haveip6.h haven2i.h havesl.h haveinline.h
rm -f *.o *.a *.da *.bbg *.bb core t haveip6.h haven2i.h havesl.h haveinline.h \
iopause.h select.h
INCLUDES=buffer.h byte.h fmt.h ip4.h ip6.h mmap.h scan.h socket.h str.h stralloc.h uint16.h uint32.h uint64.h open.h textcode.h
INCLUDES=buffer.h byte.h fmt.h ip4.h ip6.h mmap.h scan.h socket.h str.h stralloc.h \
uint16.h uint32.h uint64.h open.h textcode.h tai.h taia.h dns.h iopause.h case.h \
openreadclose.h readclose.h
install: libowfat.a
install -d $(INCLUDEDIR) $(MAN3DIR) $(LIBDIR)
@ -91,25 +112,36 @@ tar: clean rename
rename:
if test $(CURNAME) != $(VERSION); then cd .. && mv $(CURNAME) $(VERSION); fi
haveip6.h:
haveip6.h: tryip6.c
-rm -f $@
if $(DIET) $(CC) -c tryip6.c >/dev/null 2>&1; then echo "#define LIBC_HAS_IP6"; fi > $@
-rm -f tryip6.o
haven2i.h:
haven2i.h: tryn2i.c
-rm -f $@
if $(DIET) $(CC) -o t tryn2i.c >/dev/null 2>&1; then echo "#define HAVE_N2I"; fi > $@
-rm -f t
havesl.h:
havesl.h: trysl.c
-rm -f $@
if ! $(DIET) $(CC) -o t trysl.c >/dev/null 2>&1; then echo "typedef int socklen_t;"; fi > $@
-rm -f t
haveinline.h:
haveinline.h: tryinline.c
-rm -f $@
if ! $(DIET) $(CC) -c tryinline.c >/dev/null 2>&1; then echo "#define inline"; fi > $@
-rm -f tryip6.o
-rm -f tryinline.o
iopause.h: iopause.h1 iopause.h2 trypoll.c
-rm -f $@
if $(DIET) $(CC) -o t trypoll.c >/dev/null 2>&1; then cp iopause.h2 iopause.h; else cp iopause.h1 iopause.h; fi
-rm -f t
select.h: select.h1 select.h2 trysysel.c
-rm -f $@
if $(DIET) $(CC) -c trysysel.c >/dev/null 2>&1; then cp select.h2 select.h; else cp select.h1 select.h; fi
-rm -f trysysel.o
socket_accept6.o socket_connect6.o socket_local6.o socket_mchopcount6.o \
socket_mcjoin6.o socket_mcleave6.o socket_mcloop6.o socket_recv6.o \

@ -0,0 +1,24 @@
libowfat is a library of general purpose APIs extracted from Dan
Bernstein's software, reimplemented and covered by the GNU General
Public License Version 2 (no later versions).
The API has been slightly extended (for example, I provide a uint32_read
function, and I extended the socket API to support IPv6) where I found
it necessary or beneficial in a specific project.
Many of the functions I implement here have since been placed in the
public domain, so there are other sources to get this code (except for
my extensions obviously). The implementations here may not be as
portable as the original versions; I tend to focus on the Single Unix
Specification and not on some obsolete legacy systems found in the
basements of some vintage hardware clubs.
I also provide man pages for many functions, mostly extracted from Dan's
web documentation or documentation found in earlier versions of his
software. For some reason, he abandoned man pages in favor of HTML
recently.
On July 4 2002, Dan also placed his DNS routines and supporting
low level functions in the public domain, so I copy them here instead of
reimplementing them. http://online.securityfocus.com/archive/1/280642
has an online version of the bugtraq posting.

@ -1,7 +1,9 @@
#ifndef BYTE_H
#define BYTE_H
#ifdef __dietlibc__
#include <sys/cdefs.h>
#endif
#ifndef __pure__
#define __pure__

@ -0,0 +1,21 @@
#ifndef CASE_H
#define CASE_H
/* turn upper case letters to lower case letters, ASCIIZ */
extern void case_lowers(char *s);
/* turn upper case letters to lower case letters, binary */
extern void case_lowerb(char *buf,unsigned int len);
/* like str_diff, ignoring case */
extern int case_diffs(const char *,const char *);
/* like byte_diff, ignoring case */
extern int case_diffb(const char *,unsigned int,const char *);
/* like str_start, ignoring case */
extern int case_starts(const char *,const char *);
/* alias for case_diffb? */
extern int case_startb(const char *,unsigned int,const char *);
#define case_equals(s,t) (!case_diffs((s),(t)))
#endif

@ -0,0 +1,18 @@
#include "case.h"
int case_diffb(register const char *s,register unsigned int len,register const char *t)
{
register unsigned char x;
register unsigned char y;
while (len > 0) {
--len;
x = *s++ - 'A';
if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
y = *t++ - 'A';
if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
if (x != y)
return ((int)(unsigned int) x) - ((int)(unsigned int) y);
}
return 0;
}

@ -0,0 +1,17 @@
#include "case.h"
int case_diffs(register const char *s,register const char *t)
{
register unsigned char x;
register unsigned char y;
for (;;) {
x = *s++ - 'A';
if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
y = *t++ - 'A';
if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
if (x != y) break;
if (!x) break;
}
return ((int)(unsigned int) x) - ((int)(unsigned int) y);
}

@ -0,0 +1,11 @@
#include "case.h"
void case_lowerb(char *s,unsigned int len) {
unsigned char x;
while (len > 0) {
--len;
x = *s - 'A';
if (x <= 'Z' - 'A') *s = x + 'a';
++s;
}
}

@ -0,0 +1,10 @@
#include "case.h"
void case_lowers(char *s) {
unsigned char x;
for (;;) {
if (!(x=*s)) break;
if ((x -= 'A') <= 'Z' - 'A') *s = x + 'a';
++s;
}
}

93
dns.h

@ -0,0 +1,93 @@
#ifndef DNS_H
#define DNS_H
#include "stralloc.h"
#include "iopause.h"
#include "taia.h"
#define DNS_C_IN "\0\1"
#define DNS_C_ANY "\0\377"
#define DNS_T_A "\0\1"
#define DNS_T_NS "\0\2"
#define DNS_T_CNAME "\0\5"
#define DNS_T_SOA "\0\6"
#define DNS_T_PTR "\0\14"
#define DNS_T_HINFO "\0\15"
#define DNS_T_MX "\0\17"
#define DNS_T_TXT "\0\20"
#define DNS_T_RP "\0\21"
#define DNS_T_SIG "\0\30"
#define DNS_T_KEY "\0\31"
#define DNS_T_AAAA "\0\34"
#define DNS_T_AXFR "\0\374"
#define DNS_T_ANY "\0\377"
struct dns_transmit {
char *query; /* 0, or dynamically allocated */
unsigned int querylen;
char *packet; /* 0, or dynamically allocated */
unsigned int packetlen;
int s1; /* 0, or 1 + an open file descriptor */
int tcpstate;
unsigned int udploop;
unsigned int curserver;
struct taia deadline;
unsigned int pos;
const char *servers;
char localip[16];
unsigned int scope_id;
char qtype[2];
} ;
extern void dns_random_init(const char *);
extern unsigned int dns_random(unsigned int);
extern void dns_sortip(char *,unsigned int);
extern void dns_sortip6(char *,unsigned int);
extern void dns_domain_free(char **);
extern int dns_domain_copy(char **,const char *);
extern unsigned int dns_domain_length(const char *);
extern int dns_domain_equal(const char *,const char *);
extern int dns_domain_suffix(const char *,const char *);
extern unsigned int dns_domain_suffixpos(const char *,const char *);
extern int dns_domain_fromdot(char **,const char *,unsigned int);
extern int dns_domain_todot_cat(stralloc *,const char *);
extern unsigned int dns_packet_copy(const char *,unsigned int,unsigned int,char *,unsigned int);
extern unsigned int dns_packet_getname(const char *,unsigned int,unsigned int,char **);
extern unsigned int dns_packet_skipname(const char *,unsigned int,unsigned int);
extern int dns_transmit_start(struct dns_transmit *,const char *,int,const char *,const char *,const char *);
extern void dns_transmit_free(struct dns_transmit *);
extern void dns_transmit_io(struct dns_transmit *,iopause_fd *,struct taia *);
extern int dns_transmit_get(struct dns_transmit *,const iopause_fd *,const struct taia *);
extern int dns_resolvconfip(char *);
extern int dns_resolve(const char *,const char *);
extern struct dns_transmit dns_resolve_tx;
extern int dns_ip4_packet(stralloc *,const char *,unsigned int);
extern int dns_ip4(stralloc *,const stralloc *);
extern int dns_ip6_packet(stralloc *,char *,unsigned int);
extern int dns_ip6(stralloc *,stralloc *);
extern int dns_name_packet(stralloc *,const char *,unsigned int);
extern void dns_name4_domain(char *,const char *);
#define DNS_NAME4_DOMAIN 31
extern int dns_name4(stralloc *,const char *);
extern int dns_txt_packet(stralloc *,const char *,unsigned int);
extern int dns_txt(stralloc *,const stralloc *);
extern int dns_mx_packet(stralloc *,const char *,unsigned int);
extern int dns_mx(stralloc *,const stralloc *);
extern int dns_resolvconfrewrite(stralloc *);
extern int dns_ip4_qualify_rules(stralloc *,stralloc *,const stralloc *,const stralloc *);
extern int dns_ip4_qualify(stralloc *,stralloc *,const stralloc *);
extern int dns_ip6_qualify_rules(stralloc *,stralloc *,const stralloc *,const stralloc *);
extern int dns_ip6_qualify(stralloc *,stralloc *,const stralloc *);
extern int dns_name6_domain(char *,char *);
#define DNS_NAME6_DOMAIN (4*16+10)
#endif

@ -0,0 +1,69 @@
#include <stdlib.h>
#include <errno.h>
#include "byte.h"
#include "dns.h"
int dns_domain_fromdot(char **out,const char *buf,unsigned int n)
{
char label[63];
unsigned int labellen = 0; /* <= sizeof label */
char name[255];
unsigned int namelen = 0; /* <= sizeof name */
char ch;
char *x;
errno = EPROTO;
for (;;) {
if (!n) break;
ch = *buf++; --n;
if (ch == '.') {
if (labellen) {
if (namelen + labellen + 1 > sizeof name) return 0;
name[namelen++] = labellen;
byte_copy(name + namelen,labellen,label);
namelen += labellen;
labellen = 0;
}
continue;
}
if (ch == '\\') {
if (!n) break;
ch = *buf++; --n;
if ((ch >= '0') && (ch <= '7')) {
ch -= '0';
if (n && (*buf >= '0') && (*buf <= '7')) {
ch <<= 3;
ch += *buf - '0';
++buf; --n;
if (n && (*buf >= '0') && (*buf <= '7')) {
ch <<= 3;
ch += *buf - '0';
++buf; --n;
}
}
}
}
if (labellen >= sizeof label) return 0;
label[labellen++] = ch;
}
if (labellen) {
if (namelen + labellen + 1 > sizeof name) return 0;
name[namelen++] = labellen;
byte_copy(name + namelen,labellen,label);
namelen += labellen;
labellen = 0;
}
if (namelen + 1 > sizeof name) return 0;
name[namelen++] = 0;
x = malloc(namelen);
if (!x) return 0;
byte_copy(x,namelen,name);
if (*out) free(*out);
*out = x;
return 1;
}

@ -0,0 +1,73 @@
#include <stdlib.h>
#include "case.h"
#include "byte.h"
#include "dns.h"
unsigned int dns_domain_length(const char *dn)
{
const char *x;
unsigned char c;
x = dn;
while ((c = *x++))
x += (unsigned int) c;
return x - dn;
}
void dns_domain_free(char **out)
{
if (*out) {
free(*out);
*out = 0;
}
}
int dns_domain_copy(char **out,const char *in)
{
unsigned int len;
char *x;
len = dns_domain_length(in);
x = malloc(len);
if (!x) return 0;
byte_copy(x,len,in);
if (*out) free(*out);
*out = x;
return 1;
}
int dns_domain_equal(const char *dn1,const char *dn2)
{
unsigned int len;
len = dns_domain_length(dn1);
if (len != dns_domain_length(dn2)) return 0;
if (case_diffb(dn1,len,dn2)) return 0; /* safe since 63 < 'A' */
return 1;
}
int dns_domain_suffix(const char *big,const char *little)
{
unsigned char c;
for (;;) {
if (dns_domain_equal(big,little)) return 1;
c = *big++;
if (!c) return 0;
big += c;
}
}
unsigned int dns_domain_suffixpos(const char *big,const char *little)
{
const char *orig = big;
unsigned char c;
for (;;) {
if (dns_domain_equal(big,little)) return big - orig;
c = *big++;
if (!c) return 0;
big += c;
}
}

@ -0,0 +1,35 @@
#include "stralloc.h"
#include "dns.h"
int dns_domain_todot_cat(stralloc *out,const char *d)
{
char ch;
char ch2;
unsigned char ch3;
char buf[4];
if (!*d)
return stralloc_append(out,".");
for (;;) {
ch = *d++;
while (ch--) {
ch2 = *d++;
if ((ch2 >= 'A') && (ch2 <= 'Z'))
ch2 += 32;
if (((ch2 >= 'a') && (ch2 <= 'z')) || ((ch2 >= '0') && (ch2 <= '9')) || (ch2 == '-') || (ch2 == '_')) {
if (!stralloc_append(out,&ch2)) return 0;
}
else {
ch3 = ch2;
buf[3] = '0' + (ch3 & 7); ch3 >>= 3;
buf[2] = '0' + (ch3 & 7); ch3 >>= 3;
buf[1] = '0' + (ch3 & 7);
buf[0] = '\\';
if (!stralloc_catb(out,buf,4)) return 0;
}
}
if (!*d) return 1;
if (!stralloc_append(out,".")) return 0;
}
}

@ -0,0 +1,75 @@
#include "stralloc.h"
#include "uint16.h"
#include "byte.h"
#include "dns.h"
int dns_ip4_packet(stralloc *out,const char *buf,unsigned int len)
{
unsigned int pos;
char header[12];
uint16 numanswers;
uint16 datalen;
if (!stralloc_copys(out,"")) return -1;
pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return -1;
uint16_unpack_big(header + 6,&numanswers);
pos = dns_packet_skipname(buf,len,pos); if (!pos) return -1;
pos += 4;
while (numanswers--) {
pos = dns_packet_skipname(buf,len,pos); if (!pos) return -1;
pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) return -1;
uint16_unpack_big(header + 8,&datalen);
if (byte_equal(header,2,DNS_T_A))
if (byte_equal(header + 2,2,DNS_C_IN))
if (datalen == 4) {
if (!dns_packet_copy(buf,len,pos,header,4)) return -1;
if (!stralloc_catb(out,header,4)) return -1;
}
pos += datalen;
}
dns_sortip(out->s,out->len);
return 0;
}
static char *q = 0;
int dns_ip4(stralloc *out,const stralloc *fqdn)
{
unsigned int i;
char code;
char ch;
if (!stralloc_copys(out,"")) return -1;
code = 0;
for (i = 0;i <= fqdn->len;++i) {
if (i < fqdn->len)
ch = fqdn->s[i];
else
ch = '.';
if ((ch == '[') || (ch == ']')) continue;
if (ch == '.') {
if (!stralloc_append(out,&code)) return -1;
code = 0;
continue;
}
if ((ch >= '0') && (ch <= '9')) {
code *= 10;
code += ch - '0';
continue;
}
if (!dns_domain_fromdot(&q,fqdn->s,fqdn->len)) return -1;
if (dns_resolve(q,DNS_T_A) == -1) return -1;
if (dns_ip4_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen) == -1) return -1;
dns_transmit_free(&dns_resolve_tx);
dns_domain_free(&q);
return 0;
}
out->len &= ~3;
return 0;
}

@ -0,0 +1,103 @@
#include "stralloc.h"
#include "uint16.h"
#include "byte.h"
#include "dns.h"
#include "ip4.h"
#include "ip6.h"
static int dns_ip6_packet_add(stralloc *out,char *buf,unsigned int len)
{
unsigned int pos;
char header[16];
uint16 numanswers;
uint16 datalen;
pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return -1;
uint16_unpack_big(header + 6,&numanswers);
pos = dns_packet_skipname(buf,len,pos); if (!pos) return -1;
pos += 4;
while (numanswers--) {
pos = dns_packet_skipname(buf,len,pos); if (!pos) return -1;
pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) return -1;
uint16_unpack_big(header + 8,&datalen);
if (byte_equal(header,2,DNS_T_AAAA)) {
if (byte_equal(header + 2,2,DNS_C_IN))
if (datalen == 16) {
if (!dns_packet_copy(buf,len,pos,header,16)) return -1;
if (!stralloc_catb(out,header,16)) return -1;
}
} else if (byte_equal(header,2,DNS_T_A))
if (byte_equal(header + 2,2,DNS_C_IN))
if (datalen == 4) {
byte_copy(header,12,V4mappedprefix);
if (!dns_packet_copy(buf,len,pos,header+12,4)) return -1;
if (!stralloc_catb(out,header,16)) return -1;
}
pos += datalen;
}
dns_sortip6(out->s,out->len);
return 0;
}
int dns_ip6_packet(stralloc *out,char *buf,unsigned int len) {
if (!stralloc_copys(out,"")) return -1;
return dns_ip6_packet_add(out,buf,len);
}
static char *q = 0;
int dns_ip6(stralloc *out,stralloc *fqdn)
{
unsigned int i;
char code;
char ch;
char ip[16];
if (!stralloc_copys(out,"")) return -1;
if (!stralloc_readyplus(fqdn,1)) return -1;
fqdn->s[fqdn->len]=0;
if ((i=scan_ip6(fqdn->s,ip))) {
if (fqdn->s[i]) return -1;
stralloc_copyb(out,ip,16);
return 0;
}
code = 0;
for (i = 0;i <= fqdn->len;++i) {
if (i < fqdn->len)
ch = fqdn->s[i];
else
ch = '.';
if ((ch == '[') || (ch == ']')) continue;
if (ch == '.') {
if (!stralloc_append(out,&code)) return -1;
code = 0;
continue;
}
if ((ch >= '0') && (ch <= '9')) {
code *= 10;
code += ch - '0';
continue;
}
if (!dns_domain_fromdot(&q,fqdn->s,fqdn->len)) return -1;
if (!stralloc_copys(out,"")) return -1;
if (dns_resolve(q,DNS_T_AAAA) != -1)
if (dns_ip6_packet_add(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen) != -1) {
dns_transmit_free(&dns_resolve_tx);
dns_domain_free(&q);
}
if (!dns_domain_fromdot(&q,fqdn->s,fqdn->len)) return -1;
if (dns_resolve(q,DNS_T_A) != -1)
if (dns_ip6_packet_add(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen) != -1) {
dns_transmit_free(&dns_resolve_tx);
dns_domain_free(&q);
}
return out->a>0?0:-1;
}
out->len &= ~3;
return 0;
}

@ -0,0 +1,71 @@
#include "stralloc.h"
#include "case.h"
#include "byte.h"
#include "str.h"
#include "dns.h"
static int doit(stralloc *work,const char *rule)
{
char ch;
unsigned int colon;
unsigned int prefixlen;
ch = *rule++;
if ((ch != '?') && (ch != '=') && (ch != '*') && (ch != '-')) return 1;
colon = str_chr(rule,':');
if (!rule[colon]) return 1;
if (work->len < colon) return 1;
prefixlen = work->len - colon;
if ((ch == '=') && prefixlen) return 1;
if (case_diffb(rule,colon,work->s + prefixlen)) return 1;
if (ch == '?') {
if (byte_chr(work->s,prefixlen,'.') < prefixlen) return 1;
if (byte_chr(work->s,prefixlen,'[') < prefixlen) return 1;
if (byte_chr(work->s,prefixlen,']') < prefixlen) return 1;
}
work->len = prefixlen;
if (ch == '-') work->len = 0;
return stralloc_cats(work,rule + colon + 1);
}
int dns_ip4_qualify_rules(stralloc *out,stralloc *fqdn,const stralloc *in,const stralloc *rules)
{
unsigned int i;
unsigned int j;
unsigned int plus;
unsigned int fqdnlen;
if (!stralloc_copy(fqdn,in)) return -1;
for (j = i = 0;j < rules->len;++j)
if (!rules->s[j]) {
if (!doit(fqdn,rules->s + i)) return -1;
i = j + 1;
}
fqdnlen = fqdn->len;
plus = byte_chr(fqdn->s,fqdnlen,'+');
if (plus >= fqdnlen)
return dns_ip4(out,fqdn);
i = plus + 1;
for (;;) {
j = byte_chr(fqdn->s + i,fqdnlen - i,'+');
byte_copy(fqdn->s + plus,j,fqdn->s + i);
fqdn->len = plus + j;
if (dns_ip4(out,fqdn) == -1) return -1;
if (out->len) return 0;
i += j;
if (i >= fqdnlen) return 0;
++i;
}
}
int dns_ip4_qualify(stralloc *out,stralloc *fqdn,const stralloc *in)
{
static stralloc rules;
if (dns_resolvconfrewrite(&rules) == -1) return -1;
return dns_ip4_qualify_rules(out,fqdn,in,&rules);
}

@ -0,0 +1,72 @@
#include "stralloc.h"
#include "case.h"
#include "byte.h"
#include "str.h"
#include "dns.h"
static int doit(stralloc *work,const char *rule)
{
char ch;
unsigned int colon;
unsigned int prefixlen;
ch = *rule++;
if ((ch != '?') && (ch != '=') && (ch != '*') && (ch != '-')) return 1;
colon = str_chr(rule,':');
if (!rule[colon]) return 1;
if (work->len < colon) return 1;
prefixlen = work->len - colon;
if ((ch == '=') && prefixlen) return 1;
if (case_diffb(rule,colon,work->s + prefixlen)) return 1;
if (ch == '?') {
if (byte_chr(work->s,prefixlen,'.') < prefixlen) return 1;
if (byte_chr(work->s,prefixlen,':') < prefixlen) return 1;
if (byte_chr(work->s,prefixlen,'[') < prefixlen) return 1;
if (byte_chr(work->s,prefixlen,']') < prefixlen) return 1;
}
work->len = prefixlen;
if (ch == '-') work->len = 0;
return stralloc_cats(work,rule + colon + 1);
}
int dns_ip6_qualify_rules(stralloc *out,stralloc *fqdn,const stralloc *in,const stralloc *rules)
{
unsigned int i;
unsigned int j;
unsigned int plus;
unsigned int fqdnlen;
if (!stralloc_copy(fqdn,in)) return -1;
for (j = i = 0;j < rules->len;++j)
if (!rules->s[j]) {
if (!doit(fqdn,rules->s + i)) return -1;
i = j + 1;
}
fqdnlen = fqdn->len;
plus = byte_chr(fqdn->s,fqdnlen,'+');
if (plus >= fqdnlen)
return dns_ip6(out,fqdn);
i = plus + 1;
for (;;) {
j = byte_chr(fqdn->s + i,fqdnlen - i,'+');
byte_copy(fqdn->s + plus,j,fqdn->s + i);
fqdn->len = plus + j;
if (dns_ip6(out,fqdn) == -1) return -1;
if (out->len) return 0;
i += j;
if (i >= fqdnlen) return 0;
++i;
}
}
int dns_ip6_qualify(stralloc *out,stralloc *fqdn,const stralloc *in)
{
static stralloc rules;
if (dns_resolvconfrewrite(&rules) == -1) return -1;
return dns_ip6_qualify_rules(out,fqdn,in,&rules);
}

@ -0,0 +1,49 @@
#include "stralloc.h"
#include "byte.h"
#include "uint16.h"
#include "dns.h"
static char *q = 0;
int dns_mx_packet(stralloc *out,const char *buf,unsigned int len)
{
unsigned int pos;
char header[12];
char pref[2];
uint16 numanswers;
uint16 datalen;
if (!stralloc_copys(out,"")) return -1;
pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return -1;
uint16_unpack_big(header + 6,&numanswers);
pos = dns_packet_skipname(buf,len,pos); if (!pos) return -1;
pos += 4;
while (numanswers--) {
pos = dns_packet_skipname(buf,len,pos); if (!pos) return -1;
pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) return -1;
uint16_unpack_big(header + 8,&datalen);
if (byte_equal(header,2,DNS_T_MX))
if (byte_equal(header + 2,2,DNS_C_IN)) {
if (!dns_packet_copy(buf,len,pos,pref,2)) return -1;
if (!dns_packet_getname(buf,len,pos + 2,&q)) return -1;
if (!stralloc_catb(out,pref,2)) return -1;
if (!dns_domain_todot_cat(out,q)) return -1;
if (!stralloc_0(out)) return -1;
}
pos += datalen;
}
return 0;
}
int dns_mx(stralloc *out,const stralloc *fqdn)
{
if (!dns_domain_fromdot(&q,fqdn->s,fqdn->len)) return -1;
if (dns_resolve(q,DNS_T_MX) == -1) return -1;
if (dns_mx_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen) == -1) return -1;
dns_transmit_free(&dns_resolve_tx);
dns_domain_free(&q);
return 0;
}

@ -0,0 +1,63 @@
#include "stralloc.h"
#include "uint16.h"
#include "byte.h"
#include "dns.h"
#include "ip6.h"
static char *q = 0;
int dns_name_packet(stralloc *out,const char *buf,unsigned int len)
{
unsigned int pos;
char header[12];
uint16 numanswers;
uint16 datalen;
if (!stralloc_copys(out,"")) return -1;
pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return -1;
uint16_unpack_big(header + 6,&numanswers);
pos = dns_packet_skipname(buf,len,pos); if (!pos) return -1;
pos += 4;
while (numanswers--) {
pos = dns_packet_skipname(buf,len,pos); if (!pos) return -1;
pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) return -1;
uint16_unpack_big(header + 8,&datalen);
if (byte_equal(header,2,DNS_T_PTR))
if (byte_equal(header + 2,2,DNS_C_IN)) {
if (!dns_packet_getname(buf,len,pos,&q)) return -1;
if (!dns_domain_todot_cat(out,q)) return -1;
return 0;
}
pos += datalen;
}
return 0;
}
int dns_name4(stralloc *out,const char ip[4])
{
char name[DNS_NAME4_DOMAIN];
dns_name4_domain(name,ip);
if (dns_resolve(name,DNS_T_PTR) == -1) return -1;
if (dns_name_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen) == -1) return -1;
dns_transmit_free(&dns_resolve_tx);
dns_domain_free(&q);
return 0;
}
int dns_name6(stralloc *out,char ip[16])
{
char name[DNS_NAME6_DOMAIN];
if (ip6_isv4mapped(ip))
return dns_name4(out,ip+12);
dns_name6_domain(name,ip);
if (dns_resolve(name,DNS_T_PTR) == -1) return -1;
if (dns_name_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen) == -1) return -1;
dns_transmit_free(&dns_resolve_tx);
dns_domain_free(&q);
return 0;
}

@ -0,0 +1,24 @@
#include "byte.h"
#include "fmt.h"
#include "dns.h"
void dns_name4_domain(char name[DNS_NAME4_DOMAIN],const char ip[4])
{
unsigned int namelen;
unsigned int i;
namelen = 0;
i = fmt_ulong(name + namelen + 1,(unsigned long) (unsigned char) ip[3]);
name[namelen++] = i;
namelen += i;
i = fmt_ulong(name + namelen + 1,(unsigned long) (unsigned char) ip[2]);
name[namelen++] = i;
namelen += i;
i = fmt_ulong(name + namelen + 1,(unsigned long) (unsigned char) ip[1]);
name[namelen++] = i;
namelen += i;
i = fmt_ulong(name + namelen + 1,(unsigned long) (unsigned char) ip[0]);
name[namelen++] = i;
namelen += i;
byte_copy(name + namelen,14,"\7in-addr\4arpa\0");
}

@ -0,0 +1,28 @@
#include "byte.h"
#include "fmt.h"
#include "dns.h"
/* RFC1886:
* 4321:0:1:2:3:4:567:89ab
* ->
* b.a.9.8.7.6.5.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.0.0.0.0.1.2.3.4.IP6.INT.
*/
static inline char tohex(char c) {
return c>=10?c-10+'a':c+'0';
}
int dns_name6_domain(char name[DNS_NAME6_DOMAIN],char ip[16])
{
unsigned int j;
for (j=0; j<16; j++) {
name[j*4]=1;
name[j*4+1]=tohex(ip[15-j] & 15);
name[j*4+2]=1;
name[j*4+3]=tohex((unsigned char)ip[15-j] >> 4);
}
byte_copy(name + 4*16,9,"\3ip6\3int\0");
return 4*16+9;
}

@ -0,0 +1,77 @@
/*
DNS should have used LZ77 instead of its own sophomoric compression algorithm.
*/
#include <errno.h>
#include "dns.h"
unsigned int dns_packet_copy(const char *buf,unsigned int len,unsigned int pos,char *out,unsigned int outlen)
{
while (outlen) {
if (pos >= len) { errno = EPROTO; return 0; }
*out = buf[pos++];
++out; --outlen;
}
return pos;
}
unsigned int dns_packet_skipname(const char *buf,unsigned int len,unsigned int pos)
{
unsigned char ch;
for (;;) {
if (pos >= len) break;
ch = buf[pos++];
if (ch >= 192) return pos + 1;
if (ch >= 64) break;
if (!ch) return pos;
pos += ch;
}
errno = EPROTO;
return 0;
}
unsigned int dns_packet_getname(const char *buf,unsigned int len,unsigned int pos,char **d)
{
unsigned int loop = 0;
unsigned int state = 0;
unsigned int firstcompress = 0;
unsigned int where;
unsigned char ch;
char name[255];
unsigned int namelen = 0;
for (;;) {
if (pos >= len) goto PROTO; ch = buf[pos++];
if (++loop >= 1000) goto PROTO;
if (state) {
if (namelen + 1 > sizeof name) goto PROTO; name[namelen++] = ch;
--state;
}
else {
while (ch >= 192) {
where = ch; where -= 192; where <<= 8;
if (pos >= len) goto PROTO; ch = buf[pos++];
if (!firstcompress) firstcompress = pos;
pos = where + ch;
if (pos >= len) goto PROTO; ch = buf[pos++];
if (++loop >= 1000) goto PROTO;
}
if (ch >= 64) goto PROTO;
if (namelen + 1 > sizeof name) goto PROTO; name[namelen++] = ch;
if (!ch) break;
state = ch;
}
}
if (!dns_domain_copy(d,name)) return 0;
if (firstcompress) return firstcompress;
return pos;
PROTO:
errno = EPROTO;
return 0;
}

@ -0,0 +1,63 @@
#include <unistd.h>
#include "dns.h"
#include "taia.h"
#include "uint32.h"
static uint32 seed[32];
static uint32 in[12];
static uint32 out[8];
static int outleft = 0;
#define ROTATE(x,b) (((x) << (b)) | ((x) >> (32 - (b))))
#define MUSH(i,b) x = t[i] += (((x ^ seed[i]) + sum) ^ ROTATE(x,b));
static void surf(void)
{
uint32 t[12]; uint32 x; uint32 sum = 0;
int r; int i; int loop;
for (i = 0;i < 12;++i) t[i] = in[i] ^ seed[12 + i];
for (i = 0;i < 8;++i) out[i] = seed[24 + i];
x = t[11];
for (loop = 0;loop < 2;++loop) {
for (r = 0;r < 16;++r) {
sum += 0x9e3779b9;
MUSH(0,5) MUSH(1,7) MUSH(2,9) MUSH(3,13)
MUSH(4,5) MUSH(5,7) MUSH(6,9) MUSH(7,13)
MUSH(8,5) MUSH(9,7) MUSH(10,9) MUSH(11,13)
}
for (i = 0;i < 8;++i) out[i] ^= t[i + 4];
}
}
void dns_random_init(const char data[128])
{
int i;
struct taia t;
char tpack[16];
for (i = 0;i < 32;++i)
uint32_unpack(data + 4 * i,seed + i);
taia_now(&t);
taia_pack(tpack,&t);
for (i = 0;i < 4;++i)
uint32_unpack(tpack + 4 * i,in + 4 + i);
in[8] = getpid();
in[9] = getppid();
/* more space in 10 and 11, but this is probably enough */
}
unsigned int dns_random(unsigned int n)
{
if (!n) return 0;
if (!outleft) {
if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3];
surf();
outleft = 8;
}
return out[--outleft] % n;
}

@ -0,0 +1,85 @@
#include <stdlib.h>
#include "taia.h"
#include "openreadclose.h"
#include "byte.h"
#include "ip4.h"
#include "ip6.h"
#include "dns.h"
static stralloc data = {0};
static int init(char ip[256])
{
int i;
int j;
int iplen = 0;
char *x;
x = getenv("DNSCACHEIP");
if (x)
while (iplen <= 60) {
if (*x == '.')
++x;
else {
i = scan_ip6(x,ip + iplen);
if (!i) break;
x += i;
iplen += 16;
}
}
if (!iplen) {
i = openreadclose("/etc/resolv.conf",&data,64);
if (i == -1) return -1;
if (i) {
if (!stralloc_append(&data,"\n")) return -1;
i = 0;
for (j = 0;j < data.len;++j)
if (data.s[j] == '\n') {
if (byte_equal("nameserver ",11,data.s + i) || byte_equal("nameserver\t",11,data.s + i)) {
i += 10;
while ((data.s[i] == ' ') || (data.s[i] == '\t'))
++i;
if (iplen <= 60)
if (scan_ip6(data.s + i,ip + iplen)) {
iplen += 16;
}
}
i = j + 1;
}
}
}
if (!iplen) {
byte_copy(ip,16,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1");
iplen = 16;
}
byte_zero(ip + iplen,256 - iplen);
return 0;
}
static int ok = 0;
static unsigned int uses;
static struct taia deadline;
static char ip[256]; /* defined if ok */
int dns_resolvconfip(char s[256])
{
struct taia now;
taia_now(&now);
if (taia_less(&deadline,&now)) ok = 0;
if (!uses) ok = 0;
if (!ok) {
if (init(ip) == -1) return -1;
taia_uint(&deadline,600);
taia_add(&deadline,&now,&deadline);
uses = 10000;
ok = 1;
}
--uses;
byte_copy(s,256,ip);
return 0;
}

@ -0,0 +1,131 @@
#include <unistd.h>
#include <stdlib.h>
#include "taia.h"
#include "byte.h"
#include "str.h"
#include "openreadclose.h"
#include "dns.h"
static stralloc data = {0};
static int init(stralloc *rules)
{
char host[256];
const char *x;
int i;
int j;
int k;
if (!stralloc_copys(rules,"")) return -1;
x = getenv("DNSREWRITEFILE");
if (!x) x = "/etc/dnsrewrite";
i = openreadclose(x,&data,64);
if (i == -1) return -1;
if (i) {
if (!stralloc_append(&data,"\n")) return -1;
i = 0;
for (j = 0;j < data.len;++j)
if (data.s[j] == '\n') {
if (!stralloc_catb(rules,data.s + i,j - i)) return -1;
while (rules->len) {
if (rules->s[rules->len - 1] != ' ')
if (rules->s[rules->len - 1] != '\t')
if (rules->s[rules->len - 1] != '\r')
break;
--rules->len;
}
if (!stralloc_0(rules)) return -1;
i = j + 1;
}
return 0;
}
x = getenv("LOCALDOMAIN");
if (x) {
if (!stralloc_copys(&data,x)) return -1;
if (!stralloc_append(&data," ")) return -1;
if (!stralloc_copys(rules,"?:")) return -1;
i = 0;
for (j = 0;j < data.len;++j)
if (data.s[j] == ' ') {
if (!stralloc_cats(rules,"+.")) return -1;
if (!stralloc_catb(rules,data.s + i,j - i)) return -1;
i = j + 1;
}
if (!stralloc_0(rules)) return -1;
if (!stralloc_cats(rules,"*.:")) return -1;
if (!stralloc_0(rules)) return -1;
return 0;
}
i = openreadclose("/etc/resolv.conf",&data,64);
if (i == -1) return -1;
if (i) {
if (!stralloc_append(&data,"\n")) return -1;
i = 0;
for (j = 0;j < data.len;++j)
if (data.s[j] == '\n') {
if (byte_equal("search ",7,data.s + i) || byte_equal("search\t",7,data.s + i) || byte_equal("domain ",7,data.s + i) || byte_equal("domain\t",7,data.s + i)) {
if (!stralloc_copys(rules,"?:")) return -1;
i += 7;
while (i < j) {
k = byte_chr(data.s + i,j - i,' ');
k = byte_chr(data.s + i,k,'\t');
if (!k) { ++i; continue; }
if (!stralloc_cats(rules,"+.")) return -1;
if (!stralloc_catb(rules,data.s + i,k)) return -1;
i += k;
}
if (!stralloc_0(rules)) return -1;
if (!stralloc_cats(rules,"*.:")) return -1;
if (!stralloc_0(rules)) return -1;
return 0;
}
i = j + 1;
}
}
host[0] = 0;
if (gethostname(host,sizeof host) == -1) return -1;
host[(sizeof host) - 1] = 0;
i = str_chr(host,'.');
if (host[i]) {
if (!stralloc_copys(rules,"?:")) return -1;
if (!stralloc_cats(rules,host + i)) return -1;
if (!stralloc_0(rules)) return -1;
}
if (!stralloc_cats(rules,"*.:")) return -1;
if (!stralloc_0(rules)) return -1;
return 0;
}
static int ok = 0;
static unsigned int uses;
static struct taia deadline;
static stralloc rules = {0}; /* defined if ok */
int dns_resolvconfrewrite(stralloc *out)
{
struct taia now;
taia_now(&now);
if (taia_less(&deadline,&now)) ok = 0;
if (!uses) ok = 0;
if (!ok) {
if (init(&rules) == -1) return -1;
taia_uint(&deadline,600);
taia_add(&deadline,&now,&deadline);
uses = 10000;
ok = 1;
}
--uses;
if (!stralloc_copy(out,&rules)) return -1;
return 0;
}

@ -0,0 +1,30 @@
#include "iopause.h"
#include "taia.h"
#include "byte.h"
#include "dns.h"
#include "ip6.h"
struct dns_transmit dns_resolve_tx = {0};
int dns_resolve(const char *q,const char qtype[2])
{
struct taia stamp;
struct taia deadline;
char servers[256];
iopause_fd x[1];
int r;
if (dns_resolvconfip(servers) == -1) return -1;
if (dns_transmit_start(&dns_resolve_tx,servers,1,q,qtype,V6any) == -1) return -1;
for (;;) {
taia_now(&stamp);
taia_uint(&deadline,120);
taia_add(&deadline,&deadline,&stamp);
dns_transmit_io(&dns_resolve_tx,x,&deadline);
iopause(x,1,&deadline,&stamp);
r = dns_transmit_get(&dns_resolve_tx,x,&stamp);
if (r == -1) return -1;
if (r == 1) return 0;
}
}

@ -0,0 +1,20 @@
#include "byte.h"
#include "dns.h"
/* XXX: sort servers by configurable notion of closeness? */
/* XXX: pay attention to competence of each server? */
void dns_sortip(char *s,unsigned int n)
{
unsigned int i;
char tmp[4];
n >>= 2;
while (n > 1) {
i = dns_random(n);
--n;
byte_copy(tmp,4,s + (i << 2));
byte_copy(s + (i << 2),4,s + (n << 2));
byte_copy(s + (n << 2),4,tmp);
}
}

@ -0,0 +1,20 @@
#include "byte.h"
#include "dns.h"
/* XXX: sort servers by configurable notion of closeness? */
/* XXX: pay attention to competence of each server? */
void dns_sortip6(char *s,unsigned int n)
{
unsigned int i;
char tmp[16];
n >>= 4;
while (n > 1) {
i = dns_random(n);
--n;
byte_copy(tmp,16,s + (i << 4));
byte_copy(s + (i << 4),16,s + (n << 4));
byte_copy(s + (n << 4),16,tmp);
}
}

@ -0,0 +1,367 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdlib.h>
#include "socket.h"
#include <errno.h>
#include "byte.h"
#include "uint16.h"
#include "dns.h"
#include "ip6.h"
static int serverwantstcp(const char *buf,unsigned int len)
{
char out[12];
if (!dns_packet_copy(buf,len,0,out,12)) return 1;
if (out[2] & 2) return 1;
return 0;
}
static int serverfailed(const char *buf,unsigned int len)
{
char out[12];
unsigned int rcode;
if (!dns_packet_copy(buf,len,0,out,12)) return 1;
rcode = out[3];
rcode &= 15;
if (rcode && (rcode != 3)) { errno = EAGAIN; return 1; }
return 0;
}
static int irrelevant(const struct dns_transmit *d,const char *buf,unsigned int len)
{
char out[12];
char *dn;
unsigned int pos;
pos = dns_packet_copy(buf,len,0,out,12); if (!pos) return 1;
if (byte_diff(out,2,d->query + 2)) return 1;
if (out[4] != 0) return 1;
if (out[5] != 1) return 1;
dn = 0;
pos = dns_packet_getname(buf,len,pos,&dn); if (!pos) return 1;
if (!dns_domain_equal(dn,d->query + 14)) { free(dn); return 1; }
free(dn);
pos = dns_packet_copy(buf,len,pos,out,4); if (!pos) return 1;
if (byte_diff(out,2,d->qtype)) return 1;
if (byte_diff(out + 2,2,DNS_C_IN)) return 1;
return 0;
}
static void packetfree(struct dns_transmit *d)
{
if (!d->packet) return;
free(d->packet);
d->packet = 0;
}
static void queryfree(struct dns_transmit *d)
{
if (!d->query) return;
free(d->query);
d->query = 0;
}
static void socketfree(struct dns_transmit *d)
{
if (!d->s1) return;
close(d->s1 - 1);
d->s1 = 0;
}
void dns_transmit_free(struct dns_transmit *d)
{
queryfree(d);
socketfree(d);
packetfree(d);
}
static int randombind(struct dns_transmit *d)
{
int j;
for (j = 0;j < 10;++j)
if (socket_bind6(d->s1 - 1,d->localip,1025 + dns_random(64510),d->scope_id) == 0)
return 0;
if (socket_bind6(d->s1 - 1,d->localip,0,d->scope_id) == 0)
return 0;
return -1;
}
static const int timeouts[4] = { 1, 3, 11, 45 };
static int thisudp(struct dns_transmit *d)
{
const char *ip;
socketfree(d);
while (d->udploop < 4) {
for (;d->curserver < 16;++d->curserver) {
ip = d->servers + 16 * d->curserver;
if (byte_diff(ip,16,V6any)) {
d->query[2] = dns_random(256);
d->query[3] = dns_random(256);
d->s1 = 1 + socket_udp6();
if (!d->s1) { dns_transmit_free(d); return -1; }
if (randombind(d) == -1) { dns_transmit_free(d); return -1; }
if (socket_connect6(d->s1 - 1,ip,53,d->scope_id) == 0)
if (send(d->s1 - 1,d->query + 2,d->querylen - 2,0) == d->querylen - 2) {
struct taia now;
taia_now(&now);
taia_uint(&d->deadline,timeouts[d->udploop]);
taia_add(&d->deadline,&d->deadline,&now);
d->tcpstate = 0;
return 0;
}
socketfree(d);
}
}
++d->udploop;
d->curserver = 0;
}
dns_transmit_free(d); return -1;
}
static int firstudp(struct dns_transmit *d)
{
d->curserver = 0;
return thisudp(d);
}
static int nextudp(struct dns_transmit *d)
{
++d->curserver;
return thisudp(d);
}
static int thistcp(struct dns_transmit *d)
{
struct taia now;
const char *ip;
socketfree(d);
packetfree(d);
for (;d->curserver < 16;++d->curserver) {
ip = d->servers + 16 * d->curserver;
if (byte_diff(ip,16,V6any)) {
d->query[2] = dns_random(256);
d->query[3] = dns_random(256);
d->s1 = 1 + socket_tcp6();
if (!d->s1) { dns_transmit_free(d); return -1; }
if (randombind(d) == -1) { dns_transmit_free(d); return -1; }
taia_now(&now);
taia_uint(&d->deadline,10);
taia_add(&d->deadline,&d->deadline,&now);
if (socket_connect6(d->s1 - 1,ip,53,d->scope_id) == 0) {
d->tcpstate = 2;
return 0;
}
if ((errno == EINPROGRESS) || (errno == EWOULDBLOCK)) {
d->tcpstate = 1;
return 0;
}
socketfree(d);
}
}
dns_transmit_free(d); return -1;
}
static int firsttcp(struct dns_transmit *d)
{
d->curserver = 0;
return thistcp(d);
}
static int nexttcp(struct dns_transmit *d)
{
++d->curserver;
return thistcp(d);
}
int dns_transmit_start(struct dns_transmit *d,const char servers[256],int flagrecursive,const char *q,const char qtype[2],const char localip[16])
{
unsigned int len;
dns_transmit_free(d);
errno = EIO;
len = dns_domain_length(q);
d->querylen = len + 18;
d->query = malloc(d->querylen);
if (!d->query) return -1;
uint16_pack_big(d->query,len + 16);
byte_copy(d->query + 2,12,flagrecursive ? "\0\0\1\0\0\1\0\0\0\0\0\0" : "\0\0\0\0\0\1\0\0\0\0\0\0gcc-bug-workaround");
byte_copy(d->query + 14,len,q);
byte_copy(d->query + 14 + len,2,qtype);
byte_copy(d->query + 16 + len,2,DNS_C_IN);
byte_copy(d->qtype,2,qtype);
d->servers = servers;
byte_copy(d->localip,16,localip);
d->udploop = flagrecursive ? 1 : 0;
if (len + 16 > 512) return firsttcp(d);
return firstudp(d);
}
void dns_transmit_io(struct dns_transmit *d,iopause_fd *x,struct taia *deadline)
{
x->fd = d->s1 - 1;
switch(d->tcpstate) {
case 0: case 3: case 4: case 5:
x->events = IOPAUSE_READ;
break;
case 1: case 2:
x->events = IOPAUSE_WRITE;
break;
}
if (taia_less(&d->deadline,deadline))
*deadline = d->deadline;
}
int dns_transmit_get(struct dns_transmit *d,const iopause_fd *x,const struct taia *when)
{
char udpbuf[513];
unsigned char ch;
int r;
int fd;
errno = EIO;
fd = d->s1 - 1;
if (!x->revents) {
if (taia_less(when,&d->deadline)) return 0;
errno = ETIMEDOUT;
if (d->tcpstate == 0) return nextudp(d);
return nexttcp(d);
}
if (d->tcpstate == 0) {
/*
have attempted to send UDP query to each server udploop times
have sent query to curserver on UDP socket s
*/
r = recv(fd,udpbuf,sizeof udpbuf,0);
if (r <= 0) {
if (errno == ECONNREFUSED) if (d->udploop == 2) return 0;
return nextudp(d);
}
if (r + 1 > sizeof udpbuf) return 0;
if (irrelevant(d,udpbuf,r)) return 0;
if (serverwantstcp(udpbuf,r)) return firsttcp(d);
if (serverfailed(udpbuf,r)) {
if (d->udploop == 2) return 0;
return nextudp(d);
}
socketfree(d);
d->packetlen = r;
d->packet = malloc(d->packetlen);
if (!d->packet) { dns_transmit_free(d); return -1; }
byte_copy(d->packet,d->packetlen,udpbuf);
queryfree(d);
return 1;
}
if (d->tcpstate == 1) {
/*
have sent connection attempt to curserver on TCP socket s
pos not defined
*/
if (!socket_connected(fd)) return nexttcp(d);
d->pos = 0;
d->tcpstate = 2;
return 0;
}
if (d->tcpstate == 2) {
/*
have connection to curserver on TCP socket s
have sent pos bytes of query
*/
r = write(fd,d->query + d->pos,d->querylen - d->pos);
if (r <= 0) return nexttcp(d);
d->pos += r;
if (d->pos == d->querylen) {
struct taia now;
taia_now(&now);
taia_uint(&d->deadline,10);
taia_add(&d->deadline,&d->deadline,&now);
d->tcpstate = 3;
}
return 0;
}
if (d->tcpstate == 3) {
/*
have sent entire query to curserver on TCP socket s
pos not defined
*/
r = read(fd,&ch,1);
if (r <= 0) return nexttcp(d);
d->packetlen = ch;
d->tcpstate = 4;
return 0;
}
if (d->tcpstate == 4) {
/*
have sent entire query to curserver on TCP socket s
pos not defined
have received one byte of packet length into packetlen
*/
r = read(fd,&ch,1);
if (r <= 0) return nexttcp(d);
d->packetlen <<= 8;
d->packetlen += ch;
d->tcpstate = 5;
d->pos = 0;
d->packet = malloc(d->packetlen);
if (!d->packet) { dns_transmit_free(d); return -1; }
return 0;
}
if (d->tcpstate == 5) {
/*
have sent entire query to curserver on TCP socket s
have received entire packet length into packetlen
packet is allocated
have received pos bytes of packet
*/
r = read(fd,d->packet + d->pos,d->packetlen - d->pos);
if (r <= 0) return nexttcp(d);
d->pos += r;
if (d->pos < d->packetlen) return 0;
socketfree(d);
if (irrelevant(d,d->packet,d->packetlen)) return nexttcp(d);
if (serverwantstcp(d->packet,d->packetlen)) return nexttcp(d);
if (serverfailed(d->packet,d->packetlen)) return nexttcp(d);
queryfree(d);
return 1;
}
return 0;
}

@ -0,0 +1,59 @@
#include "stralloc.h"
#include "uint16.h"
#include "byte.h"
#include "dns.h"
int dns_txt_packet(stralloc *out,const char *buf,unsigned int len)
{
unsigned int pos;
char header[12];
uint16 numanswers;
uint16 datalen;
char ch;
unsigned int txtlen;
int i;
if (!stralloc_copys(out,"")) return -1;
pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return -1;
uint16_unpack_big(header + 6,&numanswers);
pos = dns_packet_skipname(buf,len,pos); if (!pos) return -1;
pos += 4;
while (numanswers--) {
pos = dns_packet_skipname(buf,len,pos); if (!pos) return -1;
pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) return -1;
uint16_unpack_big(header + 8,&datalen);
if (byte_equal(header,2,DNS_T_TXT))
if (byte_equal(header + 2,2,DNS_C_IN)) {
if (pos + datalen > len) return -1;
txtlen = 0;
for (i = 0;i < datalen;++i) {
ch = buf[pos + i];
if (!txtlen)
txtlen = (unsigned char) ch;
else {
--txtlen;
if (ch < 32) ch = '?';
if (ch > 126) ch = '?';
if (!stralloc_append(out,&ch)) return -1;
}
}
}
pos += datalen;
}
return 0;
}
static char *q = 0;
int dns_txt(stralloc *out,const stralloc *fqdn)
{
if (!dns_domain_fromdot(&q,fqdn->s,fqdn->len)) return -1;
if (dns_resolve(q,DNS_T_TXT) == -1) return -1;
if (dns_txt_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen) == -1) return -1;
dns_transmit_free(&dns_resolve_tx);
dns_domain_free(&q);
return 0;
}

@ -0,0 +1,19 @@
#ifndef IOPAUSE_H
#define IOPAUSE_H
/* sysdep: -poll */
typedef struct {
int fd;
short events;
short revents;
} iopause_fd;
#define IOPAUSE_READ 1
#define IOPAUSE_WRITE 4
#include "taia.h"
extern void iopause(iopause_fd *,unsigned int,struct taia *,struct taia *);
#endif

@ -0,0 +1,18 @@
#ifndef IOPAUSE_H
#define IOPAUSE_H
/* sysdep: +poll */
#define IOPAUSE_POLL
#include <sys/types.h>
#include <poll.h>
typedef struct pollfd iopause_fd;
#define IOPAUSE_READ POLLIN
#define IOPAUSE_WRITE POLLOUT
#include "taia.h"
extern void iopause(iopause_fd *,unsigned int,struct taia *,struct taia *);
#endif

@ -0,0 +1,16 @@
#include <errno.h>
#include "open.h"
#include "readclose.h"
#include "openreadclose.h"
int openreadclose(const char *fn,stralloc *sa,unsigned int bufsize)
{
int fd;
fd = open_read(fn);
if (fd == -1) {
if (errno == ENOENT) return 0;
return -1;
}
if (readclose(fd,sa,bufsize) == -1) return -1;
return 1;
}

@ -0,0 +1,21 @@
#include <unistd.h>
#include <errno.h>
#include "readclose.h"
int readclose_append(int fd,stralloc *sa,unsigned int bufsize)
{
int r;
for (;;) {
if (!stralloc_readyplus(sa,bufsize)) { close(fd); return -1; }
r = read(fd,sa->s + sa->len,bufsize);
if (r == -1) if (errno == EINTR) continue;
if (r <= 0) { close(fd); return r; }
sa->len += r;
}
}
int readclose(int fd,stralloc *sa,unsigned int bufsize)
{
if (!stralloc_copys(sa,"")) { close(fd); return -1; }
return readclose_append(fd,sa,bufsize);
}

@ -0,0 +1,8 @@
#ifndef OPENREADCLOSE_H
#define OPENREADCLOSE_H
#include "stralloc.h"
extern int openreadclose(const char *filename,stralloc *buf,unsigned int initiallength);
#endif

@ -0,0 +1,9 @@
#ifndef READCLOSE_H
#define READCLOSE_H
#include "stralloc.h"
extern int readclose_append(int fd,stralloc *buf,unsigned int initlen);
extern int readclose(int fd,stralloc *buf,unsigned int initlen);
#endif

@ -1,7 +1,10 @@
#ifndef SCAN_H
#define SCAN_H
#ifdef __dietlibc__
#include <sys/cdefs.h>
#endif
#ifndef __pure__
#define __pure__
#endif

@ -0,0 +1,10 @@
#ifndef SELECT_H
#define SELECT_H
/* sysdep: -sysselect */
#include <sys/types.h>
#include <sys/time.h>
extern int select();
#endif

@ -0,0 +1,11 @@
#ifndef SELECT_H
#define SELECT_H
/* sysdep: +sysselect */
#include <sys/types.h>
#include <sys/time.h>
#include <sys/select.h>
extern int select();
#endif

@ -1,7 +1,9 @@
#ifndef STR_H
#define STR_H
#ifdef __dietlibc__
#include <sys/cdefs.h>
#endif
#ifndef __pure__
#define __pure__
#endif

@ -1,8 +1,6 @@
#ifndef STRALLOC_H
#define STRALLOC_H
#include <sys/cdefs.h>
/* stralloc is the internal data structure all functions are working on.
* s is the string.
* len is the used length of the string.
@ -41,7 +39,7 @@ extern int stralloc_copys(stralloc* sa,const char* buf);
/* stralloc_copy copies the string stored in sa2 into sa. It is the same
* as stralloc_copyb(&sa,sa2.s,sa2.len). sa2 must already be allocated. */
extern int stralloc_copy(stralloc* sa,stralloc* sa2);
extern int stralloc_copy(stralloc* sa,const stralloc* sa2);
/* stralloc_catb adds the string buf[0], buf[1], ... buf[len-1] to the
* end of the string stored in sa, allocating space if necessary, and

@ -1,7 +1,7 @@
#include "stralloc.h"
#include "str.h"
extern int stralloc_copy(stralloc *sa,stralloc *sa2) {
extern int stralloc_copy(stralloc *sa,const stralloc *sa2) {
return stralloc_copyb(sa,sa2->s,sa2->len);
}

42
t.c

@ -11,15 +11,57 @@
#include "open.h"
#include "byte.h"
#include "textcode.h"
#include "dns.h"
#include "case.h"
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#define rdtscl(low) \
__asm__ __volatile__ ("rdtsc" : "=a" (low) : : "edx")
int main(int argc,char* argv[]) {
static char seed[128];
static stralloc fqdn;
static stralloc out;
char str[IP4_FMT];
int i;
dns_random_init(seed);
if (*argv) ++argv;
while (*argv) {
if (!stralloc_copys(&fqdn,*argv)) {
buffer_putsflush(buffer_2,"out of memory\n");
return 111;
}
if (dns_ip4(&out,&fqdn) == -1) {
buffer_puts(buffer_2,"unable to find IP address for ");
buffer_puts(buffer_2,*argv);
buffer_puts(buffer_2,": ");
buffer_puts(buffer_2,strerror(errno));
buffer_putnlflush(buffer_2);
return 111;
}
for (i = 0;i + 4 <= out.len;i += 4) {
buffer_put(buffer_1,str,ip4_fmt(str,out.s + i));
buffer_puts(buffer_1," ");
}
buffer_puts(buffer_1,"\n");
++argv;
}
buffer_flush(buffer_1);
return 0;
#if 0
char buf[]="FnOrD";
case_lowers(buf);
puts(buf);
#endif
#if 0
char buf[100]="foo bar baz";
printf("%d (expect 7)\n",byte_rchr(buf,11,' '));
#endif
#if 0
unsigned long size;
char* buf=mmap_read(argv[1],&size);

26
tai.h

@ -0,0 +1,26 @@
#ifndef TAI_H
#define TAI_H
#include "uint64.h"
struct tai {
uint64 x;
} ;
#define tai_unix(t,u) ((void) ((t)->x = 4611686018427387914ULL + (uint64) (u)))
extern void tai_now(struct tai *);
#define tai_approx(t) ((double) ((t)->x))
extern void tai_add(struct tai *,const struct tai *,const struct tai *);
extern void tai_sub(struct tai *,const struct tai *,const struct tai *);
#define tai_less(t,u) ((t)->x < (u)->x)
#define TAI_PACK 8
extern void tai_pack(char *,const struct tai *);
extern void tai_unpack(const char *,struct tai *);
extern void tai_uint(struct tai *,unsigned int);
#endif

@ -0,0 +1,6 @@
#include "tai.h"
void tai_add(struct tai *t,const struct tai *u,const struct tai *v)
{
t->x = u->x + v->x;
}

@ -0,0 +1,7 @@
#include <time.h>
#include "tai.h"
void tai_now(struct tai *t)
{
tai_unix(t,time((time_t *) 0));
}

@ -0,0 +1,16 @@
#include "tai.h"
void tai_pack(char *s,const struct tai *t)
{
uint64 x;
x = t->x;
s[7] = x & 255; x >>= 8;
s[6] = x & 255; x >>= 8;
s[5] = x & 255; x >>= 8;
s[4] = x & 255; x >>= 8;
s[3] = x & 255; x >>= 8;
s[2] = x & 255; x >>= 8;
s[1] = x & 255; x >>= 8;
s[0] = x;
}

@ -0,0 +1,6 @@
#include "tai.h"
void tai_sub(struct tai *t,const struct tai *u,const struct tai *v)
{
t->x = u->x - v->x;
}

@ -0,0 +1,6 @@
#include "tai.h"
void tai_uint(struct tai *t,unsigned int u)
{
t->x = u;
}

@ -0,0 +1,16 @@
#include "tai.h"
void tai_unpack(const char *s,struct tai *t)
{
uint64 x;
x = (unsigned char) s[0];
x <<= 8; x += (unsigned char) s[1];
x <<= 8; x += (unsigned char) s[2];
x <<= 8; x += (unsigned char) s[3];
x <<= 8; x += (unsigned char) s[4];
x <<= 8; x += (unsigned char) s[5];
x <<= 8; x += (unsigned char) s[6];
x <<= 8; x += (unsigned char) s[7];
t->x = x;
}

@ -0,0 +1,68 @@
#ifndef TAIA_H
#define TAIA_H
/* Times with 1 attosecond precision */
#include "tai.h"
/* A struct taia value is a number between 0 inclusive and 2^64
* exclusive. The number is a multiple of 10^-18. The format of struct
* taia is designed to speed up common operations; applications should
* not look inside struct taia. */
struct taia {
struct tai sec;
unsigned long nano; /* 0...999999999 */
unsigned long atto; /* 0...999999999 */
};
/* extract seconds */
extern void taia_tai(const struct taia *source,struct tai *dest);
/* get current time */
extern void taia_now(struct taia *);
/* return double-precision approximation; always nonnegative */
extern double taia_approx(const struct taia *);
/* return double-precision approximation of the fraction part;
* always nonnegative */
extern double taia_frac(const struct taia *);
/* add source1 to source2 modulo 2^64 and put the result in dest.
* The inputs and output may overlap */
extern void taia_add(struct taia *dest,const struct taia *source1,const struct taia *source2);
/* add secs seconds to source modulo 2^64 and put the result in dest. */
extern void taia_addsec(struct taia *dest,const struct taia *source,int secs);
/* subtract source2 from source1 modulo 2^64 and put the result in dest.
* The inputs and output may overlap */
extern void taia_sub(struct taia *,const struct taia *,const struct taia *);
/* divide source by 2, rouding down to a multiple of 10^-18, and put the
* result into dest. The input and output may overlap */
extern void taia_half(struct taia *dest,const struct taia *source);
/* return 1 if a is less than b, 0 otherwise */
extern int taia_less(const struct taia *a,const struct taia *b);
#define TAIA_PACK 16
/* char buf[TAIA_PACK] can be used to store a TAI64NA label in external
* representation, which can then be used to transmit the binary
* representation over a network or store it on disk in a byte order
* independent fashion */
/* convert a TAI64NA label from internal format in src to external
* TAI64NA format in buf. */
extern void taia_pack(char *buf,const struct taia *src);
/* convert a TAI64NA label from external TAI64NA format in buf to
* internal format in dest. */
extern void taia_unpack(const char *buf,struct taia *dest);
#define TAIA_FMTFRAC 19
/* print the 18-digit fraction part of t in decimal, without a decimal
* point but with leading zeros, into the character buffer s, without a
* terminating \0. It returns 18, the number of characters written. s
* may be zero; then taia_fmtfrac returns 18 without printing anything.
* */
extern unsigned int taia_fmtfrac(char *s,const struct taia *t);
/* initialize t to secs seconds. */
extern void taia_uint(struct taia *t,unsigned int secs);
#endif

@ -0,0 +1,18 @@
#include "taia.h"
/* XXX: breaks tai encapsulation */
void taia_add(struct taia *t,const struct taia *u,const struct taia *v)
{
t->sec.x = u->sec.x + v->sec.x;
t->nano = u->nano + v->nano;
t->atto = u->atto + v->atto;
if (t->atto > 999999999UL) {
t->atto -= 1000000000UL;
++t->nano;
}
if (t->nano > 999999999UL) {
t->nano -= 1000000000UL;
++t->sec.x;
}
}

@ -0,0 +1,6 @@
#include "taia.h"
double taia_approx(const struct taia *t)
{
return tai_approx(&t->sec) + taia_frac(t);
}

@ -0,0 +1,6 @@
#include "taia.h"
double taia_frac(const struct taia *t)
{
return (t->atto * 0.000000001 + t->nano) * 0.000000001;
}

@ -0,0 +1,12 @@
#include "taia.h"
/* XXX: breaks tai encapsulation */
int taia_less(const struct taia *t,const struct taia *u)
{
if (t->sec.x < u->sec.x) return 1;
if (t->sec.x > u->sec.x) return 0;
if (t->nano < u->nano) return 1;
if (t->nano > u->nano) return 0;
return t->atto < u->atto;
}

@ -0,0 +1,12 @@
#include <sys/types.h>
#include <sys/time.h>
#include "taia.h"
void taia_now(struct taia *t)
{
struct timeval now;
gettimeofday(&now,(struct timezone *) 0);
tai_unix(&t->sec,now.tv_sec);
t->nano = 1000 * now.tv_usec + 500;
t->atto = 0;
}

@ -0,0 +1,20 @@
#include "taia.h"
void taia_pack(char *s,const struct taia *t)
{
unsigned long x;
tai_pack(s,&t->sec);
s += 8;
x = t->atto;
s[7] = x & 255; x >>= 8;
s[6] = x & 255; x >>= 8;
s[5] = x & 255; x >>= 8;
s[4] = x;
x = t->nano;
s[3] = x & 255; x >>= 8;
s[2] = x & 255; x >>= 8;
s[1] = x & 255; x >>= 8;
s[0] = x;
}

@ -0,0 +1,21 @@
#include "taia.h"
/* XXX: breaks tai encapsulation */
void taia_sub(struct taia *t,const struct taia *u,const struct taia *v)
{
unsigned long unano = u->nano;
unsigned long uatto = u->atto;
t->sec.x = u->sec.x - v->sec.x;
t->nano = unano - v->nano;
t->atto = uatto - v->atto;
if (t->atto > uatto) {
t->atto += 1000000000UL;
--t->nano;
}
if (t->nano > unano) {
t->nano += 1000000000UL;
--t->sec.x;
}
}

@ -0,0 +1,6 @@
#include "taia.h"
void taia_tai(const struct taia *ta,struct tai *t)
{
*t = ta->sec;
}

@ -0,0 +1,10 @@
#include "taia.h"
/* XXX: breaks tai encapsulation */
void taia_uint(struct taia *t,unsigned int s)
{
t->sec.x = s;
t->nano = 0;
t->atto = 0;
}

@ -0,0 +1,18 @@
#include <sys/types.h>
#include <fcntl.h>
#include <poll.h>
int main()
{
struct pollfd x;
x.fd = open("trypoll.c",O_RDONLY);
if (x.fd == -1) _exit(111);
x.events = POLLIN;
if (poll(&x,1,10) == -1) _exit(1);
if (x.revents != POLLIN) _exit(1);
/* XXX: try to detect and avoid poll() imitation libraries */
_exit(0);
}

@ -0,0 +1,8 @@
#include <sys/types.h>
#include <sys/time.h>
#include <sys/select.h> /* SVR4 silliness */
void foo()
{
;
}

@ -1,12 +1,6 @@
#ifndef UINT64_H
#define UINT64_H
#include <endian.h>
#if __WORDSIZE == 64
typedef unsigned long uint64;
#else
typedef unsigned long long uint64;
#endif
#endif

@ -0,0 +1,76 @@
#include "taia.h"
#include "select.h"
#include "iopause.h"
void iopause(iopause_fd *x,unsigned int len,struct taia *deadline,struct taia *stamp)
{
struct taia t;
int millisecs;
double d;
int i;
if (taia_less(deadline,stamp))
millisecs = 0;
else {
t = *stamp;
taia_sub(&t,deadline,&t);
d = taia_approx(&t);
if (d > 1000.0) d = 1000.0;
millisecs = d * 1000.0 + 20.0;
}
for (i = 0;i < len;++i)
x[i].revents = 0;
#ifdef IOPAUSE_POLL
poll(x,len,millisecs);
/* XXX: some kernels apparently need x[0] even if len is 0 */
/* XXX: how to handle EAGAIN? are kernels really this dumb? */
/* XXX: how to handle EINVAL? when exactly can this happen? */
#else
{
struct timeval tv;
fd_set rfds;
fd_set wfds;
int nfds;
int fd;
FD_ZERO(&rfds);
FD_ZERO(&wfds);
nfds = 1;
for (i = 0;i < len;++i) {
fd = x[i].fd;
if (fd < 0) continue;
if (fd >= 8 * sizeof(fd_set)) continue; /*XXX*/
if (fd >= nfds) nfds = fd + 1;
if (x[i].events & IOPAUSE_READ) FD_SET(fd,&rfds);
if (x[i].events & IOPAUSE_WRITE) FD_SET(fd,&wfds);
}
tv.tv_sec = millisecs / 1000;
tv.tv_usec = 1000 * (millisecs % 1000);
if (select(nfds,&rfds,&wfds,(fd_set *) 0,&tv) <= 0)
return;
/* XXX: for EBADF, could seek out and destroy the bad descriptor */
for (i = 0;i < len;++i) {
fd = x[i].fd;
if (fd < 0) continue;
if (fd >= 8 * sizeof(fd_set)) continue; /*XXX*/
if (x[i].events & IOPAUSE_READ)
if (FD_ISSET(fd,&rfds)) x[i].revents |= IOPAUSE_READ;
if (x[i].events & IOPAUSE_WRITE)
if (FD_ISSET(fd,&wfds)) x[i].revents |= IOPAUSE_WRITE;
}
}
#endif
}
Loading…
Cancel
Save