diff options
Diffstat (limited to 'contrib/idn/mdnkit/lib/msgtrans.c')
-rw-r--r-- | contrib/idn/mdnkit/lib/msgtrans.c | 633 |
1 files changed, 633 insertions, 0 deletions
diff --git a/contrib/idn/mdnkit/lib/msgtrans.c b/contrib/idn/mdnkit/lib/msgtrans.c new file mode 100644 index 00000000..0b10a71d --- /dev/null +++ b/contrib/idn/mdnkit/lib/msgtrans.c @@ -0,0 +1,633 @@ +#ifndef lint +static char *rcsid = "$Id: msgtrans.c,v 1.1 2000/12/07 00:52:25 tale Exp $"; +#endif + +/* + * Copyright (c) 2000 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> +#include <arpa/nameser.h> +#include <resolv.h> +#endif + +#include <mdn/result.h> +#include <mdn/assert.h> +#include <mdn/logmacro.h> +#include <mdn/converter.h> +#include <mdn/normalizer.h> +#include <mdn/translator.h> +#include <mdn/zldrule.h> +#include <mdn/msgheader.h> +#include <mdn/msgtrans.h> +#include <mdn/dn.h> +#include <mdn/debug.h> + +#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 { + 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 */ + int determined; /* if ZLD/codeset are determined */ + mdn_msgtrans_param_t *param; /* 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 translate_name(mdn_msgtrans_param_t *param, + char *from, char *to, size_t tolen); +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_msgtrans_param_t *param, + 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_msgtrans_param_t *param, + 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; + + assert(param != 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, param, 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. + */ + for (i = 0; i < header.qdcount; i++) { + if ((r = translate_question(&ctx)) != mdn_success) + return (r); + } + + /* + * Translate other sections. + */ + for (i = 0; + i < header.ancount + header.nscount + header.arcount; + 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; + mdn_msgtrans_param_t *param; + char qname[DNAME_SIZE], qname_translated[DNAME_SIZE]; + + param = ctx->param; + + /* Get QNAME. */ + if ((r = get_domainname(ctx, qname, sizeof(qname))) != mdn_success) + return (r); + + if (!ctx->determined) { + /* + * Determine ZLD and character set/encoding. + */ + r = mdn_zldrule_select(param->local_rule, qname, + ¶m->local_zld, + ¶m->local_converter); + switch (r) { + case mdn_success: + ctx->determined = 1; + break; + case mdn_notfound: + /* + * No matching ZLD, no default. + */ + param->local_zld = NULL; + param->local_converter = NULL; + break; + default: + return (r); + } + } + + /* Translate QNAME. */ + r = translate_name(param, 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 = translate_name(ctx->param, 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 = translate_name(ctx->param, 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 +translate_name(mdn_msgtrans_param_t *param, + char *from, char *to, size_t tolen) +{ + if (param->local_converter == NULL) { + /* + * No translation is required. + */ + size_t fromlen = strlen(from) + 1; + if (fromlen > tolen) + return (mdn_buffer_overflow); + (void)memcpy(to, from, fromlen); + return (mdn_success); + } else { + return (mdn_translator_translate(param->local_converter, + param->local_alt_converter, + param->local_zld, + param->normalizer, + param->target_converter, + param->target_alt_converter, + param->target_zld, + from, to, tolen)); + } +} + +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_msgtrans_param_t *param, + const char *msg, size_t msglen, char *outbuf, size_t outbufsize) +{ + ctx->in = ctx->in_ptr = msg; + ctx->in_len = ctx->in_remain = msglen; + ctx->out = ctx->out_ptr = outbuf; + ctx->out_remain = outbufsize; + ctx->determined = !param->use_local_rule; + ctx->param = param; + mdn__dn_initcompress(&ctx->dn_ctx, outbuf); +} + +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; + } +} |