samba: share select wrappers.
[samba.git] / source3 / nmbd / nmbd_packets.c
index 2fbf2f41468356a93ab43f6ba6db28a24a2da788..5d5a67bf62be2dcfddba4d1a2ff9e1adb04bea83 100644 (file)
@@ -7,7 +7,7 @@
    
    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
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    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.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
 */
 
 #include "includes.h"
+#include "nmbd/nmbd.h"
+#include "../lib/util/select.h"
 
 extern int ClientNMB;
 extern int ClientDGRAM;
@@ -29,11 +30,7 @@ extern int global_nmb_port;
 
 extern int num_response_packets;
 
-extern struct in_addr loopback_ip;
-
-static void queue_packet(struct packet_struct *packet);
-
-BOOL rescan_listen_set = False;
+bool rescan_listen_set = False;
 
 
 /*******************************************************************
@@ -53,7 +50,7 @@ static int find_subnet_fd_for_address( struct in_addr local_ip )
        struct subnet_record *subrec;
 
        for( subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
-               if(ip_equal(local_ip, subrec->myip))
+               if(ip_equal_v4(local_ip, subrec->myip))
                        return subrec->nmb_sock;
 
        return ClientNMB;
@@ -68,7 +65,7 @@ static int find_subnet_mailslot_fd_for_address( struct in_addr local_ip )
        struct subnet_record *subrec;
 
        for( subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
-               if(ip_equal(local_ip, subrec->myip))
+               if(ip_equal_v4(local_ip, subrec->myip))
                        return subrec->dgram_sock;
 
        return ClientDGRAM;
@@ -144,12 +141,12 @@ static uint16 generate_name_trn_id(void)
  Either loops back or sends out a completed NetBIOS packet.
 **************************************************************************/
 
