mirror of
https://github.com/samba-team/samba.git
synced 2024-12-24 21:34:56 +03:00
b7fb6c6b38
Kanji support is one case of multibyte character
support, rather than being a specific case in
single byte character support.
This allows us to add Big5 Chinese support (code page 950)
and Korean Hangul support (code page 949) at very little
cost. Also allows us to easily add future multibyte
code pages.
Makefile: Added codepages 949, 950 as we now support more multibyte
codepages.
asyncdns.c: Fixed problem with child being re-spawned when parent killed.
charcnv.c
charset.c
client.c
clitar.c
kanji.c
kanji.h
smb.h
util.c
loadparm.c: Generic multibyte codepage support (adding Big5 Chinese
and Korean Hangul).
nmbd.c: Fixed problem with child being re-spawned when parent killed.
mangle.c: Modified str_checksum so that first 15 characters have more
effect on outcome. This helps with short name mangling as
most 'long' names are still shorter than 15 chars (bug was
foobar_mng and foobar_sum would hash to the same value, with
the modified code they hash differently.
Jeremy.
(This used to be commit 299016338c
)
350 lines
9.3 KiB
C
350 lines
9.3 KiB
C
/*
|
|
Unix SMB/Netbios implementation.
|
|
a async DNS handler
|
|
Copyright (C) Andrew Tridgell 1997-1998
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
|
|
extern int DEBUGLEVEL;
|
|
|
|
/***************************************************************************
|
|
Add a DNS result to the name cache.
|
|
****************************************************************************/
|
|
|
|
static struct name_record *add_dns_result(struct nmb_name *question, struct in_addr addr)
|
|
{
|
|
int name_type = question->name_type;
|
|
char *qname = question->name;
|
|
|
|
|
|
if (!addr.s_addr) {
|
|
/* add the fail to WINS cache of names. give it 1 hour in the cache */
|
|
DEBUG(3,("add_dns_result: Negative DNS answer for %s\n", qname));
|
|
add_name_to_subnet(wins_server_subnet,qname,name_type,
|
|
NB_ACTIVE, 60*60, DNSFAIL_NAME, 1, &addr);
|
|
return NULL;
|
|
}
|
|
|
|
/* add it to our WINS cache of names. give it 2 hours in the cache */
|
|
DEBUG(3,("add_dns_result: DNS gave answer for %s of %s\n", qname, inet_ntoa(addr)));
|
|
|
|
return add_name_to_subnet(wins_server_subnet,qname,name_type,
|
|
NB_ACTIVE, 2*60*60, DNS_NAME, 1, &addr);
|
|
}
|
|
|
|
|
|
|
|
#ifndef SYNC_DNS
|
|
|
|
static int fd_in = -1, fd_out = -1;
|
|
static int child_pid = -1;
|
|
static int in_dns;
|
|
|
|
/* this is the structure that is passed between the parent and child */
|
|
struct query_record {
|
|
struct nmb_name name;
|
|
struct in_addr result;
|
|
};
|
|
|
|
/* a queue of pending requests waiting to be sent to the DNS child */
|
|
static struct packet_struct *dns_queue;
|
|
|
|
/* the packet currently being processed by the dns child */
|
|
static struct packet_struct *dns_current;
|
|
|
|
|
|
/***************************************************************************
|
|
return the fd used to gather async dns replies. This is added to the select
|
|
loop
|
|
****************************************************************************/
|
|
int asyncdns_fd(void)
|
|
{
|
|
return fd_in;
|
|
}
|
|
|
|
/***************************************************************************
|
|
handle DNS queries arriving from the parent
|
|
****************************************************************************/
|
|
static void asyncdns_process(void)
|
|
{
|
|
struct query_record r;
|
|
fstring qname;
|
|
|
|
DEBUGLEVEL = -1;
|
|
|
|
while (1) {
|
|
if (read_data(fd_in, (char *)&r, sizeof(r)) != sizeof(r))
|
|
break;
|
|
|
|
fstrcpy(qname, r.name.name);
|
|
|
|
r.result.s_addr = interpret_addr(qname);
|
|
|
|
if (write_data(fd_out, (char *)&r, sizeof(r)) != sizeof(r))
|
|
break;
|
|
}
|
|
|
|
_exit(0);
|
|
}
|
|
|
|
/**************************************************************************** **
|
|
catch a sigterm (in the child process - the parent has a different handler
|
|
see nmbd.c for details).
|
|
We need a separate term handler here so we don't release any
|
|
names that our parent is going to release, or overwrite a
|
|
WINS db that our parent is going to write.
|
|
**************************************************************************** */
|
|
|
|
static int sig_term()
|
|
{
|
|
_exit(0);
|
|
/* Keep compiler happy.. */
|
|
return 0;
|
|
}
|
|
|
|
/***************************************************************************
|
|
Called by the parent process when it receives a SIGTERM - also kills the
|
|
child so we don't get child async dns processes lying around, causing trouble.
|
|
****************************************************************************/
|
|
|
|
void kill_async_dns_child()
|
|
{
|
|
if(child_pid != 0 && child_pid != -1)
|
|
kill(child_pid, SIGTERM);
|
|
}
|
|
|
|
/***************************************************************************
|
|
create a child process to handle DNS lookups
|
|
****************************************************************************/
|
|
void start_async_dns(void)
|
|
{
|
|
int fd1[2], fd2[2];
|
|
|
|
signal(SIGCLD, SIG_IGN);
|
|
|
|
if (pipe(fd1) || pipe(fd2)) {
|
|
return;
|
|
}
|
|
|
|
child_pid = fork();
|
|
|
|
if (child_pid) {
|
|
fd_in = fd1[0];
|
|
fd_out = fd2[1];
|
|
close(fd1[1]);
|
|
close(fd2[0]);
|
|
return;
|
|
}
|
|
|
|
fd_in = fd2[0];
|
|
fd_out = fd1[1];
|
|
|
|
signal(SIGUSR2, SIG_IGN);
|
|
signal(SIGUSR1, SIG_IGN);
|
|
signal(SIGHUP, SIG_IGN);
|
|
signal(SIGTERM, SIGNAL_CAST sig_term );
|
|
|
|
asyncdns_process();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
check if a particular name is already being queried
|
|
****************************************************************************/
|
|
static BOOL query_current(struct query_record *r)
|
|
{
|
|
return dns_current &&
|
|
nmb_name_equal(&r->name,
|
|
&dns_current->packet.nmb.question.question_name);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
write a query to the child process
|
|
****************************************************************************/
|
|
static BOOL write_child(struct packet_struct *p)
|
|
{
|
|
struct query_record r;
|
|
|
|
r.name = p->packet.nmb.question.question_name;
|
|
|
|
return write_data(fd_out, (char *)&r, sizeof(r)) == sizeof(r);
|
|
}
|
|
|
|
/***************************************************************************
|
|
check the DNS queue
|
|
****************************************************************************/
|
|
void run_dns_queue(void)
|
|
{
|
|
struct query_record r;
|
|
struct packet_struct *p, *p2;
|
|
struct name_record *namerec;
|
|
int size;
|
|
|
|
if (fd_in == -1)
|
|
return;
|
|
|
|
/* Allow SIGTERM to kill us. */
|
|
BlockSignals(False, SIGTERM);
|
|
|
|
if (!process_exists(child_pid)) {
|
|
close(fd_in);
|
|
start_async_dns();
|
|
}
|
|
|
|
if ((size=read_data(fd_in, (char *)&r, sizeof(r))) != sizeof(r)) {
|
|
if (size) {
|
|
DEBUG(0,("Incomplete DNS answer from child!\n"));
|
|
fd_in = -1;
|
|
}
|
|
BlockSignals(True, SIGTERM);
|
|
return;
|
|
}
|
|
|
|
BlockSignals(True, SIGTERM);
|
|
|
|
namerec = add_dns_result(&r.name, r.result);
|
|
|
|
if (dns_current) {
|
|
if (query_current(&r)) {
|
|
DEBUG(3,("DNS calling send_wins_name_query_response\n"));
|
|
in_dns = 1;
|
|
if(namerec == NULL)
|
|
send_wins_name_query_response(NAM_ERR, dns_current, NULL);
|
|
else
|
|
send_wins_name_query_response(0,dns_current,namerec);
|
|
in_dns = 0;
|
|
}
|
|
|
|
dns_current->locked = False;
|
|
free_packet(dns_current);
|
|
dns_current = NULL;
|
|
}
|
|
|
|
/* loop over the whole dns queue looking for entries that
|
|
match the result we just got */
|
|
for (p = dns_queue; p;) {
|
|
struct nmb_packet *nmb = &p->packet.nmb;
|
|
struct nmb_name *question = &nmb->question.question_name;
|
|
|
|
if (nmb_name_equal(question, &r.name)) {
|
|
DEBUG(3,("DNS calling send_wins_name_query_response\n"));
|
|
in_dns = 1;
|
|
if(namerec == NULL)
|
|
send_wins_name_query_response(NAM_ERR, p, NULL);
|
|
else
|
|
send_wins_name_query_response(0,p,namerec);
|
|
in_dns = 0;
|
|
p->locked = False;
|
|
|
|
if (p->prev)
|
|
p->prev->next = p->next;
|
|
else
|
|
dns_queue = p->next;
|
|
if (p->next)
|
|
p->next->prev = p->prev;
|
|
p2 = p->next;
|
|
free_packet(p);
|
|
p = p2;
|
|
} else {
|
|
p = p->next;
|
|
}
|
|
}
|
|
|
|
if (dns_queue) {
|
|
dns_current = dns_queue;
|
|
dns_queue = dns_queue->next;
|
|
if (dns_queue) dns_queue->prev = NULL;
|
|
dns_current->next = NULL;
|
|
|
|
if (!write_child(dns_current)) {
|
|
DEBUG(3,("failed to send DNS query to child!\n"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/***************************************************************************
|
|
queue a DNS query
|
|
****************************************************************************/
|
|
BOOL queue_dns_query(struct packet_struct *p,struct nmb_name *question,
|
|
struct name_record **n)
|
|
{
|
|
if (in_dns || fd_in == -1)
|
|
return False;
|
|
|
|
if (!dns_current) {
|
|
if (!write_child(p)) {
|
|
DEBUG(3,("failed to send DNS query to child!\n"));
|
|
return False;
|
|
}
|
|
dns_current = p;
|
|
p->locked = True;
|
|
} else {
|
|
p->locked = True;
|
|
p->next = dns_queue;
|
|
p->prev = NULL;
|
|
if (p->next)
|
|
p->next->prev = p;
|
|
dns_queue = p;
|
|
}
|
|
|
|
DEBUG(3,("added DNS query for %s\n", namestr(question)));
|
|
return True;
|
|
}
|
|
|
|
#else
|
|
|
|
|
|
/***************************************************************************
|
|
we use this then we can't do async DNS lookups
|
|
****************************************************************************/
|
|
BOOL queue_dns_query(struct packet_struct *p,struct nmb_name *question,
|
|
struct name_record **n)
|
|
{
|
|
char *qname = question->name;
|
|
struct in_addr dns_ip;
|
|
|
|
DEBUG(3,("DNS search for %s - ", namestr(question)));
|
|
|
|
/* Unblock TERM signal so we can be killed in DNS lookup. */
|
|
BlockSignals(False, SIGTERM);
|
|
|
|
dns_ip.s_addr = interpret_addr(qname);
|
|
|
|
/* Re-block TERM signal. */
|
|
BlockSignals(True, SIGTERM);
|
|
|
|
*n = add_dns_result(question, dns_ip);
|
|
if(*n == NULL)
|
|
send_wins_name_query_response(NAM_ERR, p, NULL);
|
|
else
|
|
send_wins_name_query_response(0, p, *n);
|
|
return False;
|
|
}
|
|
|
|
/***************************************************************************
|
|
With sync dns there is no child to kill on SIGTERM.
|
|
****************************************************************************/
|
|
void kill_async_dns_child()
|
|
{
|
|
return;
|
|
}
|
|
#endif
|