Fix the unexpected.tdb database problem. Change nmbd to store the
[samba.git] / source3 / libsmb / unexpected.c
index 7f864957a73f001f3b5fa230097173a9f33d5df9..0f4227de1657253553464ec5ce47a2829bf4bc03 100644 (file)
@@ -29,6 +29,117 @@ struct unexpected_key {
        int count;
 };
 
+struct pending_unexpected {
+       struct pending_unexpected *prev, *next;
+       enum packet_type packet_type;
+       int id;
+       time_t timeout;
+};
+
+static struct pending_unexpected *pu_list;
+
+/****************************************************************************
+ This function is called when nmbd has received an unexpected packet.
+ It checks against the list of outstanding packet transaction id's
+ to see if it should be stored in the unexpected.tdb.
+**************************************************************************/
+
+static struct pending_unexpected *find_unexpected_packet(struct packet_struct *p)
+{
+       struct pending_unexpected *pu;
+
+       if (!p) {
+               return NULL;
+       }
+
+       for (pu = pu_list; pu; pu = pu->next) {
+               if (pu->packet_type == p->packet_type) {
+                       int id = (p->packet_type == DGRAM_PACKET) ?
+                               p->packet.dgram.header.dgm_id :
+                               p->packet.nmb.header.name_trn_id;
+                       if (id == pu->id) {
+                               DEBUG(10,("find_unexpected_packet: found packet "
+                                       "with id = %d\n", pu->id ));
+                               return pu;
+                       }
+               }
+       }
+
+       return NULL;
+}
+
+
+/****************************************************************************
+ This function is called when nmbd has been given a packet to send out.
+ It stores a list of outstanding packet transaction id's and the timeout
+ when they should be removed.
+**************************************************************************/
+
+bool store_outstanding_send_packet(struct packet_struct *p)
+{
+       struct pending_unexpected *pu = NULL;
+
+       if (!p) {
+               return false;
+       }
+
+       pu = find_unexpected_packet(p);
+       if (pu) {
+               /* This is a resend, and we haven't received a
+                  reply yet ! Ignore it. */
+               return false;
+       }
+
+       pu = SMB_MALLOC_P(struct pending_unexpected);
+       if (!pu || !p) {
+               return false;
+       }
+
+       ZERO_STRUCTP(pu);
+       pu->packet_type = p->packet_type;
+       pu->id = (p->packet_type == DGRAM_PACKET) ?
+                       p->packet.dgram.header.dgm_id :
+                       p->packet.nmb.header.name_trn_id;
+       pu->timeout = time(NULL) + 15;
+
+       DLIST_ADD_END(pu_list, pu, struct pending_unexpected *);
+
+       DEBUG(10,("store_outstanding_unexpected_packet: storing packet "
+               "with id = %d\n", pu->id ));
+
+       return true;
+}
+
+/****************************************************************************
+ Return true if this is a reply to a packet we were requested to send.
+**************************************************************************/
+
+bool is_requested_send_packet(struct packet_struct *p)
+{
+       return (find_unexpected_packet(p) != NULL);
+}
+
+/****************************************************************************
+ This function is called when nmbd has received an unexpected packet.
+ It checks against the list of outstanding packet transaction id's
+ to see if it should be stored in the unexpected.tdb. Don't store if
+ not found.
+**************************************************************************/
+
+static bool should_store_unexpected_packet(struct packet_struct *p)
+{
+       struct pending_unexpected *pu = find_unexpected_packet(p);
+
+       if (!pu) {
+               return false;
+       }
+
+       /* Remove the outstanding entry. */
+       DLIST_REMOVE(pu_list, pu);
+       SAFE_FREE(pu);
+       return true;
+}
+
 /****************************************************************************
  All unexpected packets are passed in here, to be stored in a unexpected
  packet database. This allows nmblookup and other tools to receive packets
@@ -44,6 +155,13 @@ void unexpected_packet(struct packet_struct *p)
        int len=0;
        uint32_t enc_ip;
 
+       if (!should_store_unexpected_packet(p)) {
+               DEBUG(10,("Not storing unexpected packet\n"));
+               return;
+       }
+
+       DEBUG(10,("unexpected_packet: storing packet\n"));
+
        if (!tdbd) {
                tdbd = tdb_wrap_open(NULL, lock_path("unexpected.tdb"), 0,
                                     TDB_CLEAR_IF_FIRST|TDB_DEFAULT|TDB_INCOMPATIBLE_HASH,
@@ -108,6 +226,15 @@ static int traverse_fn(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *st
 
 void clear_unexpected(time_t t)
 {
+       struct pending_unexpected *pu, *pu_next;
+
+       for (pu = pu_list; pu; pu = pu_next) {
+               pu_next = pu->next;
+               if (pu->timeout < t) {
+                       DLIST_REMOVE(pu_list, pu);
+               }
+       }
+
        if (!tdbd) return;
 
        if ((lastt != 0) && (t < lastt + NMBD_UNEXPECTED_TIMEOUT))
@@ -168,8 +295,10 @@ static int traverse_match(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf,
        if ((state->match_type == NMB_PACKET &&
             p->packet.nmb.header.name_trn_id == state->match_id) ||
            (state->match_type == DGRAM_PACKET &&
-            match_mailslot_name(p, state->match_name))) {
+            match_mailslot_name(p, state->match_name) &&
+            p->packet.dgram.header.dgm_id == state->match_id)) {
                state->matched_packet = p;
+               tdb_delete(ttdb, kbuf);
                return -1;
        }
 
@@ -189,7 +318,7 @@ struct packet_struct *receive_unexpected(enum packet_type packet_type, int id,
        struct receive_unexpected_state state;
 
        tdb2 = tdb_wrap_open(talloc_tos(), lock_path("unexpected.tdb"), 0, 0,
-                            O_RDONLY, 0);
+                            O_RDWR, 0);
        if (!tdb2) return NULL;
 
        state.matched_packet = NULL;