628 lines
17 KiB
C
628 lines
17 KiB
C
/*
|
|
* message.c - mDNS Proxy, message handling
|
|
*
|
|
* message will passed with callback 'notify_message'.
|
|
* this module parse received message and forward request,
|
|
* or reply to originator
|
|
*/
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
#ifndef lint
|
|
static char *rcsid = "$Id: message.c,v 1.1 2001/06/09 00:30:35 tale Exp $";
|
|
#endif
|
|
|
|
#include <config.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#ifdef WIN32
|
|
#include <windows.h>
|
|
#include <winsock.h>
|
|
#else /* for normal systems */
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#endif
|
|
|
|
#include "mdnsproxy.h"
|
|
|
|
/*
|
|
* address handling utilities
|
|
*
|
|
* addrEq check same addresses
|
|
* addrFmt format address, port & protocol
|
|
*
|
|
* these functions are same with those in 'server.c'.
|
|
* may be put in another 'utility' module.
|
|
*/
|
|
|
|
static BOOL addrEq(struct sockaddr *a1, struct sockaddr *a2)
|
|
{
|
|
struct sockaddr_in *ip1 = (struct sockaddr_in *) a1 ;
|
|
struct sockaddr_in *ip2 = (struct sockaddr_in *) a2 ;
|
|
|
|
if (ip1->sin_addr.s_addr != ip2->sin_addr.s_addr) {
|
|
return FALSE ;
|
|
}
|
|
if (ip1->sin_port != ip2->sin_port) {
|
|
return FALSE ;
|
|
}
|
|
return TRUE ;
|
|
}
|
|
|
|
static u_char fmtbuff[64] ;
|
|
|
|
static u_char *addrFmt(struct sockaddr *addr, int proto)
|
|
{
|
|
struct sockaddr_in *iaddr = (struct sockaddr_in *) addr ;
|
|
u_char *ap ;
|
|
u_char *pp ;
|
|
|
|
ap = (u_char *) &iaddr->sin_addr ;
|
|
pp = (u_char *) &iaddr->sin_port ;
|
|
|
|
sprintf(fmtbuff, "%s:%d.%d.%d.%d:%d",
|
|
(proto == SOCK_STREAM ? "TCP" : "UDP"),
|
|
(ap[0] & 0xff), (ap[1] & 0xff), (ap[2] & 0xff), (ap[3] & 0xff),
|
|
((pp[0] & 0xff) * 256 + (pp[1] & 0xff)) ) ;
|
|
|
|
return fmtbuff ;
|
|
}
|
|
|
|
/*
|
|
* Managing Message ID
|
|
*/
|
|
|
|
static u_short msgidLast = 0xffff ;
|
|
static u_short msgidMap[4096] = { 0 } ;
|
|
|
|
#define ID_INDEX(x) (((x) & 0xfff0) >> 4)
|
|
#define ID_MASK(x) (1 << ((x) & 0x000f))
|
|
|
|
#define ID_CHECK(x) (msgidMap[ID_INDEX((x))] & ID_MASK((x)))
|
|
#define ID_USEIT(x) (msgidMap[ID_INDEX((x))] |= ID_MASK((x)))
|
|
#define ID_CLEAR(x) (msgidMap[ID_INDEX((x))] &= ~ID_MASK((x)))
|
|
|
|
static BOOL idAlloc(u_short *id)
|
|
{
|
|
u_short newid ;
|
|
|
|
for (newid = (msgidLast + 1) & 0xffff ;
|
|
newid != msgidLast ;
|
|
newid = (newid + 1) & 0xffff) {
|
|
if (ID_CHECK(newid) == 0) {
|
|
ID_USEIT(newid) ;
|
|
msgidLast = newid ;
|
|
*id = newid ;
|
|
return TRUE ;
|
|
}
|
|
}
|
|
WARN("idAlloc - no more ID\n") ;
|
|
return FALSE ;
|
|
}
|
|
|
|
static void idFree(u_short id)
|
|
{
|
|
if (ID_CHECK(id) == 0) {
|
|
WARN("idAlloc - %04x is not in use\n", id) ;
|
|
return ;
|
|
}
|
|
ID_CLEAR(id) ;
|
|
}
|
|
|
|
/*
|
|
* Message Managements
|
|
*
|
|
* Request from client is identified with its ID word. It is unique
|
|
* on one client, but proxy accepts requests from multiple clients,
|
|
* proxy cannot distinguish request with ID only, and cannot forward
|
|
* request with such ID word.
|
|
*
|
|
* So, proxy will identify requests with combination of address, port,
|
|
* (which identified client) and ID word. Then forwarding request,
|
|
* proxy allocate unique ID, and change request's ID with new one.
|
|
*
|
|
* Response from DNS server will identified with newly allocated ID.
|
|
* For responding such response to originator, replace response's
|
|
* ID with original one, and send response to marked address/port.
|
|
*/
|
|
|
|
typedef struct _MSG *MSGPTR ;
|
|
|
|
typedef struct _MSG {
|
|
MSGPTR prev ;
|
|
MSGPTR next ;
|
|
time_t last ;
|
|
struct sockaddr from ;
|
|
int proto ;
|
|
u_short orgId ;
|
|
u_short newId ;
|
|
translation_context_t trctx ;
|
|
} MSGREC ;
|
|
|
|
static MSGREC listMsg = { 0 } ;
|
|
|
|
/*
|
|
* searchReq - search request in message list, search on original ID
|
|
*/
|
|
|
|
static MSGPTR searchReq(u_short id, int proto, struct sockaddr *addr)
|
|
{
|
|
MSGPTR p ;
|
|
|
|
if (listMsg.prev == NULL || listMsg.next == NULL) {
|
|
listMsg.prev = &listMsg ;
|
|
listMsg.next = &listMsg ;
|
|
}
|
|
for (p = listMsg.next ; p != &listMsg ; p = p->next) {
|
|
if (p->orgId != id || p->proto != proto) {
|
|
continue ;
|
|
}
|
|
if (addrEq(&p->from, addr) != TRUE) {
|
|
continue ;
|
|
}
|
|
p->last = time(NULL) ;
|
|
return p ;
|
|
}
|
|
return NULL ;
|
|
}
|
|
|
|
/*
|
|
* searchOrg - search original request matching to new ID
|
|
*/
|
|
|
|
static MSGPTR searchOrg(u_short id, int proto)
|
|
{
|
|
MSGPTR p ;
|
|
|
|
if (listMsg.prev == NULL || listMsg.next == NULL) {
|
|
listMsg.prev = &listMsg ;
|
|
listMsg.next = &listMsg ;
|
|
}
|
|
for (p = listMsg.next ; p != &listMsg ; p = p->next) {
|
|
if (p->newId != id || p->proto != proto) {
|
|
continue ;
|
|
}
|
|
p->last = time(NULL) ;
|
|
return p ;
|
|
}
|
|
return NULL ;
|
|
}
|
|
|
|
/*
|
|
* createReq - create new message record for new request
|
|
*
|
|
* it also allocate new ID for this request, used for
|
|
* forwarding this request
|
|
*/
|
|
|
|
static MSGPTR createReq(u_short id, int proto, struct sockaddr *addr)
|
|
{
|
|
u_short newid ;
|
|
MSGPTR pMsg, prev, next ;
|
|
|
|
if (listMsg.prev == NULL || listMsg.next == NULL) {
|
|
listMsg.prev = &listMsg ;
|
|
listMsg.next = &listMsg ;
|
|
}
|
|
|
|
if (idAlloc(&newid) != TRUE) {
|
|
WARN("createReq - no more ID\n") ;
|
|
return NULL ;
|
|
}
|
|
if ((pMsg = (MSGPTR) malloc(sizeof(MSGREC))) == NULL) {
|
|
WARN("createReq - cannot allocate message record\n") ;
|
|
idFree(newid) ;
|
|
return NULL ;
|
|
}
|
|
|
|
memset(pMsg, 0, sizeof(MSGREC)) ;
|
|
|
|
memcpy(&pMsg->from, addr, sizeof(struct sockaddr)) ;
|
|
pMsg->proto = proto ;
|
|
pMsg->orgId = id ;
|
|
pMsg->newId = newid ;
|
|
pMsg->last = time(NULL) ;
|
|
|
|
pMsg->trctx.client = &pMsg->from ;
|
|
pMsg->trctx.protocol = pMsg->proto ;
|
|
pMsg->trctx.old_id = pMsg->orgId ;
|
|
pMsg->trctx.new_id = pMsg->newId ;
|
|
|
|
prev = listMsg.prev ;
|
|
next = prev->next ;
|
|
|
|
prev->next = pMsg ;
|
|
next->prev = pMsg ;
|
|
pMsg->prev = prev ;
|
|
pMsg->next = next ;
|
|
|
|
return pMsg ;
|
|
}
|
|
|
|
/*
|
|
* disposeReq - dispose message record
|
|
*/
|
|
|
|
static void disposeReq(MSGPTR pMsg)
|
|
{
|
|
MSGPTR p ;
|
|
|
|
if (listMsg.prev == NULL || listMsg.next == NULL) {
|
|
listMsg.prev = &listMsg ;
|
|
listMsg.next = &listMsg ;
|
|
}
|
|
|
|
for (p = listMsg.next ; p != &listMsg ; p = p->next) {
|
|
if (p == pMsg) {
|
|
break ;
|
|
}
|
|
}
|
|
if (p == pMsg) { /* safe to unlink it */
|
|
pMsg->prev->next = pMsg->next ;
|
|
pMsg->next->prev = pMsg->prev ;
|
|
}
|
|
idFree(pMsg->newId) ;
|
|
free(pMsg) ;
|
|
}
|
|
|
|
/*
|
|
* messageForward - forward the request
|
|
*/
|
|
|
|
static void errorOnRequest(MSGPTR pMsg, u_char *msg, int len, size_t err)
|
|
{
|
|
u_short errmsg[6] ;
|
|
u_short flags ;
|
|
u_short *ps ;
|
|
|
|
TRACE("errorOnRequest %d\n", err) ;
|
|
|
|
ps = (u_short *) msg ;
|
|
flags = ntohs(ps[1]) ;
|
|
flags = ((flags & 0x7fff) | 0x8000) ; /* QR to response */
|
|
flags = ((flags & 0xfff8) | (err & 0x0007)) ; /* set RCODE */
|
|
|
|
memset(errmsg, 0, sizeof(errmsg)) ;
|
|
errmsg[0] = htons(pMsg->orgId) ;
|
|
errmsg[1] = htons(flags) ;
|
|
|
|
server_response(&pMsg->from, pMsg->proto, (u_char *) errmsg, sizeof(errmsg)) ;
|
|
}
|
|
|
|
static void messageForward(MSGPTR pMsg, u_char *msg, int len)
|
|
{
|
|
u_short *p ;
|
|
u_char buff[1024] ;
|
|
u_char *bbase ;
|
|
size_t bleng ;
|
|
size_t cleng = 0 ; /* avoid un-expected length on xlate error */
|
|
size_t cstat = 0 ; /* avoid un-expected status on xlate error */
|
|
|
|
TRACE("messageForward - %04x -> %04x\n", pMsg->orgId, pMsg->newId) ;
|
|
|
|
/*
|
|
* prepare conversion buffer
|
|
*/
|
|
|
|
if (len < sizeof(buff) / 2) {
|
|
bbase = buff ;
|
|
bleng = sizeof(buff) ;
|
|
} else {
|
|
bbase = malloc(len * 2) ;
|
|
bleng = len * 2 ;
|
|
}
|
|
if (bbase == NULL) {
|
|
WARN("messageForward - cannot prepare conversion buffer\n") ;
|
|
return ;
|
|
}
|
|
|
|
/*
|
|
* translate message (domain names)
|
|
*/
|
|
|
|
TRACE("messageForward - translate request\n") ;
|
|
|
|
cstat = translate_request(&pMsg->trctx, msg, len, bbase, bleng, &cleng) ;
|
|
|
|
TRACE("messageForward - translated status %d length %d\n", cstat, cleng) ;
|
|
|
|
if (cstat != 0) { /* error on conversion */
|
|
WARN("messageForward - translation error %d\n", cstat) ;
|
|
errorOnRequest(pMsg, msg, len, cstat) ;
|
|
return ;
|
|
}
|
|
if (pMsg->proto == SOCK_DGRAM && cleng > 512) {
|
|
WARN("messageForward - translation overflow %d\n", cleng) ;
|
|
errorOnRequest(pMsg, msg, len, 2) ;
|
|
return ;
|
|
}
|
|
|
|
/*
|
|
* forward the request
|
|
*/
|
|
|
|
p = (u_short *) bbase ;
|
|
p[0] = htons(pMsg->newId) ;
|
|
|
|
server_forward(NULL, pMsg->proto, bbase, cleng) ;
|
|
|
|
/*
|
|
* cleanup buffer
|
|
*/
|
|
|
|
if (bbase != buff) {
|
|
free(bbase) ;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* messageResponse - response to originating client
|
|
*/
|
|
|
|
static void errorOnResponse(MSGPTR pMsg, u_char *msg, int len, size_t err)
|
|
{
|
|
u_short errmsg[6] ;
|
|
u_short flags ;
|
|
u_short *ps ;
|
|
|
|
TRACE("errorOnResponse %d\n", err) ;
|
|
|
|
ps = (u_short *) msg ;
|
|
flags = ntohs(ps[1]) ;
|
|
flags = ((flags & 0x7fff) | 0x8000) ; /* QR to response */
|
|
flags = ((flags & 0xfff8) | (err & 0x0007)) ; /* set RCODE */
|
|
|
|
memset(errmsg, 0, sizeof(errmsg)) ;
|
|
errmsg[0] = htons(pMsg->orgId) ;
|
|
errmsg[1] = htons(flags) ;
|
|
|
|
server_response(&pMsg->from, pMsg->proto, (u_char *) errmsg, sizeof(errmsg)) ;
|
|
}
|
|
|
|
static void messageResponse(MSGPTR pMsg, u_char *msg, int len)
|
|
{
|
|
u_short *p ;
|
|
u_char buff[1024] ;
|
|
u_char *bbase ;
|
|
size_t bleng ;
|
|
size_t cleng ;
|
|
size_t cstat ;
|
|
|
|
TRACE("messageResponse - %04x <- %04x\n", pMsg->orgId, pMsg->newId) ;
|
|
|
|
/*
|
|
* prepare conversion buffer
|
|
*/
|
|
|
|
if (len < sizeof(buff) / 2) {
|
|
bbase = buff ;
|
|
bleng = sizeof(buff) ;
|
|
} else {
|
|
bbase = malloc(len * 2) ;
|
|
bleng = len * 2 ;
|
|
}
|
|
if (bbase == NULL) {
|
|
WARN("messageResponse - cannot prepare conversion buffer\n") ;
|
|
return ;
|
|
}
|
|
|
|
/*
|
|
* translate message (domain names)
|
|
*/
|
|
|
|
TRACE("messageResponse - translate response\n") ;
|
|
|
|
cstat = translate_reply(&pMsg->trctx, msg, len, bbase, bleng, &cleng) ;
|
|
|
|
TRACE("messageResponse - translated status %d length %d\n", cstat, cleng) ;
|
|
|
|
if (cstat != 0) { /* error on conversion */
|
|
WARN("messageResponse - translation error %d\n", cstat) ;
|
|
errorOnResponse(pMsg, msg, len, cstat) ;
|
|
return ;
|
|
}
|
|
if (pMsg->proto == SOCK_DGRAM && cleng > 512) {
|
|
WARN("messageResponse - translation overflow %d\n", cleng) ;
|
|
errorOnResponse(pMsg, msg, len, 2) ;
|
|
return ;
|
|
}
|
|
|
|
/*
|
|
* reply back to requester
|
|
*/
|
|
|
|
p = (u_short *) bbase ;
|
|
p[0] = htons(pMsg->orgId) ;
|
|
|
|
server_response(&pMsg->from, pMsg->proto, bbase, cleng) ;
|
|
|
|
/*
|
|
* cleanup buffer
|
|
*/
|
|
|
|
if (bbase != buff) {
|
|
free(bbase) ;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* notify_message - callback from server loop
|
|
*/
|
|
|
|
void notify_message(struct sockaddr *from, int proto, u_char *msg, int len)
|
|
{
|
|
u_short *p = (u_short *) msg ;
|
|
u_short msgid, flags ;
|
|
MSGPTR pMsg ;
|
|
#ifdef DEBUG
|
|
char logbuf[256] ;
|
|
#endif
|
|
|
|
msgid = ntohs(p[0]) ;
|
|
flags = ntohs(p[1]) ;
|
|
|
|
#ifdef DEBUG
|
|
if ((flags & 0x8000) == 0) {
|
|
sprintf(logbuf, "Request %04x (%04x) from %s, %d bytes",
|
|
msgid, flags, addrFmt(from, proto), len) ;
|
|
} else {
|
|
sprintf(logbuf, "Response %04x (%04x) from %s %d bytes",
|
|
msgid, flags, addrFmt(from, proto), len) ;
|
|
}
|
|
TRACE("%s\n", logbuf) ;
|
|
|
|
strcpy(logbuf, " ") ;
|
|
|
|
switch ((flags & 0x7800) >> 11) {
|
|
case 0 : strcat(logbuf, "QUERY ") ; break ;
|
|
case 1 : strcat(logbuf, "IQUERY ") ; break ;
|
|
case 2 : strcat(logbuf, "STATUS ") ; break ;
|
|
default : strcat(logbuf, "UNKNOWN") ; break ;
|
|
}
|
|
if ((flags & 0x0400) != 0) {
|
|
strcat(logbuf, ",AA") ;
|
|
}
|
|
if ((flags & 0x0200) != 0) {
|
|
strcat(logbuf, ",TC") ;
|
|
}
|
|
if ((flags & 0x0100) != 0) {
|
|
strcat(logbuf, ",RD") ;
|
|
}
|
|
if ((flags & 0x0080) != 0) {
|
|
strcat(logbuf, ",RA") ;
|
|
}
|
|
switch (flags & 0x00f) {
|
|
case 0 : strcat(logbuf, ",No Error ") ; break ;
|
|
case 1 : strcat(logbuf, ",Format Error ") ; break ;
|
|
case 2 : strcat(logbuf, ",Server Failure ") ; break ;
|
|
case 3 : strcat(logbuf, ",Name Error ") ; break ;
|
|
case 4 : strcat(logbuf, ",Not Implemented") ; break ;
|
|
case 5 : strcat(logbuf, ",Refused ") ; break ;
|
|
default : strcat(logbuf, ",Unknown Error ") ; break ;
|
|
}
|
|
TRACE("%s\n", logbuf) ;
|
|
#endif
|
|
|
|
if ((flags & 0x8000) == 0) { /* request from client */
|
|
|
|
if ((pMsg = searchReq(msgid, proto, from)) == NULL) {
|
|
pMsg = createReq(msgid, proto, from) ;
|
|
}
|
|
if (pMsg == NULL) {
|
|
WARN("notify_message - cannot create message record\n") ;
|
|
return ;
|
|
}
|
|
messageForward(pMsg, msg, len) ;
|
|
|
|
} else { /* response from server */
|
|
|
|
if ((pMsg = searchOrg(msgid, proto)) == NULL) {
|
|
WARN("notify_message - no corresponding request\n") ;
|
|
return ;
|
|
}
|
|
messageResponse(pMsg, msg, len) ;
|
|
disposeReq(pMsg);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* notify_timer - timer callback
|
|
*/
|
|
|
|
static time_t timeLastCheck = 0 ;
|
|
static time_t timeInterval = 60 ;
|
|
static time_t timeTimeout = (60 * 10) ;
|
|
|
|
void notify_timer(void)
|
|
{
|
|
time_t t = time(NULL) ;
|
|
MSGPTR p, np ;
|
|
#ifdef DEBUG
|
|
int ndiscarded = 0;
|
|
#endif
|
|
|
|
if (listMsg.prev == NULL || listMsg.next == NULL) {
|
|
listMsg.prev = &listMsg ;
|
|
listMsg.next = &listMsg ;
|
|
}
|
|
|
|
if ((t - timeLastCheck) < timeInterval) {
|
|
return ;
|
|
}
|
|
for (p = listMsg.next ; p != &listMsg ; p = np) {
|
|
np = p->next ;
|
|
if ((t - p->last) > timeTimeout) {
|
|
disposeReq(p) ;
|
|
#ifdef DEBUG
|
|
ndiscarded++;
|
|
#endif
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
TRACE("notify_timer: %d discarded\n", ndiscarded);
|
|
#endif
|
|
timeLastCheck = t;
|
|
}
|