diff options
Diffstat (limited to 'source3/utils/nmb-agent.c')
-rw-r--r-- | source3/utils/nmb-agent.c | 515 |
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; +} |