Files
bind9/contrib/idn/mdnkit/lib/msgtrans.c
2001-06-09 00:30:55 +00:00

606 lines
16 KiB
C

#ifndef lint
static char *rcsid = "$Id: msgtrans.c,v 1.24 2001/02/21 05:54:17 m-kasahr Exp $";
#endif
/*
* Copyright (c) 2000,2001 Japan Network Information Center.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set forth bellow.
*
* LICENSE TERMS AND CONDITIONS
*
* The following License Terms and Conditions apply, unless a different
* license is obtained from Japan Network Information Center ("JPNIC"),
* a Japanese association, Fuundo Bldg., 1-2 Kanda Ogawamachi, Chiyoda-ku,
* Tokyo, Japan.
*
* 1. Use, Modification and Redistribution (including distribution of any
* modified or derived work) in source and/or binary forms is permitted
* under this License Terms and Conditions.
*
* 2. Redistribution of source code must retain the copyright notices as they
* appear in each source code file, this License Terms and Conditions.
*
* 3. Redistribution in binary form must reproduce the Copyright Notice,
* this License Terms and Conditions, in the documentation and/or other
* materials provided with the distribution. For the purposes of binary
* distribution the "Copyright Notice" refers to the following language:
* "Copyright (c) Japan Network Information Center. All rights reserved."
*
* 4. Neither the name of JPNIC may be used to endorse or promote products
* derived from this Software without specific prior written approval of
* JPNIC.
*
* 5. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY JPNIC
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JPNIC BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* 6. Indemnification by Licensee
* Any person or entities using and/or redistributing this Software under
* this License Terms and Conditions shall defend indemnify and hold
* harmless JPNIC from and against any and all judgements damages,
* expenses, settlement liabilities, cost and other liabilities of any
* kind as a result of use and redistribution of this Software or any
* claim, suite, action, litigation or proceeding by any third party
* arising out of or relates to this License Terms and Conditions.
*
* 7. Governing Law, Jurisdiction and Venue
* This License Terms and Conditions shall be governed by and and
* construed in accordance with the law of Japan. Any person or entities
* using and/or redistributing this Software under this License Terms and
* Conditions hereby agrees and consent to the personal and exclusive
* jurisdiction and venue of Tokyo District Court of Japan.
*/
#include <config.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#ifdef WIN32
#include <windows.h>
#include <winsock.h>
#else /* for normal systems */
#include <errno.h>
#include <sys/types.h>
#include <netinet/in.h>
#ifdef HAVE_ARPA_NAMESER_H
#include <arpa/nameser.h>
#endif
#ifdef HAVE_RESOLV_H
#include <resolv.h>
#endif
#endif
#include <mdn/result.h>
#include <mdn/assert.h>
#include <mdn/logmacro.h>
#include <mdn/converter.h>
#include <mdn/normalizer.h>
#include <mdn/res.h>
#include <mdn/msgheader.h>
#include <mdn/msgtrans.h>
#include <mdn/dn.h>
#include <mdn/debug.h>
/*
* Name translation instructions.
*
* For query, perform
* 1. local encoding to UTF-8 conversion
* 2. delimiter mapping
* 3. local mapping
* 4. nameprep
* 5. UTF-8 to IDN encoding conversion
*
* For reply,
* 1. IDN encoding to UTF-8 conversion
* 2. UTF-8 to local encoding conversion
*
* See mdn/res.h for the mnemonic.
*/
#define INSN_QUERY "ldMNI"
#define INSN_REPLY "iL"
#define DNS_HEADER_SIZE 12
#define DNAME_SIZE 512
#define RRFORMAT_HASH_SIZE 47
/*
* DNS opcodes.
*/
enum {
opcode_query = 0,
opcode_iquery = 1,
opcode_status = 2,
opcode_notify = 4,
opcode_update = 5
};
/*
* Resource record types.
*/
enum {
rrtype_A = 1,
rrtype_NS = 2,
rrtype_MD = 3,
rrtype_MF = 4,
rrtype_CNAME = 5,
rrtype_SOA = 6,
rrtype_MB = 7,
rrtype_MG = 8,
rrtype_MR = 9,
rrtype_NULL = 10,
rrtype_WKS = 11,
rrtype_PTR = 12,
rrtype_HINFO = 13,
rrtype_MINFO = 14,
rrtype_MX = 15,
rrtype_TXT = 16,
rrtype_RP = 17,
rrtype_AFSDB = 18,
rrtype_X25 = 19,
rrtype_ISDN = 20,
rrtype_RT = 21,
rrtype_AAAA = 28
};
/*
* Resource record classes.
*/
enum {
rrclass_IN = 1,
rrclass_CS = 2,
rrclass_CH = 3,
rrclass_ANY = 255
};
typedef struct msgtrans_ctx {
char *insn; /* name translation instruction */
const char *in; /* input message */
size_t in_len; /* length of it */
const char *in_ptr; /* current pointer */
size_t in_remain; /* # of remaining octets */
char *out; /* output (translated) message */
char *out_ptr; /* current pointer */
size_t out_remain; /* # of remaining (available) octets */
mdn__dn_t dn_ctx; /* for compression */
mdn_resconf_t conf; /* translation parameters */
} msgtrans_ctx_t;
static struct rrformat {
unsigned int type; /* RR type */
unsigned int class; /* RR class */
const char *format; /* RDATA format */
struct rrformat *next; /* hash chain */
} rrformats[] = {
{ rrtype_CNAME, rrclass_ANY, "D" },
{ rrtype_HINFO, rrclass_ANY, "TT" },
{ rrtype_MB, rrclass_ANY, "D" },
{ rrtype_MD, rrclass_ANY, "D" },
{ rrtype_MF, rrclass_ANY, "D" },
{ rrtype_MG, rrclass_ANY, "D" },
{ rrtype_MINFO, rrclass_ANY, "DD" },
{ rrtype_MR, rrclass_ANY, "D" },
{ rrtype_MX, rrclass_ANY, "SD" },
{ rrtype_NULL, rrclass_ANY, "R" },
{ rrtype_NS, rrclass_ANY, "D" },
{ rrtype_PTR, rrclass_ANY, "D" },
{ rrtype_SOA, rrclass_ANY, "DDLLLLL" },
{ rrtype_TXT, rrclass_ANY, "T" },
{ rrtype_A, rrclass_IN, "L" },
{ rrtype_WKS, rrclass_IN, "LCR" },
{ rrtype_RP, rrclass_ANY, "DD" },
{ rrtype_AFSDB, rrclass_ANY, "SD" },
{ rrtype_X25, rrclass_ANY, "T" },
{ rrtype_ISDN, rrclass_ANY, "TT" },
{ rrtype_RT, rrclass_ANY, "SD" },
{ rrtype_AAAA, rrclass_IN, "H" },
{ 0, 0, NULL },
};
static struct rrformat *rrformathash[RRFORMAT_HASH_SIZE];
static mdn_result_t copy_header(msgtrans_ctx_t *ctx);
static mdn_result_t translate_question(msgtrans_ctx_t *ctx);
static mdn_result_t translate_rr(msgtrans_ctx_t *ctx);
static mdn_result_t translate_rdata(msgtrans_ctx_t *ctx,
unsigned int rr_type,
unsigned int rr_class,
unsigned int rr_length);
static const char *rdata_format(unsigned int rr_type,
unsigned int rr_class);
static mdn_result_t translate_domain(msgtrans_ctx_t *ctx);
static mdn_result_t get_domainname(msgtrans_ctx_t *ctx, char *buf,
size_t bufsize);
static mdn_result_t put_domainname(msgtrans_ctx_t *ctx, char *name);
static void ctx_init(msgtrans_ctx_t *ctx,
mdn_resconf_t conf, mdn_msgheader_t *header,
const char *msg, size_t msglen,
char *outbuf, size_t outbufsize);
static mdn_result_t copy_rest(msgtrans_ctx_t *ctx);
static mdn_result_t copy_message(msgtrans_ctx_t *ctx, size_t len);
static size_t output_length(msgtrans_ctx_t *ctx);
static void dump_message(const char *title, const char *p,
size_t length);
mdn_result_t
mdn_msgtrans_translate(mdn_resconf_t conf,
const char *msg, size_t msglen,
char *outbuf, size_t outbufsize, size_t *outmsglenp)
{
mdn_result_t r;
msgtrans_ctx_t ctx;
mdn_msgheader_t header;
int i, n;
assert(conf != NULL && msg != NULL &&
outbuf != NULL && outbufsize > 0 && outmsglenp != NULL);
TRACE(("mdn_msgtrans_translate(msg=<%s>,msglen=%d)\n",
mdn_debug_hexdata(msg, msglen, 64), msglen));
if (LOGLEVEL >= mdn_log_level_dump)
dump_message("before translation", msg, msglen);
/*
* Check message length.
*/
if (msglen < DNS_HEADER_SIZE) {
INFO(("mdn_msgtrans_translate: incoming packet too short "
"(%d octets)\n", msglen));
return (mdn_invalid_message);
}
/*
* Parse message header.
*/
if ((r = mdn_msgheader_parse(msg, msglen, &header)) != mdn_success) {
WARNING(("mdn_msgtrans_translate: message header "
"parsing failed: %s\n",
mdn_result_tostring(r)));
return (r);
}
/*
* Create translation context.
*/
ctx_init(&ctx, conf, &header, msg, msglen, outbuf, outbufsize);
/*
* We handle only query, notify and update messages.
* Do not process others.
*/
switch (header.opcode) {
case opcode_query:
case opcode_notify:
case opcode_update:
break;
default:
INFO(("mdn_msgtrans_translate: pass through message "
"whose opcode is %d", header.opcode));
if ((r = copy_rest(&ctx)) == mdn_success)
*outmsglenp = output_length(&ctx);
return (mdn_success);
}
/*
* Copy header part verbatim.
*/
(void)copy_header(&ctx);
/*
* Parse question/zone section.
*/
n = header.qdcount;
for (i = 0; i < n; i++) {
if ((r = translate_question(&ctx)) != mdn_success)
return (r);
}
/*
* Translate other sections.
*/
n = header.ancount + header.nscount + header.arcount;
for (i = 0; i < n; i++) {
if ((r = translate_rr(&ctx)) != mdn_success)
return (r);
}
if (LOGLEVEL >= mdn_log_level_dump)
dump_message("after translation",
ctx.out, output_length(&ctx));
/*
* Is there anything left out?
*/
if (ctx.in_remain != 0) {
WARNING(("mdn_msgtrans_translate: garbage at the end "
"(%d octets)\n", ctx.in_remain));
/* don't consider this as an error. */
/* return (mdn_invalid_message); */
}
*outmsglenp = output_length(&ctx);
return (mdn_success);
}
static mdn_result_t
copy_header(msgtrans_ctx_t *ctx) {
return (copy_message(ctx, DNS_HEADER_SIZE));
}
static mdn_result_t
translate_question(msgtrans_ctx_t *ctx) {
mdn_result_t r;
char qname[DNAME_SIZE], qname_translated[DNAME_SIZE];
/* Get QNAME. */
if ((r = get_domainname(ctx, qname, sizeof(qname))) != mdn_success)
return (r);
/* Translate QNAME. */
r = mdn_res_nameconv(ctx->conf, ctx->insn, qname,
qname_translated, sizeof(qname_translated));
if (r != mdn_success)
return (r);
if ((r = put_domainname(ctx, qname_translated)) != mdn_success)
return (r);
/* Copy QTYPE and QCLASS */
return (copy_message(ctx, 4));
}
static mdn_result_t
translate_rr(msgtrans_ctx_t *ctx) {
mdn_result_t r;
unsigned char *p;
unsigned int rr_type, rr_class, rr_length;
char dname[DNAME_SIZE], dname_translated[DNAME_SIZE];
size_t length_before;
/* Get NAME. */
if ((r = get_domainname(ctx, dname, sizeof(dname))) != mdn_success)
return (r);
/* Translate NAME. */
r = mdn_res_nameconv(ctx->conf, ctx->insn, dname,
dname_translated, sizeof(dname_translated));
if (r != mdn_success)
return (r);
if ((r = put_domainname(ctx, dname_translated)) != mdn_success)
return (r);
/* Get TYPE and CLASS */
if (ctx->in_remain < 10)
return (mdn_invalid_message);
p = (unsigned char *)ctx->in_ptr;
#define GET16(off) ((p[off]<<8)+p[(off)+1])
rr_type = GET16(0);
rr_class = GET16(2);
rr_length = GET16(8);
#undef GET16
/* Copy TYPE, CLASS, TTL and RDLENGTH. */
if ((r = copy_message(ctx, 10)) != mdn_success)
return (r);
/* Remember the current output length. */
length_before = output_length(ctx);
/* Translate RDATA. */
r = translate_rdata(ctx, rr_type, rr_class, rr_length);
if (r == mdn_success) {
/* Reset RDLENGTH */
rr_length = output_length(ctx) - length_before;
ctx->out[length_before - 2] = (rr_length >> 8) & 0xff;
ctx->out[length_before - 1] = rr_length & 0xff;
}
return (r);
}
static mdn_result_t
translate_rdata(msgtrans_ctx_t *ctx, unsigned int rr_type,
unsigned int rr_class, unsigned int rr_length)
{
const char *format;
int c;
if ((format = rdata_format(rr_type, rr_class)) == NULL) {
INFO(("mdn_msgtrans: unknown resource record type %d "
"pass through\n", rr_type));
return (copy_message(ctx, rr_length));
}
while ((c = *format++) != '\0') {
int copy_len;
mdn_result_t r;
switch (c) {
case 'D': /* domain name */
{
int remain_org = ctx->in_remain;
if ((r = translate_domain(ctx)) != mdn_success)
return (r);
rr_length -= remain_org - ctx->in_remain;
continue;
}
case 'T': /* character string */
copy_len = *((unsigned char *)ctx->in_ptr) + 1;
break;
case 'C': /* 1-octet value */
copy_len = 1;
break;
case 'S': /* 2-octet value */
copy_len = 2;
break;
case 'L': /* 4-octet value */
copy_len = 4;
break;
case 'H': /* 16-octet value (AAAA) */
copy_len = 16;
break;
case 'R': /* the rest */
copy_len = rr_length;
break;
default:
copy_len = 0; /* for gcc -Wall */
FATAL(("mdn_msgtrans: internal error -- "
"unknown format character %c", c));
/* NOTREACHED */
break;
}
if ((r = copy_message(ctx, copy_len)) != mdn_success)
return (r);
rr_length -= copy_len;
}
return (mdn_success);
}
static const char *
rdata_format(unsigned int rr_type, unsigned int rr_class) {
static int initialized;
struct rrformat *rp;
int h;
if (!initialized) {
/*
* Build hash table.
*/
for (rp = rrformats; rp->format != NULL; rp++) {
h = rp->type % RRFORMAT_HASH_SIZE;
rp->next = rrformathash[h];
rrformathash[h] = rp;
}
initialized = 1;
}
/*
* Find the element with the specified type and class.
*/
h = rr_type % RRFORMAT_HASH_SIZE;
for (rp = rrformathash[h]; rp != NULL; rp = rp->next) {
if (rp->type == rr_type &&
(rp->class == rr_class || rp->class == rrclass_ANY))
return (rp->format);
}
return (NULL);
}
static mdn_result_t
translate_domain(msgtrans_ctx_t *ctx) {
mdn_result_t r;
char dname[DNAME_SIZE], dname_translated[DNAME_SIZE];
/* Get NAME. */
if ((r = get_domainname(ctx, dname, sizeof(dname))) != mdn_success)
return (r);
/* Translate NAME. */
r = mdn_res_nameconv(ctx->conf, ctx->insn, dname,
dname_translated, sizeof(dname_translated));
if (r != mdn_success)
return (r);
if ((r = put_domainname(ctx, dname_translated)) != mdn_success)
return (r);
return (mdn_success);
}
static mdn_result_t
get_domainname(msgtrans_ctx_t *ctx, char *buf, size_t bufsize) {
mdn_result_t r;
size_t n;
r = mdn__dn_expand(ctx->in, ctx->in_len, ctx->in_ptr,
buf, bufsize, &n);
if (r == mdn_success) {
ctx->in_ptr += n;
ctx->in_remain -= n;
}
return (r);
}
static mdn_result_t
put_domainname(msgtrans_ctx_t *ctx, char *name) {
mdn_result_t r;
size_t n;
r = mdn__dn_compress(name, ctx->out_ptr, ctx->out_remain,
&ctx->dn_ctx, &n);
if (r == mdn_success) {
ctx->out_ptr += n;
ctx->out_remain -= n;
}
return (r);
}
static void
ctx_init(msgtrans_ctx_t *ctx, mdn_resconf_t conf, mdn_msgheader_t *header,
const char *msg, size_t msglen, char *outbuf, size_t outbufsize)
{
ctx->insn = (header->qr == 0) ? INSN_QUERY : INSN_REPLY;
ctx->in = ctx->in_ptr = msg;
ctx->in_len = ctx->in_remain = msglen;
ctx->out = ctx->out_ptr = outbuf;
ctx->out_remain = outbufsize;
mdn__dn_initcompress(&ctx->dn_ctx, outbuf);
ctx->conf = conf;
}
static mdn_result_t
copy_rest(msgtrans_ctx_t *ctx) {
return (copy_message(ctx, ctx->in_remain));
}
static mdn_result_t
copy_message(msgtrans_ctx_t *ctx, size_t len) {
assert(ctx != NULL);
if (ctx->in_remain < len)
return (mdn_invalid_message);
if (ctx->out_remain < len)
return (mdn_buffer_overflow);
(void)memcpy(ctx->out_ptr, ctx->in_ptr, len);
ctx->in_ptr += len;
ctx->in_remain -= len;
ctx->out_ptr += len;
ctx->out_remain -= len;
return (mdn_success);
}
static size_t
output_length(msgtrans_ctx_t *ctx) {
return (ctx->out_ptr - ctx->out);
}
static void
dump_message(const char *title, const char *p, size_t length) {
DUMP(("message (%s): length %d\n", title, length));
while (length > 0) {
int len = length < 16 ? length : 16;
DUMP((" %s\n", mdn_debug_hexdata(p, len, 16)));
p += len;
length -= len;
}
}