-static BOOL send_netbios_packet(struct packet_struct *p)
+static bool send_netbios_packet(struct packet_struct *p)
 {
-       BOOL loopback_this_packet = False;
+       bool loopback_this_packet = False;
 
        /* Check if we are sending to or from ourselves as a WINS server. */
-       if(ismyip(p->ip) && (p->port == global_nmb_port))
+       if(ismyip_v4(p->ip) && (p->port == global_nmb_port))
                loopback_this_packet = True;
 
        if(loopback_this_packet) {
@@ -177,7 +174,7 @@ static BOOL send_netbios_packet(struct packet_struct *p)
 **************************************************************************/
 
 static struct packet_struct *create_and_init_netbios_packet(struct nmb_name *nmbname,
-                                                            BOOL bcast, BOOL rec_des,
+                                                            bool bcast, bool rec_des,
                                                             struct in_addr to_ip)
 {
        struct packet_struct *packet = NULL;
@@ -212,7 +209,8 @@ static struct packet_struct *create_and_init_netbios_packet(struct nmb_name *nmb
 
        packet->ip = to_ip;
        packet->port = NMB_PORT;
-       packet->fd = ClientNMB;
+       packet->recv_fd = -1;
+       packet->send_fd = ClientNMB;
        packet->timestamp = time(NULL);
        packet->packet_type = NMB_PACKET;
        packet->locked = False;
@@ -224,14 +222,14 @@ static struct packet_struct *create_and_init_netbios_packet(struct nmb_name *nmb
  Sets up the common elements of register, refresh or release packet.
 **************************************************************************/
 
-static BOOL create_and_init_additional_record(struct packet_struct *packet,
+static bool create_and_init_additional_record(struct packet_struct *packet,
                                                      uint16 nb_flags,
-                                                     struct in_addr *register_ip)
+                                                     const struct in_addr *register_ip)
 {
        struct nmb_packet *nmb = &packet->packet.nmb;
 
        if((nmb->additional = SMB_MALLOC_P(struct res_rec)) == NULL) {
-               DEBUG(0,("initiate_name_register_packet: malloc fail for additional record.\n"));
+               DEBUG(0,("create_and_init_additional_record: malloc fail for additional record.\n"));
                return False;
        }
 
@@ -263,7 +261,8 @@ static BOOL create_and_init_additional_record(struct packet_struct *packet,
           our standard refresh cycle for that name which copes nicely
           with disconnected networks.
        */
-       packet->fd = find_subnet_fd_for_address(*register_ip);
+       packet->recv_fd = -1;
+       packet->send_fd = find_subnet_fd_for_address(*register_ip);
 
        return True;
 }
@@ -272,7 +271,7 @@ static BOOL create_and_init_additional_record(struct packet_struct *packet,
  Sends out a name query.
 **************************************************************************/
 
-static BOOL initiate_name_query_packet( struct packet_struct *packet)
+static bool initiate_name_query_packet( struct packet_struct *packet)
 {
        struct nmb_packet *nmb = NULL;
 
@@ -294,7 +293,7 @@ static BOOL initiate_name_query_packet( struct packet_struct *packet)
  Sends out a name query - from a WINS server. 
 **************************************************************************/
 
-static BOOL initiate_name_query_packet_from_wins_server( struct packet_struct *packet)
+static bool initiate_name_query_packet_from_wins_server( struct packet_struct *packet)
 {   
        struct nmb_packet *nmb = NULL;
   
@@ -316,8 +315,8 @@ static BOOL initiate_name_query_packet_from_wins_server( struct packet_struct *p
  Sends out a name register.
 **************************************************************************/
 
-static BOOL initiate_name_register_packet( struct packet_struct *packet,
-                                    uint16 nb_flags, struct in_addr *register_ip)
+static bool initiate_name_register_packet( struct packet_struct *packet,
+                                    uint16 nb_flags, const struct in_addr *register_ip)
 {
        struct nmb_packet *nmb = &packet->packet.nmb;
 
@@ -340,7 +339,7 @@ static BOOL initiate_name_register_packet( struct packet_struct *packet,
  Sends out a multihomed name register.
 **************************************************************************/
 
-static BOOL initiate_multihomed_name_register_packet(struct packet_struct *packet,
+static bool initiate_multihomed_name_register_packet(struct packet_struct *packet,
                                                     uint16 nb_flags, struct in_addr *register_ip)
 {
        struct nmb_packet *nmb = &packet->packet.nmb;
@@ -368,7 +367,7 @@ for name %s IP %s (bcast=%s) to IP %s\n",
  Sends out a name refresh.
 **************************************************************************/
 
-static BOOL initiate_name_refresh_packet( struct packet_struct *packet,
+static bool initiate_name_refresh_packet( struct packet_struct *packet,
                                    uint16 nb_flags, struct in_addr *refresh_ip)
 {
        struct nmb_packet *nmb = &packet->packet.nmb;
@@ -392,7 +391,7 @@ static BOOL initiate_name_refresh_packet( struct packet_struct *packet,
  Sends out a name release.
 **************************************************************************/
 
-static BOOL initiate_name_release_packet( struct packet_struct *packet,
+static bool initiate_name_release_packet( struct packet_struct *packet,
                                    uint16 nb_flags, struct in_addr *release_ip)
 {
        struct nmb_packet *nmb = &packet->packet.nmb;
@@ -416,7 +415,7 @@ static BOOL initiate_name_release_packet( struct packet_struct *packet,
  Sends out a node status.
 **************************************************************************/
 
-static BOOL initiate_node_status_packet( struct packet_struct *packet )
+static bool initiate_node_status_packet( struct packet_struct *packet )
 {
        struct nmb_packet *nmb = &packet->packet.nmb;
 
@@ -445,7 +444,7 @@ static BOOL initiate_node_status_packet( struct packet_struct *packet )
  broadcast subnet.
 ****************************************************************************/
 
-static BOOL assert_check_subnet(struct subnet_record *subrec)
+static bool assert_check_subnet(struct subnet_record *subrec)
 {
        if( subrec == remote_broadcast_subnet) {
                DEBUG(0,("assert_check_subnet: Attempt to send packet on remote broadcast subnet. \
@@ -470,7 +469,8 @@ struct response_record *queue_register_name( struct subnet_record *subrec,
 {
        struct packet_struct *p;
        struct response_record *rrec;
-
+       struct sockaddr_storage ss;
+       const struct sockaddr_storage *pss = NULL;
        if(assert_check_subnet(subrec))
                return NULL;
 
@@ -479,7 +479,16 @@ struct response_record *queue_register_name( struct subnet_record *subrec,
                                subrec->bcast_ip)) == NULL)
                return NULL;
 
-       if(initiate_name_register_packet( p, nb_flags, iface_ip(subrec->bcast_ip)) == False) {
+       in_addr_to_sockaddr_storage(&ss, subrec->bcast_ip);
+       pss = iface_ip((struct sockaddr *)&ss);
+       if (!pss || pss->ss_family != AF_INET) {
+               p->locked = False;
+               free_packet(p);
+               return NULL;
+       }
+
+       if(initiate_name_register_packet(p, nb_flags,
+                       &((const struct sockaddr_in *)pss)->sin_addr) == False) {
                p->locked = False;
                free_packet(p);
                return NULL;
@@ -580,7 +589,7 @@ struct response_record *queue_register_multihomed_name( struct subnet_record *su
 {
        struct packet_struct *p;
        struct response_record *rrec;
-       BOOL ret;
+       bool ret;
        
        /* Sanity check. */
        if(subrec != unicast_subnet) {
@@ -699,7 +708,7 @@ struct response_record *queue_query_name( struct subnet_record *subrec,
   
        /* queries to the WINS server turn up here as queries to IP 0.0.0.0 
                        These need to be handled a bit differently */
-       if (subrec->type == UNICAST_SUBNET && is_zero_ip(to_ip)) {
+       if (subrec->type == UNICAST_SUBNET && is_zero_ip_v4(to_ip)) {
                /* What we really need to do is loop over each of our wins
                 * servers and wins server tags here, but that just doesn't
                 * fit our architecture at the moment (userdata may already
@@ -725,20 +734,20 @@ struct response_record *queue_query_name( struct subnet_record *subrec,
 
                DEBUG(10,("queue_query_name: bind_interfaces_only is set, looking for suitable source IP\n"));
                for(i = 0; i < iface_count(); i++) {
-                       struct in_addr *ifip = iface_n_ip(i);
+                       const struct in_addr *ifip = iface_n_ip_v4(i);
 
-                       if(ifip == NULL) {
+                       if (ifip == NULL) {
                                DEBUG(0,("queue_query_name: interface %d has NULL IP address !\n", i));
                                continue;
                        }
 
-                       if (ip_equal(*ifip,loopback_ip)) {
+                       if (is_loopback_ip_v4(*ifip)) {
                                DEBUG(5,("queue_query_name: ignoring loopback interface (%d)\n", i));
                                continue;
                        }
 
                        DEBUG(10,("queue_query_name: using source IP %s\n",inet_ntoa(*ifip)));
-                               p->fd = find_subnet_fd_for_address( *ifip );
+                               p->send_fd = find_subnet_fd_for_address( *ifip );
                                break;
                }
        }
@@ -767,7 +776,7 @@ struct response_record *queue_query_name( struct subnet_record *subrec,
 /****************************************************************************
  Queue a query name packet to a given address from the WINS subnet.
 ****************************************************************************/
+
 struct response_record *queue_query_name_from_wins_server( struct in_addr to_ip,
                           response_function resp_fn,
                           timeout_response_function timeout_fn,
@@ -806,7 +815,7 @@ struct response_record *queue_query_name_from_wins_server( struct in_addr to_ip,
 /****************************************************************************
  Queue a node status packet to a given name and address.
 ****************************************************************************/
+
 struct response_record *queue_node_status( struct subnet_record *subrec,
                           response_function resp_fn,
                           timeout_response_function timeout_fn,
@@ -836,7 +845,7 @@ unicast subnet. subnet is %s\n.", subrec->subnet_name ));
                p->locked = False;
                free_packet(p);
                return NULL;
-       } 
+       }
 
        if((rrec = make_response_record(subrec,           /* subnet record. */
                                        p,                     /* packet we sent. */
@@ -865,14 +874,14 @@ void reply_netbios_packet(struct packet_struct *orig_packet,
        struct nmb_packet *nmb = NULL;
        struct res_rec answers;
        struct nmb_packet *orig_nmb = &orig_packet->packet.nmb;
-       BOOL loopback_this_packet = False;
+       bool loopback_this_packet = False;
        int rr_type = RR_TYPE_NB;
        const char *packet_type = "unknown";
-  
+
        /* Check if we are sending to or from ourselves. */
-       if(ismyip(orig_packet->ip) && (orig_packet->port == global_nmb_port))
+       if(ismyip_v4(orig_packet->ip) && (orig_packet->port == global_nmb_port))
                loopback_this_packet = True;
-  
+
        nmb = &packet.packet.nmb;
 
        /* Do a partial copy of the packet. We clear the locked flag and
@@ -945,36 +954,47 @@ for id %hu\n", packet_type, nmb_namestr(&orig_nmb->question.question_name),
        nmb->header.nm_flags.bcast = False;
        nmb->header.nm_flags.trunc = False;
        nmb->header.nm_flags.authoritative = True;
-  
+
        nmb->header.rcode = rcode;
        nmb->header.qdcount = 0;
        nmb->header.ancount = 1;
        nmb->header.nscount = 0;
        nmb->header.arcount = 0;
-  
+
        memset((char*)&nmb->question,'\0',sizeof(nmb->question));
-  
+
        nmb->answers = &answers;
        memset((char*)nmb->answers,'\0',sizeof(*nmb->answers));
-  
+
        nmb->answers->rr_name  = orig_nmb->question.question_name;
        nmb->answers->rr_type  = rr_type;
        nmb->answers->rr_class = RR_CLASS_IN;
        nmb->answers->ttl      = ttl;
-  
+
        if (data && len) {
+               if (len < 0 || len > sizeof(nmb->answers->rdata)) {
+                       DEBUG(5,("reply_netbios_packet: "
+                               "invalid packet len (%d)\n",
+                               len ));
+                       return;
+               }
                nmb->answers->rdlength = len;
                memcpy(nmb->answers->rdata, data, len);
        }
-  
+
        packet.packet_type = NMB_PACKET;
+       packet.recv_fd = -1;
        /* Ensure we send out on the same fd that the original
                packet came in on to give the correct source IP address. */
-       packet.fd = orig_packet->fd;
+       if (orig_packet->send_fd != -1) {
+               packet.send_fd = orig_packet->send_fd;
+       } else {
+               packet.send_fd = orig_packet->recv_fd;
+       }
        packet.timestamp = time(NULL);
 
        debug_nmb_packet(&packet);
-  
+
        if(loopback_this_packet) {
                struct packet_struct *lo_packet;
                DEBUG(5,("reply_netbios_packet: sending packet to ourselves.\n"));
@@ -991,24 +1011,9 @@ for id %hu\n", packet_type, nmb_namestr(&orig_nmb->question.question_name),
   Queue a packet into a packet queue
 ******************************************************************/
 
-static void queue_packet(struct packet_struct *packet)
+void queue_packet(struct packet_struct *packet)
 {
-       struct packet_struct *p;
-
-       if (!packet_queue) {
-               packet->prev = NULL;
-               packet->next = NULL;
-               packet_queue = packet;
-               return;
-       }
-  
-       /* find the bottom */
-       for (p=packet_queue;p->next;p=p->next) 
-               ;
-
-       p->next = packet;
-       packet->next = NULL;
-       packet->prev = p;
+       DLIST_ADD_END(packet_queue, packet, struct packet_struct *);
 }
 
 /****************************************************************************
@@ -1021,7 +1026,7 @@ static struct subnet_record *find_subnet_for_dgram_browse_packet(struct packet_s
 
        /* Go through all the broadcast subnets and see if the mask matches. */
        for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
-               if(same_net(p->ip, subrec->bcast_ip, subrec->mask_ip))
+               if(same_net_v4(p->ip, subrec->bcast_ip, subrec->mask_ip))
                        return subrec;
        }
 
@@ -1057,7 +1062,7 @@ mismatch with our scope (%s).\n", inet_ntoa(p->ip), scope, global_scope()));
 
        pull_ascii_nstring(src_name, sizeof(src_name), dgram->source_name.name);
        if (is_myname(src_name)) {
-               DEBUG(0,("process_browse_packet: Discarding datagram from IP %s. Source name \
+               DEBUG(7,("process_browse_packet: Discarding datagram from IP %s. Source name \
 %s is one of our names !\n", inet_ntoa(p->ip), nmb_namestr(&dgram->source_name)));
                return;
        }
@@ -1105,7 +1110,7 @@ packet from %s IP %s\n", nmb_namestr(&dgram->source_name), inet_ntoa(p->ip)));
                        process_master_browser_announce(subrec, p, buf+1);
                        break;
                case ANN_BecomeBackup:
-                       /* 
+                       /*
                         * We don't currently implement this. Log it just in case.
                         */
                        debug_browse_data(buf, len);
@@ -1119,7 +1124,7 @@ command ANN_BecomeBackup from %s IP %s to %s\n", subrec->subnet_name, nmb_namest
 command code %d from %s IP %s to %s\n", subrec->subnet_name, command, nmb_namestr(&dgram->source_name),
                                inet_ntoa(p->ip), nmb_namestr(&dgram->dest_name)));
                        break;
-       } 
+       }
 }
 
 /****************************************************************************
@@ -1172,12 +1177,12 @@ command code %d from %s IP %s to %s\n", subrec->subnet_name, command, nmb_namest
   stage as subsequent processing is expensive. 
 ****************************************************************************/
 
-static BOOL listening(struct packet_struct *p,struct nmb_name *nbname)
+static bool listening(struct packet_struct *p,struct nmb_name *nbname)
 {
        struct subnet_record *subrec = NULL;
 
        for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
-               if(same_net(p->ip, subrec->bcast_ip, subrec->mask_ip))
+               if(same_net_v4(p->ip, subrec->bcast_ip, subrec->mask_ip))
                        break;
        }
 
@@ -1300,9 +1305,9 @@ packet sent to name %s from IP %s\n",
   Validate a response nmb packet.
 ****************************************************************************/
 
-static BOOL validate_nmb_response_packet( struct nmb_packet *nmb )
+static bool validate_nmb_response_packet( struct nmb_packet *nmb )
 {
-       BOOL ignore = False;
+       bool ignore = False;
 
        switch (nmb->header.opcode) {
                case NMB_NAME_REG_OPCODE:
@@ -1346,14 +1351,14 @@ static BOOL validate_nmb_response_packet( struct nmb_packet *nmb )
 
        return ignore;
 }
+
 /****************************************************************************
   Validate a request nmb packet.
 ****************************************************************************/
 
-static BOOL validate_nmb_packet( struct nmb_packet *nmb )
+static bool validate_nmb_packet( struct nmb_packet *nmb )
 {
-       BOOL ignore = False;
+       bool ignore = False;
 
        switch (nmb->header.opcode) {
                case NMB_NAME_REG_OPCODE:
@@ -1440,7 +1445,7 @@ static struct subnet_record *find_subnet_for_nmb_packet( struct packet_struct *p
 
        /* Go through all the broadcast subnets and see if the mask matches. */
        for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
-               if(same_net(p->ip, subrec->bcast_ip, subrec->mask_ip))
+               if(same_net_v4(p->ip, subrec->bcast_ip, subrec->mask_ip))
                        return subrec;
        }
 
@@ -1511,7 +1516,7 @@ not allowed.\n"));
                                        break;
                        }
                        break;
-      
+
                case NMB_NAME_RELEASE_OPCODE:
                        if(subrec == wins_server_subnet)
                                wins_process_name_release_request(subrec, p);
@@ -1550,7 +1555,7 @@ found for id = %hu. Ignoring packet.\n", nmb->header.name_trn_id));
        rrec->num_msgs++;
        /* Ensure we don't re-send the request. */
        rrec->repeat_count = 0;
-  
+
        /* Call the response received function for this packet. */
        (*rrec->resp_fn)(subrec, rrec, p);
 }
@@ -1564,10 +1569,7 @@ void run_packet_queue(void)
        struct packet_struct *p;
 
        while ((p = packet_queue)) {
-               packet_queue = p->next;
-               if (packet_queue)
-                       packet_queue->prev = NULL;
-               p->next = p->prev = NULL;
+               DLIST_REMOVE(packet_queue, p);
 
                switch (p->packet_type) {
                        case NMB_PACKET:
@@ -1583,7 +1585,7 @@ void run_packet_queue(void)
                }
                free_packet(p);
        }
-} 
+}
 
 /*******************************************************************
  Retransmit or timeout elements from all the outgoing subnet response
@@ -1600,9 +1602,11 @@ void retransmit_or_expire_response_records(time_t t)
        for (subrec = FIRST_SUBNET; subrec; subrec = get_next_subnet_maybe_unicast_or_wins_server(subrec)) {
                struct response_record *rrec, *nextrrec;
 
+  restart:
+
                for (rrec = subrec->responselist; rrec; rrec = nextrrec) {
                        nextrrec = rrec->next;
-   
+
                        if (rrec->repeat_time <= t) {
                                if (rrec->repeat_count > 0) {
                                        /* Resend while we have a non-zero repeat_count. */
@@ -1638,6 +1642,9 @@ on subnet %s\n", rrec->response_id, inet_ntoa(rrec->packet->ip), subrec->subnet_
                                                                        no timeout function. */
                                                        remove_response_record(subrec, rrec);
                                                }
+                                               /* We have changed subrec->responselist,
+                                                * restart from the beginning of this list. */
+                                               goto restart;
                                        } /* !rrec->in_expitation_processing */
                                } /* rrec->repeat_count > 0 */
                        } /* rrec->repeat_time <= t */
@@ -1650,7 +1657,7 @@ on subnet %s\n", rrec->response_id, inet_ntoa(rrec->packet->ip), subrec->subnet_
   plus the broadcast sockets.
 ***************************************************************************/
 
-static BOOL create_listen_fdset(fd_set **ppset, int **psock_array, int *listen_number, int *maxfd)
+static bool create_listen_fdset(fd_set **ppset, int **psock_array, int *listen_number, int *maxfd)
 {
        int *sock_array = NULL;
        struct subnet_record *subrec = NULL;
@@ -1663,66 +1670,167 @@ static BOOL create_listen_fdset(fd_set **ppset, int **psock_array, int *listen_n
                return True;
        }
 
+       /* The Client* sockets */
+       count++;
+
        /* Check that we can add all the fd's we need. */
        for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
                count++;
 
-       if((count*2) + 2 > FD_SETSIZE) {
+       /* each interface gets 4 sockets */
+       count *= 4;
+
+       if(count > FD_SETSIZE) {
                DEBUG(0,("create_listen_fdset: Too many file descriptors needed (%d). We can \
-only use %d.\n", (count*2) + 2, FD_SETSIZE));
+only use %d.\n", count, FD_SETSIZE));
                SAFE_FREE(pset);
                return True;
        }
 
-       if((sock_array = SMB_MALLOC_ARRAY(int, (count*2) + 2)) == NULL) {
-               DEBUG(0,("create_listen_fdset: malloc fail for socket array.\n"));
+       if((sock_array = SMB_MALLOC_ARRAY(int, count)) == NULL) {
+               DEBUG(0,("create_listen_fdset: malloc fail for socket array. size %d\n", count));
                SAFE_FREE(pset);
                return True;
        }
 
        FD_ZERO(pset);
 
-       /* Add in the broadcast socket on 137. */
+       /* Add in the lp_socket_address() interface on 137. */
        FD_SET(ClientNMB,pset);
        sock_array[num++] = ClientNMB;
        *maxfd = MAX( *maxfd, ClientNMB);
 
+       /* the lp_socket_address() interface has only one socket */
+       sock_array[num++] = -1;
+
        /* Add in the 137 sockets on all the interfaces. */
        for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
                FD_SET(subrec->nmb_sock,pset);
                sock_array[num++] = subrec->nmb_sock;
                *maxfd = MAX( *maxfd, subrec->nmb_sock);
+
+               sock_array[num++] = subrec->nmb_bcast;
+               if (subrec->nmb_bcast != -1) {
+                       FD_SET(subrec->nmb_bcast,pset);
+                       *maxfd = MAX( *maxfd, subrec->nmb_bcast);
+               }
        }
 
-       /* Add in the broadcast socket on 138. */
+       /* Add in the lp_socket_address() interface on 138. */
        FD_SET(ClientDGRAM,pset);
        sock_array[num++] = ClientDGRAM;
        *maxfd = MAX( *maxfd, ClientDGRAM);
 
+       /* the lp_socket_address() interface has only one socket */
+       sock_array[num++] = -1;
+
        /* Add in the 138 sockets on all the interfaces. */
        for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
                FD_SET(subrec->dgram_sock,pset);
                sock_array[num++] = subrec->dgram_sock;
                *maxfd = MAX( *maxfd, subrec->dgram_sock);
+
+               sock_array[num++] = subrec->dgram_bcast;
+               if (subrec->dgram_bcast != -1) {
+                       FD_SET(subrec->dgram_bcast,pset);
+                       *maxfd = MAX( *maxfd, subrec->dgram_bcast);
+               }
        }
 
-       *listen_number = (count*2) + 2;
+       *listen_number = count;
 
        SAFE_FREE(*ppset);
        SAFE_FREE(*psock_array);
 
        *ppset = pset;
        *psock_array = sock_array;
+
        return False;
 }
 
+/****************************************************************************
+ List of packets we're processing this select.
+***************************************************************************/
+
+struct processed_packet {
+       struct processed_packet *next;
+       struct processed_packet *prev;
+       enum packet_type packet_type;
+       struct in_addr ip;
+       int packet_id;
+};
+
+/****************************************************************************
+ Have we seen this before ?
+***************************************************************************/
+
+static bool is_processed_packet(struct processed_packet *processed_packet_list,
+                               struct packet_struct *packet)
+{
+       struct processed_packet *p = NULL;
+
+       for (p = processed_packet_list; p; p = p->next) {
+               if (ip_equal_v4(p->ip, packet->ip) && p->packet_type == packet->packet_type) {
+                       if ((p->packet_type == NMB_PACKET) &&
+                               (p->packet_id ==
+                                       packet->packet.nmb.header.name_trn_id)) {
+                               return true;
+                       } else if ((p->packet_type == DGRAM_PACKET) &&
+                               (p->packet_id ==
+                                       packet->packet.dgram.header.dgm_id)) {
+                               return true;
+                       }
+               }
+       }
+       return false;
+}
+
+/****************************************************************************
+ Keep a list of what we've seen before.
+***************************************************************************/
+
+static bool store_processed_packet(struct processed_packet **pp_processed_packet_list,
+                               struct packet_struct *packet)
+{
+       struct processed_packet *p = SMB_MALLOC_P(struct processed_packet);
+       if (!p) {
+               return false;
+       }
+       p->packet_type = packet->packet_type;
+       p->ip = packet->ip;
+       if (packet->packet_type == NMB_PACKET) {
+               p->packet_id = packet->packet.nmb.header.name_trn_id;
+       } else if (packet->packet_type == DGRAM_PACKET) {
+               p->packet_id = packet->packet.dgram.header.dgm_id;
+       } else {
+               return false;
+       }
+
+       DLIST_ADD(*pp_processed_packet_list, p);
+       return true;
+}
+
+/****************************************************************************
+ Throw away what we've seen before.
+***************************************************************************/
+
+static void free_processed_packet_list(struct processed_packet **pp_processed_packet_list)
+{
+       struct processed_packet *p = NULL, *next = NULL;
+
+       for (p = *pp_processed_packet_list; p; p = next) {
+               next = p->next;
+               DLIST_REMOVE(*pp_processed_packet_list, p);
+               SAFE_FREE(p);
+       }
+}
+
 /****************************************************************************
   Listens for NMB or DGRAM packets, and queues them.
   return True if the socket is dead
 ***************************************************************************/
 
-BOOL listen_for_packets(BOOL run_election)
+bool listen_for_packets(bool run_election)
 {
        static fd_set *listen_set = NULL;
        static int listen_number = 0;
@@ -1737,6 +1845,7 @@ BOOL listen_for_packets(BOOL run_election)
 #ifndef SYNC_DNS
        int dns_fd;
 #endif
+       struct processed_packet *processed_packet_list = NULL;
 
        if(listen_set == NULL || rescan_listen_set) {
                if(create_listen_fdset(&listen_set, &sock_array, &listen_number, &maxfd)) {
@@ -1757,7 +1866,12 @@ BOOL listen_for_packets(BOOL run_election)
        }
 #endif
 
-       /* 
+       /* Process a signal and timer events now... */
+       if (run_events(nmbd_event_context(), 0, NULL, NULL)) {
+               return False;
+       }
+
+       /*
         * During elections and when expecting a netbios response packet we
         * need to send election packets at tighter intervals.
         * Ideally it needs to be the interval (in ms) between time now and
@@ -1773,28 +1887,13 @@ BOOL listen_for_packets(BOOL run_election)
                                         &r_fds, &w_fds, &timeout, &maxfd);
        }
 
-       if (timeval_is_zero(&timeout)) {
-               /* Process a timed event now... */
-               if (run_events(nmbd_event_context(), 0, NULL, NULL)) {
-                       return False;
-               }
-       }
-       
-       /* Prepare for the select - allow certain signals. */
-
-       BlockSignals(False, SIGTERM);
-
        selrtn = sys_select(maxfd+1,&r_fds,&w_fds,NULL,&timeout);
 
-       /* We can only take signals when we are in the select - block them again here. */
-
-       BlockSignals(True, SIGTERM);
-
-       if(selrtn == -1) {
+       if (run_events(nmbd_event_context(), selrtn, &r_fds, &w_fds)) {
                return False;
        }
 
-       if (run_events(nmbd_event_context(), selrtn, &r_fds, &w_fds)) {
+       if (selrtn == -1) {
                return False;
        }
 
@@ -1805,61 +1904,101 @@ BOOL listen_for_packets(BOOL run_election)
 #endif
 
        for(i = 0; i < listen_number; i++) {
+               enum packet_type packet_type;
+               struct packet_struct *packet;
+               const char *packet_name;
+               int client_fd;
+               int client_port;
+
+               if (sock_array[i] == -1) {
+                       continue;
+               }
+
+               if (!FD_ISSET(sock_array[i],&r_fds)) {
+                       continue;
+               }
+
                if (i < (listen_number/2)) {
-                       /* Processing a 137 socket. */
-                       if (FD_ISSET(sock_array[i],&r_fds)) {
-                               struct packet_struct *packet = read_packet(sock_array[i], NMB_PACKET);
-                               if (packet) {
-                                       /*
-                                        * If we got a packet on the broadcast socket and interfaces
-                                        * only is set then check it came from one of our local nets. 
-                                        */
-                                       if(lp_bind_interfaces_only() && (sock_array[i] == ClientNMB) && 
-                                                               (!is_local_net(packet->ip))) {
-                                               DEBUG(7,("discarding nmb packet sent to broadcast socket from %s:%d\n",
-                                                       inet_ntoa(packet->ip),packet->port));     
-                                               free_packet(packet);
-                                       } else if ((ip_equal(loopback_ip, packet->ip) || 
-                                                               ismyip(packet->ip)) && packet->port == global_nmb_port &&
-                                                               packet->packet.nmb.header.nm_flags.bcast) {
-                                               DEBUG(7,("discarding own bcast packet from %s:%d\n",
-                                                       inet_ntoa(packet->ip),packet->port));     
-                                               free_packet(packet);
-                                       } else {
-                                               /* Save the file descriptor this packet came in on. */
-                                               packet->fd = sock_array[i];
-                                               queue_packet(packet);
-                                       }
-                               }
-                       }
+                       /* Port 137 */
+                       packet_type = NMB_PACKET;
+                       packet_name = "nmb";
+                       client_fd = ClientNMB;
+                       client_port = global_nmb_port;
                } else {
-                       /* Processing a 138 socket. */
-                               if (FD_ISSET(sock_array[i],&r_fds)) {
-                               struct packet_struct *packet = read_packet(sock_array[i], DGRAM_PACKET);
-                               if (packet) {
-                                       /*
-                                        * If we got a packet on the broadcast socket and interfaces
-                                        * only is set then check it came from one of our local nets. 
-                                        */
-                                       if(lp_bind_interfaces_only() && (sock_array[i] == ClientDGRAM) && 
-                                                               (!is_local_net(packet->ip))) {
-                                               DEBUG(7,("discarding dgram packet sent to broadcast socket from %s:%d\n",
-                                               inet_ntoa(packet->ip),packet->port));     
-                                               free_packet(packet);
-                                       } else if ((ip_equal(loopback_ip, packet->ip) || 
-                                                       ismyip(packet->ip)) && packet->port == DGRAM_PORT) {
-                                               DEBUG(7,("discarding own dgram packet from %s:%d\n",
-                                                       inet_ntoa(packet->ip),packet->port));     
-                                               free_packet(packet);
-                                       } else {
-                                               /* Save the file descriptor this packet came in on. */
-                                               packet->fd = sock_array[i];
-                                               queue_packet(packet);
-                                       }
-                               }
+                       /* Port 138 */
+                       packet_type = DGRAM_PACKET;
+                       packet_name = "dgram";
+                       client_fd = ClientDGRAM;
+                       client_port = DGRAM_PORT;
+               }
+
+               packet = read_packet(sock_array[i], packet_type);
+               if (!packet) {
+                       continue;
+               }
+
+               /*
+                * If we got a packet on the broadcast socket and interfaces
+                * only is set then check it came from one of our local nets.
+                */
+               if (lp_bind_interfaces_only() &&
+                   (sock_array[i] == client_fd) &&
+                   (!is_local_net_v4(packet->ip))) {
+                       DEBUG(7,("discarding %s packet sent to broadcast socket from %s:%d\n",
+                               packet_name, inet_ntoa(packet->ip), packet->port));
+                       free_packet(packet);
+                       continue;
+               }
+
+               if ((is_loopback_ip_v4(packet->ip) || ismyip_v4(packet->ip)) &&
+                   packet->port == client_port)
+               {
+                       if (client_port == DGRAM_PORT) {
+                               DEBUG(7,("discarding own dgram packet from %s:%d\n",
+                                       inet_ntoa(packet->ip),packet->port));
+                               free_packet(packet);
+                               continue;
                        }
-               } /* end processing 138 socket. */
-       } /* end for */
+
+                       if (packet->packet.nmb.header.nm_flags.bcast) {
+                               DEBUG(7,("discarding own nmb bcast packet from %s:%d\n",
+                                       inet_ntoa(packet->ip),packet->port));
+                               free_packet(packet);
+                               continue;
+                       }
+               }
+
+
+               if (is_processed_packet(processed_packet_list, packet)) {
+                       DEBUG(7,("discarding duplicate packet from %s:%d\n",
+                               inet_ntoa(packet->ip),packet->port));
+                       free_packet(packet);
+                       continue;
+               }
+
+               store_processed_packet(&processed_packet_list, packet);
+
+               /*
+                * 0,2,4,... are unicast sockets
+                * 1,3,5,... are broadcast sockets
+                *
+                * on broadcast socket we only receive packets
+                * and send replies via the unicast socket.
+                *
+                * 0,1 and 2,3 and ... belong together.
+                */
+               if ((i % 2) != 0) {
+                       /* this is a broadcast socket */
+                       packet->send_fd = sock_array[i-1];
+               } else {
+                       /* this is already a unicast socket */
+                       packet->send_fd = sock_array[i];
+               }
+
+               queue_packet(packet);
+       }
+
+       free_processed_packet_list(&processed_packet_list);
        return False;
 }
 
@@ -1867,13 +2006,13 @@ BOOL listen_for_packets(BOOL run_election)
   Construct and send a netbios DGRAM.
 **************************************************************************/
 
-BOOL send_mailslot(BOOL unique, const char *mailslot,char *buf, size_t len,
+bool send_mailslot(bool unique, const char *mailslot,char *buf, size_t len,
                    const char *srcname, int src_type,
                    const char *dstname, int dest_type,
                    struct in_addr dest_ip,struct in_addr src_ip,
                   int dest_port)
 {
-       BOOL loopback_this_packet = False;
+       bool loopback_this_packet = False;
        struct packet_struct p;
        struct dgram_packet *dgram = &p.packet.dgram;
        char *ptr,*p2;
@@ -1881,13 +2020,13 @@ BOOL send_mailslot(BOOL unique, const char *mailslot,char *buf, size_t len,
 
        memset((char *)&p,'\0',sizeof(p));
 
-       if(ismyip(dest_ip) && (dest_port == DGRAM_PORT)) /* Only if to DGRAM_PORT */
+       if(ismyip_v4(dest_ip) && (dest_port == DGRAM_PORT)) /* Only if to DGRAM_PORT */
                loopback_this_packet = True;
 
        /* generate_name_trn_id(); */ /* Not used, so gone, RJS */
 
        /* DIRECT GROUP or UNIQUE datagram. */
-       dgram->header.msg_type = unique ? 0x10 : 0x11; 
+       dgram->header.msg_type = unique ? 0x10 : 0x11;
        dgram->header.flags.node_type = M_NODE;
        dgram->header.flags.first = True;
        dgram->header.flags.more = False;
@@ -1896,7 +2035,7 @@ BOOL send_mailslot(BOOL unique, const char *mailslot,char *buf, size_t len,
        dgram->header.source_port = DGRAM_PORT;
        dgram->header.dgm_length = 0; /* Let build_dgram() handle this. */
        dgram->header.packet_offset = 0;
-  
+
        make_nmb_name(&dgram->source_name,srcname,src_type);
        make_nmb_name(&dgram->dest_name,dstname,dest_type);
 
@@ -1905,7 +2044,13 @@ BOOL send_mailslot(BOOL unique, const char *mailslot,char *buf, size_t len,
        /* Setup the smb part. */
        ptr -= 4; /* XXX Ugliness because of handling of tcp SMB length. */
        memcpy(tmp,ptr,4);
-       set_message(NULL,ptr,17,strlen(mailslot) + 1 + len,True);
+
+       if (smb_size + 17*2 + strlen(mailslot) + 1 + len > MAX_DGRAM_SIZE) {
+               DEBUG(0, ("send_mailslot: Cannot write beyond end of packet\n"));
+               return false;
+       }
+
+       cli_set_message(ptr,17,strlen(mailslot) + 1 + len,True);
        memcpy(ptr,tmp,4);
 
        SCVAL(ptr,smb_com,SMBtrans);
@@ -1919,12 +2064,14 @@ BOOL send_mailslot(BOOL unique, const char *mailslot,char *buf, size_t len,
        p2 = smb_buf(ptr);
        safe_strcpy_base(p2, mailslot, dgram->data, sizeof(dgram->data));
        p2 = skip_string(ptr,MAX_DGRAM_SIZE,p2);
-  
+
        if (((p2+len) > dgram->data+sizeof(dgram->data)) || ((p2+len) < p2)) {
                DEBUG(0, ("send_mailslot: Cannot write beyond end of packet\n"));
                return False;
        } else {
-               memcpy(p2,buf,len);
+               if (len) {
+                       memcpy(p2,buf,len);
+               }
                p2 += len;
        }
 
@@ -1932,7 +2079,8 @@ BOOL send_mailslot(BOOL unique, const char *mailslot,char *buf, size_t len,
 
        p.ip = dest_ip;
        p.port = dest_port;
-       p.fd = find_subnet_mailslot_fd_for_address( src_ip );
+       p.recv_fd = -1;
+       p.send_fd = find_subnet_mailslot_fd_for_address( src_ip );
        p.timestamp = time(NULL);
        p.packet_type = DGRAM_PACKET;