s3:nmbd: also listen explicit on the subnet broadcast addresses
authorStefan Metzmacher <metze@samba.org>
Thu, 28 Jan 2010 10:04:05 +0000 (11:04 +0100)
committerStefan Metzmacher <metze@samba.org>
Mon, 8 Feb 2010 17:35:10 +0000 (18:35 +0100)
And send replies always via the unicast address of the subnet.

This behavior is off by default (as before)
and can be enabled with "nmbd:bind explicit broadcast = yes".

metze

source3/include/nameserv.h
source3/libsmb/namequery.c
source3/libsmb/nmblib.c
source3/nmbd/nmbd.c
source3/nmbd/nmbd_packets.c
source3/nmbd/nmbd_subnetdb.c

index 496d87e2dbace6ceb90cbc41958fc3d9f6aeb1e3..53ffd6faec3c4a069c884688a45e767b9c60e1bb 100644 (file)
@@ -434,7 +434,9 @@ struct subnet_record {
        struct in_addr mask_ip;
        struct in_addr myip;
        int nmb_sock;               /* socket to listen for unicast 137. */
+       int nmb_bcast;              /* socket to listen for broadcast 137. */
        int dgram_sock;             /* socket to listen for unicast 138. */
+       int dgram_bcast;            /* socket to listen for broadcast 138. */
 };
 
 /* A resource record. */
@@ -530,7 +532,8 @@ struct packet_struct
        bool locked;
        struct in_addr ip;
        int port;
-       int fd;
+       int recv_fd;
+       int send_fd;
        time_t timestamp;
        enum packet_type packet_type;
        union {
index a6fc612a0f425d77a64ef0f4b3439aa582d0c7f4..be038ecdad4e4d7598c3506032796e6a9b629f7b 100644 (file)
@@ -289,7 +289,8 @@ NODE_STATUS_STRUCT *node_status_query(int fd,
 
        p.ip = ((const struct sockaddr_in *)to_ss)->sin_addr;
        p.port = NMB_PORT;
-       p.fd = fd;
+       p.recv_fd = -1;
+       p.send_fd = fd;
        p.timestamp = time(NULL);
        p.packet_type = NMB_PACKET;
 
@@ -698,7 +699,8 @@ struct sockaddr_storage *name_query(int fd,
 
        p.ip = ((struct sockaddr_in *)to_ss)->sin_addr;
        p.port = NMB_PORT;
-       p.fd = fd;
+       p.recv_fd = -1;
+       p.send_fd = fd;
        p.timestamp = time(NULL);
        p.packet_type = NMB_PACKET;
 
index 5f3eda44fe2960300e2e2e79fc42016b550c3c57..1a2106675b47c17575ab34703bc9603a18301531 100644 (file)
@@ -601,6 +601,8 @@ static struct packet_struct *copy_nmb_packet(struct packet_struct *packet)
 
        /* Ensure this copy is not locked. */
        pkt_copy->locked = False;
+       pkt_copy->recv_fd = -1;
+       pkt_copy->send_fd = -1;
 
        /* Ensure this copy has no resource records. */
        nmb = &packet->packet.nmb;
@@ -666,6 +668,8 @@ static struct packet_struct *copy_dgram_packet(struct packet_struct *packet)
 
        /* Ensure this copy is not locked. */
        pkt_copy->locked = False;
+       pkt_copy->recv_fd = -1;
+       pkt_copy->send_fd = -1;
 
        /* There are no additional pointers in a dgram packet,
                we are finished. */
@@ -791,7 +795,8 @@ struct packet_struct *read_packet(int fd,enum packet_type packet_type)
        if (!packet)
                return NULL;
 
-       packet->fd = fd;
+       packet->recv_fd = fd;
+       packet->send_fd = -1;
 
        DEBUG(5,("Received a packet of len %d from (%s) port %d\n",
                 length, inet_ntoa(packet->ip), packet->port ) );
@@ -1075,7 +1080,7 @@ bool send_packet(struct packet_struct *p)
        if (!len)
                return(False);
 
-       return(send_udp(p->fd,buf,len,p->ip,p->port));
+       return(send_udp(p->send_fd,buf,len,p->ip,p->port));
 }
 
 /****************************************************************************
index 961e9307280865475d8daa5cdb296b8d69793cd7..3b51a78dfe8b29a7f5d69aab0ae5ff92d7081b41 100644 (file)
@@ -443,13 +443,14 @@ static void msg_nmbd_send_packet(struct messaging_context *msg,
        local_ip = &((const struct sockaddr_in *)pss)->sin_addr;
        subrec = FIRST_SUBNET;
 
-       p->fd = (p->packet_type == NMB_PACKET) ?
+       p->recv_fd = -1;
+       p->send_fd = (p->packet_type == NMB_PACKET) ?
                subrec->nmb_sock : subrec->dgram_sock;
 
        for (subrec = FIRST_SUBNET; subrec != NULL;
             subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
                if (ip_equal_v4(*local_ip, subrec->myip)) {
-                       p->fd = (p->packet_type == NMB_PACKET) ?
+                       p->send_fd = (p->packet_type == NMB_PACKET) ?
                                subrec->nmb_sock : subrec->dgram_sock;
                        break;
                }
index 6136c6d1711ee19037335a38ad4295d42d368b69..013ebf65897cbd95419f752040a8f260973b3c00 100644 (file)
@@ -207,7 +207,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;
@@ -258,7 +259,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;
 }
@@ -743,7 +745,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;
                }
        }
@@ -979,9 +981,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);
@@ -1679,50 +1686,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);
@@ -1811,61 +1842,90 @@ 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 137 */
+                       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;
+                       }
+               }
+
+               /*
+                * 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);
+       }
+
        return False;
 }
 
@@ -1946,7 +2006,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;
 
index 13bc931863b47c07fcb99844cc1451464ea0a530..96d7b3211efd0e3001d9276a46f105b01aa92b82 100644 (file)
@@ -76,18 +76,21 @@ static struct subnet_record *make_subnet(const char *name, enum subnet_type type
                                         struct in_addr mask_ip)
 {
        struct subnet_record *subrec = NULL;
-       int nmb_sock, dgram_sock;
+       int nmb_sock = -1;
+       int dgram_sock = -1;
+       int nmb_bcast = -1;
+       int dgram_bcast = -1;
+       bool bind_bcast = lp_parm_bool(-1, "nmbd", "bind explicit broadcast", false);
 
        /* Check if we are creating a non broadcast subnet - if so don't create
                sockets.  */
 
