#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 #include #include #include #ifdef WIN32 #include #include #else /* for normal systems */ #include #include #include #ifdef HAVE_ARPA_NAMESER_H #include #endif #ifdef HAVE_RESOLV_H #include #endif #endif #include #include #include #include #include #include #include #include #include #include /* * 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; } }