Compare commits
29 Commits
c0d188f7b3
...
58dcca3b5e
Author | SHA1 | Date |
---|---|---|
Jonas 'Sortie' Termansen | 58dcca3b5e | |
Jonas 'Sortie' Termansen | 3f4e1a4732 | |
Jonas 'Sortie' Termansen | 718f69e576 | |
Jonas 'Sortie' Termansen | db9e4f415c | |
Jonas 'Sortie' Termansen | 30c6849616 | |
Jonas 'Sortie' Termansen | cbe2ba567b | |
Jonas 'Sortie' Termansen | c8c6f48037 | |
Jonas 'Sortie' Termansen | ea89ece89c | |
Jonas 'Sortie' Termansen | 0b32b1fc93 | |
Jonas 'Sortie' Termansen | 0969b54fb6 | |
Jonas 'Sortie' Termansen | 6dad066eaf | |
Jonas 'Sortie' Termansen | 2a1263c647 | |
Jonas 'Sortie' Termansen | 4a8d728fa6 | |
Jonas 'Sortie' Termansen | b5cab6ccbd | |
Jonas 'Sortie' Termansen | 2447eeb18c | |
Jonas 'Sortie' Termansen | 539dfe1833 | |
Jonas 'Sortie' Termansen | 1d48ebecf8 | |
Jonas 'Sortie' Termansen | 05b709480b | |
Jonas 'Sortie' Termansen | edf9987b2c | |
Jonas 'Sortie' Termansen | 68b73fd2b3 | |
Jonas 'Sortie' Termansen | f0a635fd8c | |
Jonas 'Sortie' Termansen | cfa4d0cac1 | |
Jonas 'Sortie' Termansen | f180e2ca98 | |
Jonas 'Sortie' Termansen | 86d12f43a6 | |
Jonas 'Sortie' Termansen | ddc60e265a | |
Jonas 'Sortie' Termansen | f0caedab74 | |
Jonas 'Sortie' Termansen | c5beb7bf51 | |
Jonas 'Sortie' Termansen | 373cf6cd3a | |
Jonas 'Sortie' Termansen | 26290f64ce |
2
Makefile
2
Makefile
|
@ -22,7 +22,6 @@ editor \
|
||||||
ext \
|
ext \
|
||||||
fat \
|
fat \
|
||||||
games \
|
games \
|
||||||
host \
|
|
||||||
hostname \
|
hostname \
|
||||||
ifconfig \
|
ifconfig \
|
||||||
init \
|
init \
|
||||||
|
@ -686,6 +685,7 @@ release-repository: sysroot $(SYSTEM_INITRD) $(SORTIX_RELEASE_DIR)/$(RELEASE)/re
|
||||||
cp $(SORTIX_REPOSITORY_DIR)/$(HOST)/$$port.tix.tar.xz $(SORTIX_RELEASE_DIR)/$(RELEASE)/repository/$(HOST) && \
|
cp $(SORTIX_REPOSITORY_DIR)/$(HOST)/$$port.tix.tar.xz $(SORTIX_RELEASE_DIR)/$(RELEASE)/repository/$(HOST) && \
|
||||||
cp $(SORTIX_REPOSITORY_DIR)/$(HOST)/$$port.version $(SORTIX_RELEASE_DIR)/$(RELEASE)/repository/$(HOST); \
|
cp $(SORTIX_REPOSITORY_DIR)/$(HOST)/$$port.version $(SORTIX_RELEASE_DIR)/$(RELEASE)/repository/$(HOST); \
|
||||||
done
|
done
|
||||||
|
tix-repository --generation=3 $(SORTIX_RELEASE_DIR)/$(RELEASE)/repository/$(HOST)
|
||||||
|
|
||||||
.PHONY: release-scripts
|
.PHONY: release-scripts
|
||||||
release-scripts: \
|
release-scripts: \
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
host
|
|
|
@ -1,25 +0,0 @@
|
||||||
include ../build-aux/platform.mak
|
|
||||||
include ../build-aux/compiler.mak
|
|
||||||
include ../build-aux/version.mak
|
|
||||||
include ../build-aux/dirs.mak
|
|
||||||
|
|
||||||
OPTLEVEL?=$(DEFAULT_OPTLEVEL)
|
|
||||||
CFLAGS?=$(OPTLEVEL)
|
|
||||||
|
|
||||||
CFLAGS += -Wall -Wextra
|
|
||||||
|
|
||||||
BINARIES = host
|
|
||||||
|
|
||||||
all: $(BINARIES)
|
|
||||||
|
|
||||||
.PHONY: all install clean
|
|
||||||
|
|
||||||
install: all
|
|
||||||
mkdir -p $(DESTDIR)$(BINDIR)
|
|
||||||
install $(BINARIES) $(DESTDIR)$(BINDIR)
|
|
||||||
|
|
||||||
%: %.c
|
|
||||||
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) $< -o $@
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f $(BINARIES)
|
|
511
host/host.c
511
host/host.c
|
@ -1,511 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*
|
|
||||||
* host.c
|
|
||||||
* Domain name system client.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <sys/dnsconfig.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <endian.h>
|
|
||||||
#include <err.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#define DNS_SIZE 512
|
|
||||||
#define DNS_NAME_MAX 255
|
|
||||||
#define DNS_LABEL_MAX 64
|
|
||||||
|
|
||||||
struct dns_header
|
|
||||||
{
|
|
||||||
uint16_t id;
|
|
||||||
uint16_t flags;
|
|
||||||
uint16_t qdcount;
|
|
||||||
uint16_t ancount;
|
|
||||||
uint16_t nscount;
|
|
||||||
uint16_t arcount;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dns_question
|
|
||||||
{
|
|
||||||
uint16_t qtype;
|
|
||||||
uint16_t qclass;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dns_record
|
|
||||||
{
|
|
||||||
uint16_t type;
|
|
||||||
uint16_t class;
|
|
||||||
uint16_t ttl_high;
|
|
||||||
uint16_t ttl_low;
|
|
||||||
uint16_t rdlength;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define DNS_HEADER_FLAGS_RCODE_MASK (0xF << 0)
|
|
||||||
#define DNS_HEADER_FLAGS_RCODE_NO (0 << 0)
|
|
||||||
#define DNS_HEADER_FLAGS_RCODE_FORMAT (1 << 0)
|
|
||||||
#define DNS_HEADER_FLAGS_RCODE_SERVER (2 << 0)
|
|
||||||
#define DNS_HEADER_FLAGS_RCODE_NAME (3 << 0)
|
|
||||||
#define DNS_HEADER_FLAGS_RCODE_NOT_IMPLEMENTED (4 << 0)
|
|
||||||
#define DNS_HEADER_FLAGS_RCODE_REFUSED (5 << 0)
|
|
||||||
#define DNS_HEADER_FLAGS_RA (1 << 7)
|
|
||||||
#define DNS_HEADER_FLAGS_RD (1 << 8)
|
|
||||||
#define DNS_HEADER_FLAGS_TC (1 << 9)
|
|
||||||
#define DNS_HEADER_FLAGS_AA (1 << 10)
|
|
||||||
#define DNS_HEADER_FLAGS_OPCODE_MASK (0xF << 11)
|
|
||||||
#define DNS_HEADER_FLAGS_OPCODE_QUERY (0 << 11)
|
|
||||||
#define DNS_HEADER_FLAGS_OPCODE_IQUERY (1 << 11)
|
|
||||||
#define DNS_HEADER_FLAGS_OPCODE_STATUS (2 << 11)
|
|
||||||
#define DNS_HEADER_FLAGS_QR (1 << 15)
|
|
||||||
|
|
||||||
#define DNS_TYPE_A 1
|
|
||||||
#define DNS_TYPE_NS 2
|
|
||||||
#define DNS_TYPE_MD 3
|
|
||||||
#define DNS_TYPE_MF 4
|
|
||||||
#define DNS_TYPE_CNAME 5
|
|
||||||
#define DNS_TYPE_SOA 6
|
|
||||||
#define DNS_TYPE_MB 7
|
|
||||||
#define DNS_TYPE_MG 8
|
|
||||||
#define DNS_TYPE_MR 9
|
|
||||||
#define DNS_TYPE_NULL 10
|
|
||||||
#define DNS_TYPE_WKS 11
|
|
||||||
#define DNS_TYPE_PTR 12
|
|
||||||
#define DNS_TYPE_HINFO 13
|
|
||||||
#define DNS_TYPE_MINFO 14
|
|
||||||
#define DNS_TYPE_MX 15
|
|
||||||
#define DNS_TYPE_TXT 16
|
|
||||||
#define DNS_TYPE_AAAA 28
|
|
||||||
|
|
||||||
#define DNS_QTYPE_AXFR 252
|
|
||||||
#define DNS_QTYPE_MAILB 253
|
|
||||||
#define DNS_QTYPE_MAILA 254
|
|
||||||
#define DNS_QTYPE_ANY 255
|
|
||||||
|
|
||||||
#define DNS_CLASS_IN 1
|
|
||||||
#define DNS_CLASS_CS 2
|
|
||||||
#define DNS_CLASS_CH 3
|
|
||||||
#define DNS_CLASS_HS 4
|
|
||||||
|
|
||||||
#define DNS_QCLASS_ANY 255
|
|
||||||
|
|
||||||
static size_t encode_dns_header(unsigned char* msg,
|
|
||||||
size_t offset,
|
|
||||||
const struct dns_header* hdrin)
|
|
||||||
{
|
|
||||||
struct dns_header hdr;
|
|
||||||
if ( DNS_SIZE - offset < sizeof(hdr) )
|
|
||||||
errx(1, "dns message too large");
|
|
||||||
hdr.id = htobe16(hdrin->id);
|
|
||||||
hdr.flags = htobe16(hdrin->flags);
|
|
||||||
hdr.qdcount = htobe16(hdrin->qdcount);
|
|
||||||
hdr.ancount = htobe16(hdrin->ancount);
|
|
||||||
hdr.nscount = htobe16(hdrin->nscount);
|
|
||||||
hdr.arcount = htobe16(hdrin->arcount);
|
|
||||||
memcpy(msg + offset, &hdr, sizeof(hdr));
|
|
||||||
return offset + sizeof(hdr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t encode_dns_byte(unsigned char* msg,
|
|
||||||
size_t offset,
|
|
||||||
unsigned char byte)
|
|
||||||
{
|
|
||||||
if ( DNS_SIZE - offset < 1 )
|
|
||||||
errx(1, "dns message too large");
|
|
||||||
msg[offset] = byte;
|
|
||||||
return offset + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t encode_dns_name(unsigned char* msg,
|
|
||||||
size_t offset,
|
|
||||||
const char* name)
|
|
||||||
{
|
|
||||||
size_t index = 0;
|
|
||||||
size_t namelen = 0;
|
|
||||||
if ( !name[0] )
|
|
||||||
errx(1, "'%s' is not a valid name (unexpected end of input)", name);
|
|
||||||
while ( name[index] )
|
|
||||||
{
|
|
||||||
if ( !strcmp(name + index, ".") )
|
|
||||||
break;
|
|
||||||
if ( name[index] == '.' )
|
|
||||||
errx(1, "'%s' is not a valid name (empty label)", name);
|
|
||||||
size_t length = strcspn(name + index, ".");
|
|
||||||
if ( DNS_LABEL_MAX <= length )
|
|
||||||
errx(1, "'%s' is not a valid name (label too long)", name);
|
|
||||||
if ( namelen++ == DNS_NAME_MAX )
|
|
||||||
errx(1, "'%s' is not a valid name (name is too long)", name);
|
|
||||||
offset = encode_dns_byte(msg, offset, length & 0xFF);
|
|
||||||
for ( size_t i = 0; i < length; i++ )
|
|
||||||
{
|
|
||||||
if ( namelen++ == DNS_NAME_MAX )
|
|
||||||
errx(1, "'%s' is not a valid name (name is too long)", name);
|
|
||||||
offset = encode_dns_byte(msg, offset, name[index + i]);
|
|
||||||
}
|
|
||||||
index += length;
|
|
||||||
if ( name[index] == '.' )
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
if ( namelen++ == DNS_NAME_MAX )
|
|
||||||
errx(1, "'%s' is not a valid name (name is too long)", name);
|
|
||||||
offset = encode_dns_byte(msg, offset, 0);
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t encode_dns_question(unsigned char* msg,
|
|
||||||
size_t offset,
|
|
||||||
const char* name,
|
|
||||||
const struct dns_question* qsin)
|
|
||||||
{
|
|
||||||
offset = encode_dns_name(msg, offset, name);
|
|
||||||
struct dns_question qs;
|
|
||||||
if ( DNS_SIZE - offset < sizeof(qs) )
|
|
||||||
errx(1, "dns message too large");
|
|
||||||
qs.qtype = htobe16(qsin->qtype);
|
|
||||||
qs.qclass = htobe16(qsin->qclass);
|
|
||||||
memcpy(msg + offset, &qs, sizeof(qs));
|
|
||||||
return offset + sizeof(qs);
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t decode_dns_header(const unsigned char* msg,
|
|
||||||
size_t offset,
|
|
||||||
size_t msg_size,
|
|
||||||
struct dns_header* hdrout)
|
|
||||||
{
|
|
||||||
struct dns_header hdr;
|
|
||||||
if ( msg_size - offset < sizeof(hdr) )
|
|
||||||
errx(1, "dns message too small");
|
|
||||||
memcpy(&hdr, msg + offset, sizeof(hdr));
|
|
||||||
hdrout->id = be16toh(hdr.id);
|
|
||||||
hdrout->flags = be16toh(hdr.flags);
|
|
||||||
hdrout->qdcount = be16toh(hdr.qdcount);
|
|
||||||
hdrout->ancount = be16toh(hdr.ancount);
|
|
||||||
hdrout->nscount = be16toh(hdr.nscount);
|
|
||||||
hdrout->arcount = be16toh(hdr.arcount);
|
|
||||||
return offset + sizeof(hdr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t decode_dns_byte(const unsigned char* msg,
|
|
||||||
size_t offset,
|
|
||||||
size_t msg_size,
|
|
||||||
unsigned char* byte)
|
|
||||||
{
|
|
||||||
if ( msg_size <= offset || msg_size - offset < 1 )
|
|
||||||
errx(1, "dns message too small");
|
|
||||||
*byte = msg[offset];
|
|
||||||
return offset + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t decode_dns_name(const unsigned char* msg,
|
|
||||||
size_t offset,
|
|
||||||
size_t msg_size,
|
|
||||||
char* name)
|
|
||||||
{
|
|
||||||
bool real_offset_set = false;
|
|
||||||
size_t real_offset = 0;
|
|
||||||
size_t index = 0;
|
|
||||||
size_t namelen = 0;
|
|
||||||
uint8_t b;
|
|
||||||
while ( true )
|
|
||||||
{
|
|
||||||
if ( namelen++ == DNS_NAME_MAX )
|
|
||||||
errx(1, "name too long");
|
|
||||||
offset = decode_dns_byte(msg, offset, msg_size, &b);
|
|
||||||
if ( 0xC0 & b )
|
|
||||||
{
|
|
||||||
namelen--;
|
|
||||||
size_t ptr = (b & 0x3F) << 8;
|
|
||||||
offset = decode_dns_byte(msg, offset, msg_size, &b);
|
|
||||||
ptr |= b;
|
|
||||||
if ( !real_offset_set )
|
|
||||||
{
|
|
||||||
real_offset = offset;
|
|
||||||
real_offset_set = true;
|
|
||||||
}
|
|
||||||
offset = ptr;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
size_t length = b;
|
|
||||||
if ( DNS_LABEL_MAX <= length )
|
|
||||||
errx(1, "label too long");
|
|
||||||
if ( !length )
|
|
||||||
break;
|
|
||||||
if ( index )
|
|
||||||
name[index++] = '.';
|
|
||||||
for ( size_t i = 0; i < length; i++ )
|
|
||||||
{
|
|
||||||
if ( namelen++ == DNS_NAME_MAX )
|
|
||||||
errx(1, "name too long");
|
|
||||||
offset = decode_dns_byte(msg, offset, msg_size, &b);
|
|
||||||
// TODO: Handle if b == '.'.
|
|
||||||
name[index++] = b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
name[index++] = '.';
|
|
||||||
name[index] = '\0';
|
|
||||||
if ( real_offset_set )
|
|
||||||
return real_offset;
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t decode_dns_question(const unsigned char* msg,
|
|
||||||
size_t offset,
|
|
||||||
size_t msg_size,
|
|
||||||
char* name,
|
|
||||||
struct dns_question* qsout)
|
|
||||||
{
|
|
||||||
offset = decode_dns_name(msg, offset, msg_size, name);
|
|
||||||
struct dns_question qs;
|
|
||||||
if ( msg_size <= offset || msg_size - offset < sizeof(qs) )
|
|
||||||
errx(1, "dns message too small");
|
|
||||||
memcpy(&qs, msg + offset, sizeof(qs));
|
|
||||||
qsout->qtype = be16toh(qs.qtype);
|
|
||||||
qsout->qclass = be16toh(qs.qclass);
|
|
||||||
return offset + sizeof(qs);
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t decode_dns_record(const unsigned char* msg,
|
|
||||||
size_t offset,
|
|
||||||
size_t msg_size,
|
|
||||||
char* name,
|
|
||||||
struct dns_record* rrout)
|
|
||||||
{
|
|
||||||
offset = decode_dns_name(msg, offset, msg_size, name);
|
|
||||||
struct dns_record rr;
|
|
||||||
if ( msg_size <= offset || msg_size - offset < sizeof(rr) )
|
|
||||||
errx(1, "dns message too small");
|
|
||||||
memcpy(&rr, msg + offset, sizeof(rr));
|
|
||||||
rrout->type = be16toh(rr.type);
|
|
||||||
rrout->class = be16toh(rr.class);
|
|
||||||
rrout->ttl_high = be16toh(rr.ttl_high);
|
|
||||||
rrout->ttl_low = be16toh(rr.ttl_low);
|
|
||||||
rrout->rdlength = be16toh(rr.rdlength);
|
|
||||||
return offset + sizeof(rr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void compact_arguments(int* argc, char*** argv)
|
|
||||||
{
|
|
||||||
for ( int i = 0; i < *argc; i++ )
|
|
||||||
{
|
|
||||||
while ( i < *argc && !(*argv)[i] )
|
|
||||||
{
|
|
||||||
for ( int n = i; n < *argc; n++ )
|
|
||||||
(*argv)[n] = (*argv)[n+1];
|
|
||||||
(*argc)--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
|
||||||
{
|
|
||||||
int ipv = 4;
|
|
||||||
|
|
||||||
const char* argv0 = argv[0];
|
|
||||||
for ( int i = 1; i < argc; i++ )
|
|
||||||
{
|
|
||||||
const char* arg = argv[i];
|
|
||||||
if ( arg[0] != '-' || !arg[1] )
|
|
||||||
continue;
|
|
||||||
argv[i] = NULL;
|
|
||||||
if ( !strcmp(arg, "--") )
|
|
||||||
break;
|
|
||||||
if ( arg[1] != '-' )
|
|
||||||
{
|
|
||||||
char c;
|
|
||||||
while ( (c = *++arg) ) switch ( c )
|
|
||||||
{
|
|
||||||
case '4': ipv = 4; break;
|
|
||||||
case '6': ipv = 6; break;
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s: unknown option: %s\n", argv0, arg);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
compact_arguments(&argc, &argv);
|
|
||||||
|
|
||||||
if ( argc < 2 )
|
|
||||||
errx(1, "No host given");
|
|
||||||
const char* host = argv[1];
|
|
||||||
|
|
||||||
char nsipstr[INET_ADDRSTRLEN];
|
|
||||||
const char* nameserver;
|
|
||||||
if ( argc < 3 )
|
|
||||||
{
|
|
||||||
struct dnsconfig dnscfg;
|
|
||||||
if ( getdnsconfig(&dnscfg) < 0 )
|
|
||||||
err(1, "dnsconfig");
|
|
||||||
bool found = false;
|
|
||||||
for ( size_t i = 0; !found && i < dnscfg.servers_count; i++ )
|
|
||||||
{
|
|
||||||
if ( dnscfg.servers[i].family != AF_INET )
|
|
||||||
continue;
|
|
||||||
inet_ntop(AF_INET, &dnscfg.servers[i].addr, nsipstr, sizeof(nsipstr));
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
if ( !found )
|
|
||||||
errx(1, "No nameserver given and no default configured");
|
|
||||||
nameserver = nsipstr;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
nameserver = argv[2];
|
|
||||||
|
|
||||||
int port = 4 <= argc ? atoi(argv[3]) : 53;
|
|
||||||
if ( 5 <= argc )
|
|
||||||
errx(1, "Unexpected extra operand");
|
|
||||||
|
|
||||||
int fd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
||||||
if ( fd < 0 )
|
|
||||||
err(1, "socket");
|
|
||||||
|
|
||||||
struct sockaddr_in addr;
|
|
||||||
addr.sin_family = AF_INET;
|
|
||||||
addr.sin_port = htobe16(port);
|
|
||||||
if ( inet_pton(AF_INET, nameserver, &addr.sin_addr) < 1 )
|
|
||||||
errx(1, "invalid ip address: %s", nameserver);
|
|
||||||
|
|
||||||
if ( connect(fd, (const struct sockaddr*) &addr, sizeof(addr)) < 0 )
|
|
||||||
err(1, "connect");
|
|
||||||
|
|
||||||
unsigned char req[DNS_SIZE];
|
|
||||||
size_t req_size = 0;
|
|
||||||
struct dns_header hdr;
|
|
||||||
hdr.id = 0;
|
|
||||||
hdr.flags = DNS_HEADER_FLAGS_RD;
|
|
||||||
hdr.qdcount = 1;
|
|
||||||
hdr.ancount = 0;
|
|
||||||
hdr.nscount = 0;
|
|
||||||
hdr.arcount = 0;
|
|
||||||
req_size = encode_dns_header(req, req_size, &hdr);
|
|
||||||
struct dns_question qs;
|
|
||||||
qs.qtype = ipv == 4 ? DNS_TYPE_A : DNS_TYPE_AAAA;
|
|
||||||
qs.qclass = DNS_CLASS_IN;
|
|
||||||
req_size = encode_dns_question(req, req_size, host, &qs);
|
|
||||||
|
|
||||||
ssize_t amount = send(fd, req, req_size, 0);
|
|
||||||
if ( amount < 0 )
|
|
||||||
err(1, "send");
|
|
||||||
|
|
||||||
// TODO: Use recvfrom to get the server ip.
|
|
||||||
unsigned char resp[DNS_SIZE];
|
|
||||||
ssize_t resp_size = recv(fd, resp, sizeof(resp), 0);
|
|
||||||
if ( resp_size < 0 )
|
|
||||||
err(1, "recv");
|
|
||||||
|
|
||||||
// TODO: Verify the response came from the correct ip.
|
|
||||||
|
|
||||||
size_t offset = 0;
|
|
||||||
offset = decode_dns_header(resp, offset, resp_size, &hdr);
|
|
||||||
|
|
||||||
// TODO: Verify the response has the correct id.
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
printf("id = %u\n", hdr.id);
|
|
||||||
printf("flags = 0x%X\n", hdr.flags);
|
|
||||||
printf("qdcount = %u\n", hdr.qdcount);
|
|
||||||
printf("ancount = %u\n", hdr.ancount);
|
|
||||||
printf("nscount = %u\n", hdr.nscount);
|
|
||||||
printf("arcount = %u\n", hdr.arcount);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
uint16_t rcode = hdr.flags & DNS_HEADER_FLAGS_RCODE_MASK;
|
|
||||||
if ( rcode == DNS_HEADER_FLAGS_RCODE_FORMAT )
|
|
||||||
errx(1, "format error");
|
|
||||||
else if ( rcode == DNS_HEADER_FLAGS_RCODE_SERVER )
|
|
||||||
errx(1, "server error");
|
|
||||||
else if ( rcode == DNS_HEADER_FLAGS_RCODE_NAME )
|
|
||||||
errx(1, "no such name");
|
|
||||||
else if ( rcode == DNS_HEADER_FLAGS_RCODE_NOT_IMPLEMENTED )
|
|
||||||
errx(1, "not implemented error");
|
|
||||||
else if ( rcode == DNS_HEADER_FLAGS_RCODE_REFUSED )
|
|
||||||
errx(1, "refused");
|
|
||||||
else if ( rcode != DNS_HEADER_FLAGS_RCODE_NO )
|
|
||||||
errx(1, "unknown error (rcode=0x%X)", rcode);
|
|
||||||
|
|
||||||
if ( hdr.flags & DNS_HEADER_FLAGS_TC )
|
|
||||||
errx(1, "truncated");
|
|
||||||
|
|
||||||
// TODO: Check query bit.
|
|
||||||
|
|
||||||
for ( uint16_t i = 0; i < hdr.qdcount; i++ )
|
|
||||||
{
|
|
||||||
char name[DNS_NAME_MAX + 1];
|
|
||||||
offset = decode_dns_question(resp, offset, resp_size, name, &qs);
|
|
||||||
//printf("%s type=%u class=%u\n", name, qs.qtype, qs.qclass);
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( uint16_t i = 0; i < hdr.ancount; i++ )
|
|
||||||
{
|
|
||||||
char name[DNS_NAME_MAX + 1];
|
|
||||||
struct dns_record rr;
|
|
||||||
offset = decode_dns_record(resp, offset, resp_size, name, &rr);
|
|
||||||
uint32_t ttl = (uint32_t) rr.ttl_high << 16 | rr.ttl_low;
|
|
||||||
printf("%s type=%u class=%u ttl=%u ", name, rr.type, rr.class, ttl);
|
|
||||||
if ( rr.class == DNS_CLASS_IN && rr.type == DNS_TYPE_A )
|
|
||||||
{
|
|
||||||
unsigned char ip[4];
|
|
||||||
for ( size_t i = 0; i < 4; i++ )
|
|
||||||
offset = decode_dns_byte(resp, offset, resp_size, &ip[i]);
|
|
||||||
printf("%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
|
|
||||||
}
|
|
||||||
else if ( rr.class == DNS_CLASS_IN && rr.type == DNS_TYPE_AAAA )
|
|
||||||
{
|
|
||||||
unsigned char ip[16];
|
|
||||||
for ( size_t i = 0; i < 16; i++ )
|
|
||||||
offset = decode_dns_byte(resp, offset, resp_size, &ip[i]);
|
|
||||||
for ( size_t i = 0; i < 16; i++ )
|
|
||||||
{
|
|
||||||
if ( i && !(i & 1) )
|
|
||||||
putchar(':');
|
|
||||||
printf("%02x", ip[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ( rr.type == DNS_TYPE_CNAME )
|
|
||||||
{
|
|
||||||
char cname[DNS_NAME_MAX + 1];
|
|
||||||
offset = decode_dns_name(resp, offset, resp_size, cname);
|
|
||||||
printf("CNAME %s", cname);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf("0x");
|
|
||||||
fflush(stdout);
|
|
||||||
for ( size_t i = 0; i < rr.rdlength; i++ )
|
|
||||||
{
|
|
||||||
unsigned char b;
|
|
||||||
offset = decode_dns_byte(resp, offset, resp_size, &b);
|
|
||||||
if ( isprint(b) && b != '\'' )
|
|
||||||
printf("'%c'", b);
|
|
||||||
else
|
|
||||||
printf("%02X", b);
|
|
||||||
fflush(stdout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -34,6 +34,7 @@ tix-iso-add \
|
||||||
tix-iso-bootconfig \
|
tix-iso-bootconfig \
|
||||||
tix-iso-liveconfig \
|
tix-iso-liveconfig \
|
||||||
tix-port \
|
tix-port \
|
||||||
|
tix-repository \
|
||||||
tix-upgrade \
|
tix-upgrade \
|
||||||
|
|
||||||
MANPAGES8=\
|
MANPAGES8=\
|
||||||
|
|
|
@ -811,6 +811,9 @@ static void TixInfo(struct metainfo* minfo)
|
||||||
// TODO: Shell escape the values if needed.
|
// TODO: Shell escape the values if needed.
|
||||||
fwrite_variable(tixinfo_fp, "TIX_VERSION", "3");
|
fwrite_variable(tixinfo_fp, "TIX_VERSION", "3");
|
||||||
fwrite_variable(tixinfo_fp, "NAME", minfo->package_name);
|
fwrite_variable(tixinfo_fp, "NAME", minfo->package_name);
|
||||||
|
const char* edition = metainfo_get(minfo, "EDITION", "pkg.edition");
|
||||||
|
if ( edition )
|
||||||
|
fwrite_variable(tixinfo_fp, "EDITION", edition);
|
||||||
const char* version = metainfo_get(minfo, "VERSION", "VERSION");
|
const char* version = metainfo_get(minfo, "VERSION", "VERSION");
|
||||||
if ( version )
|
if ( version )
|
||||||
fwrite_variable(tixinfo_fp, "VERSION", version);
|
fwrite_variable(tixinfo_fp, "VERSION", version);
|
||||||
|
@ -829,6 +832,9 @@ static void TixInfo(struct metainfo* minfo)
|
||||||
else
|
else
|
||||||
fwrite_variable(tixinfo_fp, "PREFIX", minfo->prefix);
|
fwrite_variable(tixinfo_fp, "PREFIX", minfo->prefix);
|
||||||
}
|
}
|
||||||
|
const char* renames = metainfo_get(minfo, "RENAMES", "pkg.renames");
|
||||||
|
if ( renames )
|
||||||
|
fwrite_variable(tixinfo_fp, "RENAMES", renames);
|
||||||
}
|
}
|
||||||
// TODO: After releasing Sortix 1.1, remove generation 2 compatibility.
|
// TODO: After releasing Sortix 1.1, remove generation 2 compatibility.
|
||||||
else
|
else
|
||||||
|
|
|
@ -37,6 +37,7 @@ output_sha256sum=
|
||||||
patch=false
|
patch=false
|
||||||
package=false
|
package=false
|
||||||
release=false
|
release=false
|
||||||
|
repository_metadata=false
|
||||||
sha256=false
|
sha256=false
|
||||||
sha256sum=false
|
sha256sum=false
|
||||||
source=false
|
source=false
|
||||||
|
@ -115,6 +116,7 @@ for argument do
|
||||||
--output-upgrade-file) previous_option=output_upgrade_file ;;
|
--output-upgrade-file) previous_option=output_upgrade_file ;;
|
||||||
--package) package=true ;;
|
--package) package=true ;;
|
||||||
--patch) patch=true ;;
|
--patch) patch=true ;;
|
||||||
|
--repository-metadata) repository_metadata=true ;;
|
||||||
--sha256) sha256=true ;;
|
--sha256) sha256=true ;;
|
||||||
--sha256sum) sha256sum=true ;;
|
--sha256sum) sha256sum=true ;;
|
||||||
--source-full) source_full=true ;;
|
--source-full) source_full=true ;;
|
||||||
|
@ -426,6 +428,8 @@ for REQUEST; do
|
||||||
if $package; then
|
if $package; then
|
||||||
REQUEST="$REQUEST.tix.tar.xz"
|
REQUEST="$REQUEST.tix.tar.xz"
|
||||||
REQUESTDIR="repository/$MACHINE-sortix/"
|
REQUESTDIR="repository/$MACHINE-sortix/"
|
||||||
|
elif $repository_metadata; then
|
||||||
|
REQUESTDIR="repository/$MACHINE-sortix/"
|
||||||
elif $boot; then
|
elif $boot; then
|
||||||
REQUEST="$REQUEST"
|
REQUEST="$REQUEST"
|
||||||
REQUESTDIR="$MACHINE/boot/"
|
REQUESTDIR="$MACHINE/boot/"
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# Copyright (c) 2023 Jonas 'Sortie' Termansen.
|
||||||
|
#
|
||||||
|
# Permission to use, copy, modify, and distribute this software for any
|
||||||
|
# purpose with or without fee is hereby granted, provided that the above
|
||||||
|
# copyright notice and this permission notice appear in all copies.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
#
|
||||||
|
# tix-repository
|
||||||
|
# Generate repository metadata.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
generation=3
|
||||||
|
|
||||||
|
unset repository
|
||||||
|
|
||||||
|
operand=1
|
||||||
|
dashdash=
|
||||||
|
previous_option=
|
||||||
|
for argument do
|
||||||
|
if [ -n "$previous_option" ]; then
|
||||||
|
eval $previous_option=\$argument
|
||||||
|
previous_option=
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
case $argument in
|
||||||
|
*=?*) parameter=$(expr "X$argument" : '[^=]*=\(.*\)' || true) ;;
|
||||||
|
*=) parameter= ;;
|
||||||
|
*) parameter=yes ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
case $dashdash$argument in
|
||||||
|
--) dashdash=yes ;;
|
||||||
|
--generation=*) generation=$parameter ;;
|
||||||
|
--generation) previous_option=generation ;;
|
||||||
|
-*) echo "$0: unrecognized option $argument" >&2
|
||||||
|
exit 1 ;;
|
||||||
|
*)
|
||||||
|
if [ $operand = 1 ]; then
|
||||||
|
repository="$argument"
|
||||||
|
operand=2
|
||||||
|
else
|
||||||
|
echo "$0: unexpected extra operand $argument" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -n "$previous_option" ]; then
|
||||||
|
echo "$0: option '$argument' requires an argument" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$repository" ]; then
|
||||||
|
echo "$0: error: No repository was specified" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$generation" != 3 ]; then
|
||||||
|
echo "$0: error: --generation=$generation is not supported by this version" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$repository"
|
||||||
|
|
||||||
|
ls |
|
||||||
|
grep -E '\.tix.tar.xz$' |
|
||||||
|
grep -Eo '^[^.]*' |
|
||||||
|
LC_ALL=C sort -o packages.list
|
||||||
|
|
||||||
|
true > renames.list
|
||||||
|
true > manifest.list
|
||||||
|
|
||||||
|
for package in $(cat packages.list); do
|
||||||
|
tar -xOf "$package.tix.tar.xz" "tix/tixinfo/$package" > "$package.info"
|
||||||
|
tar -xOf "$package.tix.tar.xz" "tix/manifest/$package" > "$package.manifest"
|
||||||
|
sha256sum "$package.tix.tar.xz" > "$package.tix.tar.xz.sha256sum"
|
||||||
|
tix-vars -d '' "$package.info" RENAMES | tr , '\n' | sed -E '/^$/d' >> renames.list
|
||||||
|
sed -E "s/$/:$package/" "$package.manifest" >> manifest.list
|
||||||
|
done
|
||||||
|
|
||||||
|
LC_ALL=C sort -t: -k1,1 manifest.list -o manifest.list
|
||||||
|
|
||||||
|
find -type f '!' -name 'sha256sum' '!' -name '*.sha256sum' -exec sha256sum '{}' '+' |
|
||||||
|
sed -E 's, \./, ,' |
|
||||||
|
LC_ALL=C sort -k 2 > sha256sum
|
|
@ -90,6 +90,10 @@ conf() {
|
||||||
tix-vars -d "$2" - "$4"
|
tix-vars -d "$2" - "$4"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
escape_extended_regex_sed() {
|
||||||
|
printf "%s\n" "$1" | sed -E -e 's/[[$()*?\+.^{|}'"$2"']/\\\0/g'
|
||||||
|
}
|
||||||
|
|
||||||
collection=$(cd "$collection" && pwd)
|
collection=$(cd "$collection" && pwd)
|
||||||
|
|
||||||
if ! $upgrade_ports && ! $upgrade_system; then
|
if ! $upgrade_ports && ! $upgrade_system; then
|
||||||
|
@ -183,27 +187,55 @@ else
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Fetch each binary package from the mirror.
|
# TODO: Tracking sets like minimal/basic/full with new mandatory or recommended ports.
|
||||||
# TODO: Handle new mandatory / recommended ports.
|
|
||||||
# TODO: Handle renamed ports.
|
|
||||||
# TODO: Tracking sets like minimal/basic/full.
|
|
||||||
mkdir -p -- "$cachedir/repository"
|
mkdir -p -- "$cachedir/repository"
|
||||||
|
|
||||||
|
tix-fetch $fetch_options \
|
||||||
|
--collection="$collection" \
|
||||||
|
--input-release-file="$cachedir/release.sh" \
|
||||||
|
--input-sha256sum="$cachedir/sha256sum" \
|
||||||
|
--repository-metadata -O "$cachedir/repository" renames.list
|
||||||
|
|
||||||
|
# Follow RENAMES recursively to handle renames, splits, and deletions.
|
||||||
|
rename() {
|
||||||
|
# TODO: Need to escape at least +.
|
||||||
|
if grep -Eq "^$(escape_extended_regex_sed "$1"):" "$cachedir/repository/renames.list"; then
|
||||||
|
for new in $(grep -E "^$1:" "$cachedir/repository/renames.list" |
|
||||||
|
grep -Eo '[^:]*$'); do
|
||||||
|
rename "$new"
|
||||||
|
done
|
||||||
|
else
|
||||||
|
echo "$1" | grep -Eo '^[^@]*'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Determine the final package list after the renames.
|
||||||
packages=""
|
packages=""
|
||||||
for package in $(LC_ALL=C ls -- "$collection/tix/tixinfo"); do
|
for package in $(LC_ALL=C ls -- "$collection/tix/tixinfo"); do
|
||||||
# The package exists upstream if it has a hash.
|
edition=$(tix-vars -d 1 "$collection/tix/tixinfo/$package" EDITION)
|
||||||
sha256=$(tix-fetch $fetch_options \
|
packages="$packages $(rename "$package@$edition")"
|
||||||
--collection="$collection" \
|
done
|
||||||
--input-release-file="$cachedir/release.sh" \
|
|
||||||
--input-sha256sum="$cachedir/sha256sum" \
|
# Sort and deduplicate the package list and check for existence.
|
||||||
--sha256 --package -- $package)
|
packages=$(for package in $packages; do
|
||||||
if [ -n "$sha256" ]; then
|
# The package exists upstream if it has a hash.
|
||||||
tix-fetch $fetch_options \
|
if [ -n "$(tix-fetch $fetch_options \
|
||||||
--collection="$collection" \
|
--collection="$collection" \
|
||||||
--input-release-file="$cachedir/release.sh" \
|
--input-release-file="$cachedir/release.sh" \
|
||||||
--input-sha256sum="$cachedir/sha256sum" \
|
--input-sha256sum="$cachedir/sha256sum" \
|
||||||
-c --package -O "$cachedir/repository" -- $package
|
--sha256 --package -- $package)" ]; then
|
||||||
packages="$packages $package"
|
echo $package
|
||||||
fi
|
fi
|
||||||
|
done | LC_ALL=C sort -u)
|
||||||
|
|
||||||
|
# Fetch each binary package from the mirror.
|
||||||
|
for package in $packages; do
|
||||||
|
tix-fetch $fetch_options \
|
||||||
|
--collection="$collection" \
|
||||||
|
--input-release-file="$cachedir/release.sh" \
|
||||||
|
--input-sha256sum="$cachedir/sha256sum" \
|
||||||
|
-c --package -O "$cachedir/repository" -- $package
|
||||||
done
|
done
|
||||||
|
|
||||||
# Stop if only downloading.
|
# Stop if only downloading.
|
||||||
|
|
Loading…
Reference in New Issue