-       if(type != NORMAL_SUBNET) {
-               nmb_sock = -1;
-               dgram_sock = -1;
-       } else {
+       if (type == NORMAL_SUBNET) {
                struct sockaddr_storage ss;
+               struct sockaddr_storage ss_bcast;
 
                in_addr_to_sockaddr_storage(&ss, myip);
+               in_addr_to_sockaddr_storage(&ss_bcast, bcast_ip);
 
                /*
                 * Attempt to open the sockets on port 137/138 for this interface
@@ -95,60 +98,74 @@ static struct subnet_record *make_subnet(const char *name, enum subnet_type type
                 * Fail the subnet creation if this fails.
                 */
 
-               if((nmb_sock = open_socket_in(SOCK_DGRAM, global_nmb_port,0, &ss,true)) == -1) {
-                       if( DEBUGLVL( 0 ) ) {
-                               Debug1( "nmbd_subnetdb:make_subnet()\n" );
-                               Debug1( "  Failed to open nmb socket on interface %s ", inet_ntoa(myip) );
-                               Debug1( "for port %d.  ", global_nmb_port );
-                               Debug1( "Error was %s\n", strerror(errno) );
+               nmb_sock = open_socket_in(SOCK_DGRAM, global_nmb_port,
+                                         0, &ss, true);
+               if (nmb_sock == -1) {
+                       DEBUG(0,   ("nmbd_subnetdb:make_subnet()\n"));
+                       DEBUGADD(0,("  Failed to open nmb socket on interface %s ",
+                                   inet_ntoa(myip)));
+                       DEBUGADD(0,("for port %d.  ", global_nmb_port));
+                       DEBUGADD(0,("Error was %s\n", strerror(errno)));
+                       goto failed;
+               }
+               set_socket_options(nmb_sock,"SO_BROADCAST");
+               set_blocking(nmb_sock, false);
+
+               if (bind_bcast) {
+                       nmb_bcast = open_socket_in(SOCK_DGRAM, global_nmb_port,
+                                                  0, &ss_bcast, true);
+                       if (nmb_bcast == -1) {
+                               DEBUG(0,   ("nmbd_subnetdb:make_subnet()\n"));
+                               DEBUGADD(0,("  Failed to open nmb bcast socket on interface %s ",
+                                           inet_ntoa(bcast_ip)));
+                               DEBUGADD(0,("for port %d.  ", global_nmb_port));
+                               DEBUGADD(0,("Error was %s\n", strerror(errno)));
+                               goto failed;
                        }
-                       return NULL;
+                       set_socket_options(nmb_bcast, "SO_BROADCAST");
+                       set_blocking(nmb_bcast, false);
                }
 
-               if((dgram_sock = open_socket_in(SOCK_DGRAM,DGRAM_PORT,3, &ss, true)) == -1) {
-                       if( DEBUGLVL( 0 ) ) {
-                               Debug1( "nmbd_subnetdb:make_subnet()\n" );
-                               Debug1( "  Failed to open dgram socket on interface %s ", inet_ntoa(myip) );
-                               Debug1( "for port %d.  ", DGRAM_PORT );
-                               Debug1( "Error was %s\n", strerror(errno) );
+               dgram_sock = open_socket_in(SOCK_DGRAM, DGRAM_PORT,
+                                           3, &ss, true);
+               if (dgram_sock == -1) {
+                       DEBUG(0,   ("nmbd_subnetdb:make_subnet()\n"));
+                       DEBUGADD(0,("  Failed to open dgram socket on interface %s ",
+                                   inet_ntoa(myip)));
+                       DEBUGADD(0,("for port %d.  ", DGRAM_PORT));
+                       DEBUGADD(0,("Error was %s\n", strerror(errno)));
+                       goto failed;
+               }
+               set_socket_options(dgram_sock, "SO_BROADCAST");
+               set_blocking(dgram_sock, false);
+
+               if (bind_bcast) {
+                       dgram_bcast = open_socket_in(SOCK_DGRAM, DGRAM_PORT,
+                                                    3, &ss_bcast, true);
+                       if (dgram_bcast == -1) {
+                               DEBUG(0,   ("nmbd_subnetdb:make_subnet()\n"));
+                               DEBUGADD(0,("  Failed to open dgram bcast socket on interface %s ",
+                                           inet_ntoa(bcast_ip)));
+                               DEBUGADD(0,("for port %d.  ", DGRAM_PORT));
+                               DEBUGADD(0,("Error was %s\n", strerror(errno)));
+                               goto failed;
                        }
-                       return NULL;
+                       set_socket_options(dgram_bcast, "SO_BROADCAST");
+                       set_blocking(dgram_bcast, false);
                }
-
-               /* Make sure we can broadcast from these sockets. */
-               set_socket_options(nmb_sock,"SO_BROADCAST");
-               set_socket_options(dgram_sock,"SO_BROADCAST");
-
-               /* Set them non-blocking. */
-               set_blocking(nmb_sock, False);
-               set_blocking(dgram_sock, False);
        }
 
        subrec = SMB_MALLOC_P(struct subnet_record);
        if (!subrec) {
                DEBUG(0,("make_subnet: malloc fail !\n"));
-               if (nmb_sock != -1) {
-                       close(nmb_sock);
-               }
-               if (dgram_sock != -1) {
-                       close(dgram_sock);
-               }
-               return(NULL);
+               goto failed;
        }
   
        ZERO_STRUCTP(subrec);
 
        if((subrec->subnet_name = SMB_STRDUP(name)) == NULL) {
                DEBUG(0,("make_subnet: malloc fail for subnet name !\n"));
-               if (nmb_sock != -1) {
-                       close(nmb_sock);
-               }
-               if (dgram_sock != -1) {
-                       close(dgram_sock);
-               }
-               ZERO_STRUCTP(subrec);
-               SAFE_FREE(subrec);
-               return(NULL);
+               goto failed;
        }
 
        DEBUG(2, ("making subnet name:%s ", name ));
@@ -163,9 +180,27 @@ static struct subnet_record *make_subnet(const char *name, enum subnet_type type
        subrec->myip = myip;
        subrec->type = type;
        subrec->nmb_sock = nmb_sock;
+       subrec->nmb_bcast = nmb_bcast;
        subrec->dgram_sock = dgram_sock;
-  
+       subrec->dgram_bcast = dgram_bcast;
+
        return subrec;
+
+failed:
+       SAFE_FREE(subrec);
+       if (nmb_sock != -1) {
+               close(nmb_sock);
+       }
+       if (nmb_bcast != -1) {
+               close(nmb_bcast);
+       }
+       if (dgram_sock != -1) {
+               close(dgram_sock);
+       }
+       if (dgram_bcast != -1) {
+               close(dgram_bcast);
+       }
+       return NULL;
 }
 
 /****************************************************************************