samba: share select wrappers.
[samba.git] / source3 / nmbd / nmbd_packets.c
index 349d36ce70df1a5d2ccc3e888076a4962273b3a3..5d5a67bf62be2dcfddba4d1a2ff9e1adb04bea83 100644 (file)
@@ -21,6 +21,8 @@
 */
 
 #include "includes.h"
+#include "nmbd/nmbd.h"
+#include "../lib/util/select.h"
 
 extern int ClientNMB;
 extern int ClientDGRAM;
@@ -28,8 +30,6 @@ extern int global_nmb_port;
 
 extern int num_response_packets;
 
-static void queue_packet(struct packet_struct *packet);
-
 bool rescan_listen_set = False;
 
 
@@ -209,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;
@@ -260,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;
 }
@@ -478,7 +480,7 @@ struct response_record *queue_register_name( struct subnet_record *subrec,
                return NULL;
 
        in_addr_to_sockaddr_storage(&ss, subrec->bcast_ip);
-       pss = iface_ip(&ss);
+       pss = iface_ip((struct sockaddr *)&ss);
        if (!pss || pss->ss_family != AF_INET) {
                p->locked = False;
                free_packet(p);
@@ -745,7 +747,7 @@ struct response_record *queue_query_name( struct subnet_record *subrec,
                        }
 
                        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;
                }
        }
@@ -981,9 +983,14 @@ for id %hu\n", packet_type, nmb_namestr(&orig_nmb->question.question_name),
        }
 
        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);
@@ -1004,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 *);
 }
 
 /****************************************************************************
@@ -1070,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;
        }
@@ -1577,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:
@@ -1613,6 +1602,8 @@ 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;
 
@@ -1651,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 */
@@ -1676,50 +1670,74 @@ 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);
@@ -1730,6 +1748,83 @@ only use %d.\n", (count*2) + 2, FD_SETSIZE));
        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
@@ -1750,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)) {
@@ -1770,6 +1866,11 @@ 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.
@@ -1786,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;
        }
 
@@ -1818,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_v4(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 ((is_loopback_ip_v4(packet->ip) || 
-                                                               ismyip_v4(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_v4(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 ((is_loopback_ip_v4(packet->ip) || 
-                                                       ismyip_v4(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;
+                       }
+
+                       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;
                        }
-               } /* end processing 138 socket. */
-       } /* end for */
+               }
+
+
+               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;
 }
 
@@ -1924,7 +2050,7 @@ bool send_mailslot(bool unique, const char *mailslot,char *buf, size_t len,
                return false;
        }
 
-       set_message(ptr,17,strlen(mailslot) + 1 + len,True);
+       cli_set_message(ptr,17,strlen(mailslot) + 1 + len,True);
        memcpy(ptr,tmp,4);
 
        SCVAL(ptr,smb_com,SMBtrans);
@@ -1943,7 +2069,9 @@ bool send_mailslot(bool unique, const char *mailslot,char *buf, size_t len,
                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;
        }
 
@@ -1951,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;