mirror of
https://github.com/samba-team/samba.git
synced 2025-03-09 08:58:35 +03:00
created an "nmb-agent" utility that, yes: it connects to the 137 socket and accepts unix socket connections which it redirects onto port 137. it uses the name_trn_id field to filter requests to the correct location. name_query() and name_status() are the first victims to use this feature (by specifying a file descriptor of -1).
-
516 lines
10 KiB
C
516 lines
10 KiB
C
/*
|
|
Unix SMB/Netbios implementation.
|
|
Version 2
|
|
SMB agent/socket plugin
|
|
Copyright (C) Andrew Tridgell 1999
|
|
|
|
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"
|
|
#include "smb.h"
|
|
|
|
#define SECURITY_MASK 0
|
|
#define SECURITY_SET 0
|
|
|
|
/* this forces non-unicode */
|
|
#define CAPABILITY_MASK CAP_UNICODE
|
|
#define CAPABILITY_SET 0
|
|
|
|
/* and non-unicode for the client too */
|
|
#define CLI_CAPABILITY_MASK CAP_UNICODE
|
|
#define CLI_CAPABILITY_SET 0
|
|
|
|
extern int DEBUGLEVEL;
|
|
|
|
static int ClientNMB = -1;
|
|
|
|
struct sock_redir
|
|
{
|
|
int c;
|
|
int c_trn_id;
|
|
int s_trn_id;
|
|
struct nmb_state *n;
|
|
time_t time;
|
|
|
|
};
|
|
|
|
static uint32 num_socks = 0;
|
|
static struct sock_redir **socks = NULL;
|
|
|
|
/****************************************************************************
|
|
terminate sockent connection
|
|
****************************************************************************/
|
|
static void sock_redir_free(struct sock_redir *sock)
|
|
{
|
|
close(sock->c);
|
|
sock->c = -1;
|
|
if (sock->n != NULL)
|
|
{
|
|
#if 0
|
|
free(sock->n);
|
|
#endif
|
|
sock->n = NULL;
|
|
}
|
|
#if 0
|
|
free(sock);
|
|
#endif
|
|
ZERO_STRUCTP(sock);
|
|
}
|
|
|
|
/****************************************************************************
|
|
free a sockent array
|
|
****************************************************************************/
|
|
static void free_sock_array(uint32 num_entries, struct sock_redir **entries)
|
|
{
|
|
void(*fn)(void*) = (void(*)(void*))&sock_redir_free;
|
|
free_void_array(num_entries, (void**)entries, *fn);
|
|
}
|
|
|
|
/****************************************************************************
|
|
add a sockent state to the array
|
|
****************************************************************************/
|
|
static struct sock_redir* add_sock_to_array(uint32 *len,
|
|
struct sock_redir ***array,
|
|
struct sock_redir *sock)
|
|
{
|
|
int i;
|
|
for (i = 0; i < num_socks; i++)
|
|
{
|
|
if (socks[i] == NULL)
|
|
{
|
|
socks[i] = sock;
|
|
return sock;
|
|
}
|
|
}
|
|
|
|
return (struct sock_redir*)add_item_to_array(len,
|
|
(void***)array, (void*)sock);
|
|
|
|
}
|
|
|
|
/****************************************************************************
|
|
initiate sockent array
|
|
****************************************************************************/
|
|
void init_sock_redir(void)
|
|
{
|
|
socks = NULL;
|
|
num_socks = 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
terminate sockent array
|
|
****************************************************************************/
|
|
void free_sock_redir(void)
|
|
{
|
|
free_sock_array(num_socks, socks);
|
|
init_sock_redir();
|
|
}
|
|
|
|
/****************************************************************************
|
|
create a new sockent state from user credentials
|
|
****************************************************************************/
|
|
static struct sock_redir *sock_redir_get(int fd)
|
|
{
|
|
struct sock_redir *sock;
|
|
|
|
sock = (struct sock_redir*)malloc(sizeof(*sock));
|
|
|
|
if (sock == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ZERO_STRUCTP(sock);
|
|
|
|
sock->c = fd;
|
|
sock->n = NULL;
|
|
sock->time = time(NULL);
|
|
|
|
DEBUG(10,("sock_redir_get:\tfd:\t%d\t\n", fd));
|
|
|
|
return sock;
|
|
}
|
|
|
|
/****************************************************************************
|
|
init sock state
|
|
****************************************************************************/
|
|
static void sock_add(int fd)
|
|
{
|
|
struct sock_redir *sock;
|
|
sock = sock_redir_get(fd);
|
|
if (sock != NULL)
|
|
{
|
|
add_sock_to_array(&num_socks, &socks, sock);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
delete a sockent state
|
|
****************************************************************************/
|
|
static BOOL sock_del(int fd)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < num_socks; i++)
|
|
{
|
|
if (socks[i] == NULL) continue;
|
|
if (socks[i]->c == fd)
|
|
{
|
|
sock_redir_free(socks[i]);
|
|
socks[i] = NULL;
|
|
return True;
|
|
}
|
|
}
|
|
|
|
return False;
|
|
}
|
|
|
|
static void filter_reply(struct packet_struct *p, int tr_id)
|
|
{
|
|
p->packet.nmb.header.name_trn_id = tr_id;
|
|
}
|
|
|
|
static BOOL process_cli_sock(struct sock_redir **sock)
|
|
{
|
|
struct packet_struct *p;
|
|
struct nmb_state *nmb;
|
|
static uint16 trn_id = 0x0;
|
|
|
|
p = receive_packet((*sock)->c, NMB_SOCK_PACKET, 0);
|
|
if (p == NULL)
|
|
{
|
|
DEBUG(0,("client closed connection\n"));
|
|
return False;
|
|
}
|
|
|
|
nmb = (struct nmb_state*)malloc(sizeof(struct nmb_state));
|
|
if (nmb == NULL)
|
|
{
|
|
return False;
|
|
}
|
|
|
|
(*sock)->n = nmb;
|
|
(*sock)->c_trn_id = p->packet.nmb.header.name_trn_id;
|
|
(*sock)->s_trn_id = trn_id;
|
|
trn_id++;
|
|
if (trn_id > 0xffff)
|
|
{
|
|
trn_id = 0x0;
|
|
}
|
|
|
|
DEBUG(10,("new trn_id: %d\n", trn_id));
|
|
|
|
filter_reply(p, (*sock)->s_trn_id);
|
|
|
|
nmb->ip = p->ip;
|
|
nmb->port = p->port;
|
|
|
|
p->fd = ClientNMB;
|
|
p->packet_type = NMB_PACKET;
|
|
|
|
if (!send_packet(p))
|
|
{
|
|
DEBUG(0,("server is dead\n"));
|
|
return False;
|
|
}
|
|
return True;
|
|
}
|
|
|
|
static BOOL process_srv_sock(struct sock_redir *sock, struct packet_struct *p)
|
|
{
|
|
int nmb_id;
|
|
int tr_id;
|
|
if (p == NULL)
|
|
{
|
|
return False;
|
|
}
|
|
|
|
nmb_id = p->packet.nmb.header.name_trn_id;
|
|
tr_id = sock->s_trn_id;
|
|
|
|
DEBUG(10,("process_srv_sock:\tnmb_id:\t%d\n", nmb_id));
|
|
|
|
DEBUG(10,("list:\tfd:\t%d\tnmb_id:\t%d\ttr_id:\t%d\n",
|
|
sock->c,
|
|
nmb_id,
|
|
tr_id));
|
|
|
|
if (nmb_id != tr_id)
|
|
{
|
|
return False;
|
|
}
|
|
|
|
filter_reply(p, sock->c_trn_id);
|
|
p->fd = sock->c;
|
|
p->packet_type = NMB_SOCK_PACKET;
|
|
|
|
if (!send_packet(p))
|
|
{
|
|
DEBUG(0,("client is dead\n"));
|
|
}
|
|
return True;
|
|
}
|
|
|
|
static void start_agent(void)
|
|
{
|
|
int s, c;
|
|
struct sockaddr_un sa;
|
|
fstring path;
|
|
fstring dir;
|
|
|
|
CatchChild();
|
|
|
|
slprintf(dir, sizeof(dir)-1, "/tmp/.nmb");
|
|
mkdir(dir, 0777);
|
|
|
|
slprintf(path, sizeof(path)-1, "%s/agent", dir);
|
|
if (chmod(dir, 0777) < 0)
|
|
{
|
|
fprintf(stderr, "chmod on %s failed\n", sa.sun_path);
|
|
exit(1);
|
|
}
|
|
|
|
|
|
/* start listening on unix socket */
|
|
s = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
|
|
if (s < 0)
|
|
{
|
|
fprintf(stderr, "socket open failed\n");
|
|
exit(1);
|
|
}
|
|
|
|
ZERO_STRUCT(sa);
|
|
sa.sun_family = AF_UNIX;
|
|
safe_strcpy(sa.sun_path, path, sizeof(sa.sun_path)-1);
|
|
|
|
if (bind(s, (struct sockaddr*) &sa, sizeof(sa)) < 0)
|
|
{
|
|
fprintf(stderr, "socket bind to %s failed\n", sa.sun_path);
|
|
close(s);
|
|
remove(path);
|
|
exit(1);
|
|
}
|
|
|
|
if (s == -1)
|
|
{
|
|
DEBUG(0,("bind failed\n"));
|
|
remove(path);
|
|
exit(1);
|
|
}
|
|
|
|
if (listen(s, 5) == -1)
|
|
{
|
|
DEBUG(0,("listen failed\n"));
|
|
remove(path);
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
int i;
|
|
fd_set fds;
|
|
int num;
|
|
struct sockaddr_un addr;
|
|
int in_addrlen = sizeof(addr);
|
|
int maxfd = s;
|
|
struct packet_struct *p = NULL;
|
|
time_t current_time = time(NULL);
|
|
|
|
FD_ZERO(&fds);
|
|
FD_SET(s, &fds);
|
|
FD_SET(ClientNMB, &fds);
|
|
maxfd = MAX(ClientNMB, maxfd);
|
|
|
|
for (i = 0; i < num_socks; i++)
|
|
{
|
|
if (socks[i] != NULL)
|
|
{
|
|
int fd = socks[i]->c;
|
|
FD_SET(fd, &fds);
|
|
maxfd = MAX(maxfd, fd);
|
|
|
|
}
|
|
}
|
|
|
|
dbgflush();
|
|
num = sys_select(maxfd+1,&fds,NULL, NULL);
|
|
|
|
if (num <= 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (FD_ISSET(s, &fds))
|
|
{
|
|
c = accept(s, (struct sockaddr*)&addr, &in_addrlen);
|
|
if (c != -1)
|
|
{
|
|
sock_add(c);
|
|
}
|
|
}
|
|
|
|
if (FD_ISSET(ClientNMB, &fds))
|
|
{
|
|
p = receive_packet(ClientNMB, NMB_PACKET, 0);
|
|
if (p && !p->packet.nmb.header.response)
|
|
{
|
|
free(p);
|
|
p = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
p = NULL;
|
|
}
|
|
for (i = 0; i < num_socks; i++)
|
|
{
|
|
if (socks[i] == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
if (FD_ISSET(socks[i]->c, &fds))
|
|
{
|
|
if (!process_cli_sock(&socks[i]))
|
|
{
|
|
sock_redir_free(socks[i]);
|
|
socks[i] = NULL;
|
|
}
|
|
}
|
|
|
|
if (p == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (socks[i] == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (process_srv_sock(socks[i], p) ||
|
|
current_time > socks[i]->time + 5)
|
|
{
|
|
sock_redir_free(socks[i]);
|
|
socks[i] = NULL;
|
|
}
|
|
}
|
|
|
|
if (p != NULL)
|
|
{
|
|
free(p);
|
|
p = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**************************************************************************** **
|
|
open the socket communication
|
|
**************************************************************************** */
|
|
static BOOL open_sockets(BOOL isdaemon, int port)
|
|
{
|
|
/* The sockets opened here will be used to receive broadcast
|
|
packets *only*. Interface specific sockets are opened in
|
|
make_subnet() in namedbsubnet.c. Thus we bind to the
|
|
address "0.0.0.0". The parameter 'socket address' is
|
|
now deprecated.
|
|
*/
|
|
|
|
if ( isdaemon )
|
|
ClientNMB = open_socket_in(SOCK_DGRAM, port,0,0);
|
|
else
|
|
ClientNMB = 0;
|
|
|
|
if ( ClientNMB == -1 )
|
|
return( False );
|
|
|
|
/* we are never interested in SIGPIPE */
|
|
BlockSignals(True,SIGPIPE);
|
|
|
|
set_socket_options( ClientNMB, "SO_BROADCAST" );
|
|
|
|
DEBUG( 3, ( "open_sockets: Broadcast sockets opened.\n" ) );
|
|
return( True );
|
|
} /* open_sockets */
|
|
|
|
/****************************************************************************
|
|
usage on the program
|
|
****************************************************************************/
|
|
static void usage(char *pname)
|
|
{
|
|
printf("Usage: %s [-D]", pname);
|
|
|
|
printf("\nVersion %s\n",VERSION);
|
|
printf("\t-D run as a daemon\n");
|
|
printf("\t-h usage\n");
|
|
printf("\n");
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
pstring configfile;
|
|
BOOL is_daemon = False;
|
|
int opt;
|
|
extern pstring debugf;
|
|
int global_nmb_port = NMB_PORT;
|
|
|
|
TimeInit();
|
|
|
|
pstrcpy(configfile,CONFIGFILE);
|
|
|
|
while ((opt = getopt(argc, argv, "Dh")) != EOF)
|
|
{
|
|
switch (opt)
|
|
{
|
|
case 'D':
|
|
{
|
|
is_daemon = True;
|
|
break;
|
|
}
|
|
case 'h':
|
|
default:
|
|
{
|
|
usage(argv[0]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
slprintf(debugf, sizeof(debugf)-1, "log.%s", argv[0]);
|
|
setup_logging(argv[0], !is_daemon);
|
|
|
|
charset_initialise();
|
|
|
|
if (!lp_load(configfile,True,False,False))
|
|
{
|
|
DEBUG(0,("Unable to load config file\n"));
|
|
}
|
|
|
|
if (is_daemon)
|
|
{
|
|
DEBUG(0,("%s: becoming daemon\n", argv[0]));
|
|
become_daemon();
|
|
}
|
|
|
|
if (!open_sockets(True, global_nmb_port))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
start_agent();
|
|
|
|
return 0;
|
|
}
|