summaryrefslogtreecommitdiff
path: root/source3/utils/nmb-agent.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/utils/nmb-agent.c')
-rw-r--r--source3/utils/nmb-agent.c515
1 files changed, 515 insertions, 0 deletions
diff --git a/source3/utils/nmb-agent.c b/source3/utils/nmb-agent.c
new file mode 100644
index 0000000000..12795e5bdd
--- /dev/null
+++ b/source3/utils/nmb-agent.c
@@ -0,0 +1,515 @@
+/*
+ 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;
+}