first pass at updating head branch to be to be the same as the SAMBA_2_0 branch
[kai/samba.git] / source3 / nmbd / nmbd_packets.c
index cafed5f79acff0af91fce1315978944497001695..0a7696a46631a1e26ada3206c8e37c96ca2eca80 100644 (file)
@@ -35,6 +35,11 @@ extern int num_response_packets;
 extern pstring scope;
 extern struct in_addr loopback_ip;
 
+static void queue_packet(struct packet_struct *packet);
+
+BOOL rescan_listen_set = False;
+
+
 /*******************************************************************
   The global packet linked-list. Incoming entries are 
   added to the end of this list. It is supposed to remain fairly 
@@ -95,9 +100,11 @@ Dumps out the browse packet data.
 static void debug_browse_data(char *outbuf, int len)
 {
   int i,j;
+
+  DEBUG( 4, ( "debug_browse_data():\n" ) );
   for (i = 0; i < len; i+= 16)
   {
-    DEBUG(4, ("%3x char ", i));
+    DEBUGADD( 4, ( "%3x char ", i ) );
 
     for (j = 0; j < 16; j++)
     {
@@ -107,19 +114,19 @@ static void debug_browse_data(char *outbuf, int len)
            
       if (i+j >= len)
         break;
-      DEBUG(4, ("%c", x));
+      DEBUGADD( 4, ( "%c", x ) );
     }
 
-    DEBUG(4, (" hex ", i));
+    DEBUGADD( 4, ( "%*s hex", 16-j, "" ) );
 
     for (j = 0; j < 16; j++)
     {
       if (i+j >= len) 
         break;
-      DEBUG(4, (" %02x", (unsigned char)outbuf[i+j]));
+      DEBUGADD( 4, ( " %02x", (unsigned char)outbuf[i+j] ) );
     }
 
-    DEBUG(4, ("\n"));
+    DEBUGADD( 4, ("\n") );
   }
 }
 
@@ -134,7 +141,7 @@ static uint16 generate_name_trn_id(void)
 
   if (!name_trn_id)
   {
-    name_trn_id = (time(NULL)%(unsigned)0x7FFF) + (getpid()%(unsigned)100);
+    name_trn_id = ((unsigned)time(NULL)%(unsigned)0x7FFF) + ((unsigned)getpid()%(unsigned)100);
   }
   name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF;
   return name_trn_id;
@@ -188,7 +195,7 @@ static struct packet_struct *create_and_init_netbios_packet(struct nmb_name *nmb
     return NULL;
   }
     
-  bzero((char *)packet,sizeof(*packet));
+  memset((char *)packet,'\0',sizeof(*packet));
 
   nmb = &packet->packet.nmb;
 
@@ -235,13 +242,17 @@ static BOOL create_and_init_additional_record(struct packet_struct *packet,
     return False;
   }
 
-  bzero((char *)nmb->additional,sizeof(struct res_rec));
+  memset((char *)nmb->additional,'\0',sizeof(struct res_rec));
 
   nmb->additional->rr_name  = nmb->question.question_name;
   nmb->additional->rr_type  = RR_TYPE_NB;
   nmb->additional->rr_class = RR_CLASS_IN;
 
-  nmb->additional->ttl = lp_max_ttl();
+  /* See RFC 1002, sections 5.1.1.1, 5.1.1.2 and 5.1.1.3 */
+  if (nmb->header.nm_flags.bcast)
+    nmb->additional->ttl = PERMANENT_TTL;
+  else
+    nmb->additional->ttl = lp_max_ttl();
 
   nmb->additional->rdlength = 6;
 
@@ -275,12 +286,34 @@ static BOOL initiate_name_query_packet( struct packet_struct *packet)
   nmb->header.nm_flags.recursion_desired = True;
 
   DEBUG(4,("initiate_name_query_packet: sending query for name %s (bcast=%s) to IP %s\n",
-          namestr(&nmb->question.question_name), 
+          nmb_namestr(&nmb->question.question_name), 
            BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip)));
 
   return send_netbios_packet( packet );
 }
 
+/***************************************************************************
+ Sends out a name query - from a WINS server. 
+**************************************************************************/
+
+static BOOL initiate_name_query_packet_from_wins_server( struct packet_struct *packet)
+{   
+  struct nmb_packet *nmb = NULL;
+  
+  nmb = &packet->packet.nmb;
+
+  nmb->header.opcode = NMB_NAME_QUERY_OPCODE;
+  nmb->header.arcount = 0;
+    
+  nmb->header.nm_flags.recursion_desired = False;
+  
+  DEBUG(4,("initiate_name_query_packet_from_wins_server: sending query for name %s (bcast=%s) to IP %s\n",
+           nmb_namestr(&nmb->question.question_name),
+           BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip)));
+    
+  return send_netbios_packet( packet );
+} 
+
 /***************************************************************************
  Sends out a name register.
 **************************************************************************/
@@ -299,7 +332,7 @@ static BOOL initiate_name_register_packet( struct packet_struct *packet,
     return False;
 
   DEBUG(4,("initiate_name_register_packet: sending registration for name %s (bcast=%s) to IP %s\n",
-          namestr(&nmb->additional->rr_name),
+          nmb_namestr(&nmb->additional->rr_name),
            BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip)));
 
   return send_netbios_packet( packet );
@@ -313,9 +346,9 @@ static BOOL initiate_multihomed_name_register_packet( struct packet_struct *pack
                                     uint16 nb_flags, struct in_addr *register_ip)
 {
   struct nmb_packet *nmb = &packet->packet.nmb;
-  char second_ip_buf[25];
+  fstring second_ip_buf;
 
-  strcpy(second_ip_buf, inet_ntoa(packet->ip));
+  fstrcpy(second_ip_buf, inet_ntoa(packet->ip));
 
   nmb->header.opcode = NMB_NAME_MULTIHOMED_REG_OPCODE;
   nmb->header.arcount = 1;
@@ -327,7 +360,7 @@ static BOOL initiate_multihomed_name_register_packet( struct packet_struct *pack
 
   DEBUG(4,("initiate_multihomed_name_register_packet: sending registration \
 for name %s IP %s (bcast=%s) to IP %s\n",
-          namestr(&nmb->additional->rr_name), inet_ntoa(*register_ip),
+          nmb_namestr(&nmb->additional->rr_name), inet_ntoa(*register_ip),
            BOOLSTR(nmb->header.nm_flags.bcast), second_ip_buf ));
 
   return send_netbios_packet( packet );
@@ -351,7 +384,7 @@ static BOOL initiate_name_refresh_packet( struct packet_struct *packet,
     return False;
 
   DEBUG(4,("initiate_name_refresh_packet: sending refresh for name %s (bcast=%s) to IP %s\n",
-          namestr(&nmb->additional->rr_name),
+          nmb_namestr(&nmb->additional->rr_name),
            BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip)));
 
   return send_netbios_packet( packet );
@@ -375,7 +408,7 @@ static BOOL initiate_name_release_packet( struct packet_struct *packet,
     return False;
 
   DEBUG(4,("initiate_name_release_packet: sending release for name %s (bcast=%s) to IP %s\n",
-          namestr(&nmb->additional->rr_name),
+          nmb_namestr(&nmb->additional->rr_name),
            BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip)));
 
   return send_netbios_packet( packet );
@@ -397,7 +430,7 @@ static BOOL initiate_node_status_packet( struct packet_struct *packet )
   nmb->question.question_type = QUESTION_TYPE_NB_STATUS;
 
   DEBUG(4,("initiate_node_status_packet: sending node status request for name %s to IP %s\n",
-          namestr(&nmb->question.question_name),
+          nmb_namestr(&nmb->question.question_name),
            inet_ntoa(packet->ip)));
 
   return send_netbios_packet( packet );
@@ -580,6 +613,17 @@ struct response_record *queue_release_name( struct subnet_record *subrec,
     return NULL;
   }
 
+  /*
+   * For a broadcast release packet, only send once.
+   * This will cause us to remove the name asap. JRA.
+   */
+
+  if(bcast)
+  {
+    rrec->repeat_count = 0;
+    rrec->repeat_time = 0;
+  }
+
   return rrec;
 }
 
@@ -607,7 +651,7 @@ struct response_record *queue_refresh_name( struct subnet_record *subrec,
                      subrec->bcast_ip)) == NULL)
     return NULL;
 
-  if(initiate_name_refresh_packet( p, namerec->nb_flags, &refresh_ip) == False)
+  if( !initiate_name_refresh_packet( p, namerec->data.nb_flags, &refresh_ip ) )
   {
     p->locked = False;
     free_packet(p);
@@ -679,6 +723,48 @@ struct response_record *queue_query_name( struct subnet_record *subrec,
   return rrec;
 }
 
+/****************************************************************************
+ 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,
+                          query_name_success_function success_fn,
+                          query_name_fail_function fail_fn,
+                          struct userdata_struct *userdata,
+                          struct nmb_name *nmbname)
+{
+  struct packet_struct *p;
+  struct response_record *rrec;
+  BOOL bcast = False;
+
+  if(( p = create_and_init_netbios_packet(nmbname, bcast, to_ip)) == NULL)
+    return NULL;
+
+  if(initiate_name_query_packet_from_wins_server( p ) == False)
+  {
+    p->locked = False;
+    free_packet(p);
+    return NULL;
+  }
+
+  if((rrec = make_response_record(wins_server_subnet,           /* subnet record. */
+               p,                     /* packet we sent. */
+               resp_fn,               /* function to call on response. */
+               timeout_fn,            /* function to call on timeout. */
+               (success_function)success_fn,            /* function to call on operation success. */
+               (fail_function)fail_fn,               /* function to call on operation fail. */
+               userdata)) == NULL)
+  {
+    p->locked = False;
+    free_packet(p);
+    return NULL;
+  }
+
+  return rrec;
+}
+
 /****************************************************************************
  Queue a node status packet to a given name and address.
 ****************************************************************************/
@@ -819,7 +905,7 @@ void reply_netbios_packet(struct packet_struct *orig_packet,
     default:
     {
       DEBUG(0,("reply_netbios_packet: Unknown packet type: %s %s to ip %s\n",
-                   packet_type, namestr(&orig_nmb->question.question_name),
+                   packet_type, nmb_namestr(&orig_nmb->question.question_name),
                     inet_ntoa(packet.ip)));
 
       return;
@@ -828,7 +914,7 @@ void reply_netbios_packet(struct packet_struct *orig_packet,
 
   DEBUG(4,("reply_netbios_packet: sending a reply of packet type: %s %s to ip %s \
 for id %hu\n",
-          packet_type, namestr(&orig_nmb->question.question_name),
+          packet_type, nmb_namestr(&orig_nmb->question.question_name),
            inet_ntoa(packet.ip), orig_nmb->header.name_trn_id));
 
   nmb->header.name_trn_id = orig_nmb->header.name_trn_id;
@@ -844,10 +930,10 @@ for id %hu\n",
   nmb->header.nscount = 0;
   nmb->header.arcount = 0;
   
-  bzero((char*)&nmb->question,sizeof(nmb->question));
+  memset((char*)&nmb->question,'\0',sizeof(nmb->question));
   
   nmb->answers = &answers;
-  bzero((char*)nmb->answers,sizeof(*nmb->answers));
+  memset((char*)nmb->answers,'\0',sizeof(*nmb->answers));
   
   nmb->answers->rr_name  = orig_nmb->question.question_name;
   nmb->answers->rr_type  = orig_nmb->question.question_type;
@@ -886,8 +972,7 @@ for id %hu\n",
 /*******************************************************************
   Queue a packet into a packet queue
 ******************************************************************/
-
-void queue_packet(struct packet_struct *packet)
+static void queue_packet(struct packet_struct *packet)
 {
   struct packet_struct *p;
 
@@ -936,8 +1021,7 @@ static struct subnet_record *find_subnet_for_dgram_browse_packet(struct packet_s
 /****************************************************************************
 Dispatch a browse frame from port 138 to the correct processing function.
 ****************************************************************************/
-
-void process_browse_packet(struct packet_struct *p, char *buf,int len)
+static void process_browse_packet(struct packet_struct *p, char *buf,int len)
 {
   struct dgram_packet *dgram = &p->packet.dgram;
   int command = CVAL(buf,0);
@@ -956,7 +1040,7 @@ mismatch with our scope (%s).\n", inet_ntoa(p->ip), dgram->dest_name.scope, scop
   if (is_myname(dgram->source_name.name))
   {
     DEBUG(0,("process_browse_packet: Discarding datagram from IP %s. Source name \
-%s is one of our names !\n", inet_ntoa(p->ip), namestr(&dgram->source_name)));
+%s is one of our names !\n", inet_ntoa(p->ip), nmb_namestr(&dgram->source_name)));
     return;
   }
 
@@ -995,15 +1079,6 @@ mismatch with our scope (%s).\n", inet_ntoa(p->ip), dgram->dest_name.scope, scop
     case ANN_GetBackupListReq:
     {
       debug_browse_data(buf, len);
-
-      /* This is one occasion where we change a subnet that is
-        given to us. If the packet was sent to WORKGROUP<1b> instead
-        of WORKGROUP<1d> then it was unicast to us a domain master
-        browser. Change subrec to unicast.
-      */
-      if(dgram->dest_name.name_type == 0x1b)
-        subrec = unicast_subnet;
-
       process_get_backup_list_request(subrec, p, buf+1);
       break;
     }
@@ -1013,7 +1088,7 @@ mismatch with our scope (%s).\n", inet_ntoa(p->ip), dgram->dest_name.scope, scop
       /* We never send ANN_GetBackupListReq so we
          should never get these. */
       DEBUG(0,("process_browse_packet: Discarding GetBackupListResponse \
-packet from %s IP %s\n", namestr(&dgram->source_name), inet_ntoa(p->ip)));
+packet from %s IP %s\n", nmb_namestr(&dgram->source_name), inet_ntoa(p->ip)));
       break;
     }
     case ANN_ResetBrowserState:
@@ -1040,8 +1115,8 @@ packet from %s IP %s\n", namestr(&dgram->source_name), inet_ntoa(p->ip)));
       debug_browse_data(buf, len);
       DEBUG(10,("process_browse_packet: On subnet %s ignoring browse packet \
 command ANN_BecomeBackup from %s IP %s to %s\n",
-            subrec->subnet_name, namestr(&dgram->source_name),
-            inet_ntoa(p->ip), namestr(&dgram->dest_name)));
+            subrec->subnet_name, nmb_namestr(&dgram->source_name),
+            inet_ntoa(p->ip), nmb_namestr(&dgram->dest_name)));
       break;
     }
     default:
@@ -1049,8 +1124,8 @@ command ANN_BecomeBackup from %s IP %s to %s\n",
       debug_browse_data(buf, len);
       DEBUG(0,("process_browse_packet: On subnet %s ignoring browse packet \
 command code %d from %s IP %s to %s\n", 
-            subrec->subnet_name, command, namestr(&dgram->source_name),
-            inet_ntoa(p->ip), namestr(&dgram->dest_name)));
+            subrec->subnet_name, command, nmb_namestr(&dgram->source_name),
+            inet_ntoa(p->ip), nmb_namestr(&dgram->dest_name)));
     }
   } 
 }
@@ -1058,8 +1133,7 @@ command code %d from %s IP %s to %s\n",
 /****************************************************************************
  Dispatch a LanMan browse frame from port 138 to the correct processing function.
 ****************************************************************************/
-
-void process_lanman_packet(struct packet_struct *p, char *buf,int len)
+static void process_lanman_packet(struct packet_struct *p, char *buf,int len)
 {
   struct dgram_packet *dgram = &p->packet.dgram;
   int command = SVAL(buf,0);
@@ -1078,7 +1152,7 @@ mismatch with our scope (%s).\n", inet_ntoa(p->ip), dgram->dest_name.scope, scop
   if (is_myname(dgram->source_name.name))
   {
     DEBUG(0,("process_lanman_packet: Discarding datagram from IP %s. Source name \
-%s is one of our names !\n", inet_ntoa(p->ip), namestr(&dgram->source_name)));
+%s is one of our names !\n", inet_ntoa(p->ip), nmb_namestr(&dgram->source_name)));
     return;
   }
 
@@ -1099,8 +1173,8 @@ mismatch with our scope (%s).\n", inet_ntoa(p->ip), dgram->dest_name.scope, scop
     {
       DEBUG(0,("process_lanman_packet: On subnet %s ignoring browse packet \
 command code %d from %s IP %s to %s\n",
-            subrec->subnet_name, command, namestr(&dgram->source_name),
-            inet_ntoa(p->ip), namestr(&dgram->dest_name)));
+            subrec->subnet_name, command, nmb_namestr(&dgram->source_name),
+            inet_ntoa(p->ip), nmb_namestr(&dgram->dest_name)));
     }
   }
 }
@@ -1142,7 +1216,7 @@ static void process_dgram(struct packet_struct *p)
   if (!listening(p,&dgram->dest_name))
   {
     DEBUG(5,("process_dgram: ignoring dgram packet sent to name %s from %s\n",
-           namestr(&dgram->dest_name), inet_ntoa(p->ip)));
+           nmb_namestr(&dgram->dest_name), inet_ntoa(p->ip)));
     return;
   }
 
@@ -1153,7 +1227,7 @@ static void process_dgram(struct packet_struct *p)
     /* Don't process error packets etc yet */
     DEBUG(5,("process_dgram: ignoring dgram packet sent to name %s from IP %s as it is \
            an error packet of type %x\n",
-           namestr(&dgram->dest_name), inet_ntoa(p->ip), dgram->header.msg_type));
+           nmb_namestr(&dgram->dest_name), inet_ntoa(p->ip), dgram->header.msg_type));
     return;
   }
 
@@ -1168,7 +1242,7 @@ static void process_dgram(struct packet_struct *p)
   buf2 = smb_base(buf) + SVAL(buf,smb_vwv12);
 
   DEBUG(4,("process_dgram: datagram from %s to %s IP %s for %s of type %d len=%d\n",
-          namestr(&dgram->source_name),namestr(&dgram->dest_name),
+          nmb_namestr(&dgram->source_name),nmb_namestr(&dgram->dest_name),
           inet_ntoa(p->ip), smb_buf(buf),CVAL(buf2,0),len));
 
  
@@ -1207,7 +1281,7 @@ static void process_dgram(struct packet_struct *p)
   Validate a response nmb packet.
 ****************************************************************************/
 
-BOOL validate_nmb_response_packet( struct nmb_packet *nmb )
+static BOOL validate_nmb_response_packet( struct nmb_packet *nmb )
 {
   BOOL ignore = False;
 
@@ -1261,7 +1335,7 @@ BOOL validate_nmb_response_packet( struct nmb_packet *nmb )
   Validate a request nmb packet.
 ****************************************************************************/
 
-BOOL validate_nmb_packet( struct nmb_packet *nmb )
+static BOOL validate_nmb_packet( struct nmb_packet *nmb )
 {
   BOOL ignore = False;
 
@@ -1488,7 +1562,7 @@ found for id = %hu. Ignoring packet.\n", nmb->header.name_trn_id));
   Run elements off the packet queue till its empty
 ******************************************************************/
 
-void run_packet_queue()
+void run_packet_queue(void)
 {
   struct packet_struct *p;
 
@@ -1557,16 +1631,31 @@ to IP %s on subnet %s\n", rrec->response_id, inet_ntoa(rrec->packet->ip),
 on subnet %s\n", rrec->response_id, inet_ntoa(rrec->packet->ip), 
                  subrec->subnet_name));
 
-          /* Call the timeout function. This will deal with removing the
-             timed out packet. */
-          if(rrec->timeout_fn)
-            (*rrec->timeout_fn)(subrec, rrec);
-          else
+          /*
+           * Check the flag in this record to prevent recursion if we end
+           * up in this function again via the timeout function call.
+           */
+
+          if(!rrec->in_expiration_processing)
           {
-            /* We must remove the record ourself if there is
-               no timeout function. */
-            remove_response_record(subrec, rrec);
-          }
+
+            /*
+             * Set the recursion protection flag in this record.
+             */
+
+            rrec->in_expiration_processing = True;
+
+            /* Call the timeout function. This will deal with removing the
+               timed out packet. */
+            if(rrec->timeout_fn)
+              (*rrec->timeout_fn)(subrec, rrec);
+            else
+            {
+              /* We must remove the record ourself if there is
+                 no timeout function. */
+              remove_response_record(subrec, rrec);
+            }
+          } /* !rrec->in_expitation_processing */
         } /* rrec->repeat_count > 0 */
       } /* rrec->repeat_time <= t */
     } /* end for rrec */
@@ -1634,6 +1723,10 @@ only use %d.\n", (count*2) + 2, FD_SETSIZE));
   }
 
   *listen_number = (count*2) + 2;
+
+  if (*ppset) free(*ppset);
+  if (*psock_array) free(*psock_array);
+
   *ppset = pset;
   *psock_array = sock_array;
  
@@ -1657,13 +1750,14 @@ BOOL listen_for_packets(BOOL run_election)
   int dns_fd;
 #endif
 
-  if(listen_set == NULL)
+  if(listen_set == NULL || rescan_listen_set)
   {
     if(create_listen_fdset(&listen_set, &sock_array, &listen_number))
     {
       DEBUG(0,("listen_for_packets: Fatal error. unable to create listen set. Exiting.\n"));
       return True;
     }
+    rescan_listen_set = False;
   }
 
   memcpy((char *)&fds, (char *)listen_set, sizeof(fd_set));
@@ -1696,7 +1790,7 @@ BOOL listen_for_packets(BOOL run_election)
   BlockSignals(False, SIGUSR2);
 #endif /* SIGUSR2 */
 
-  selrtn = sys_select(&fds,&timeout);
+  selrtn = sys_select(FD_SETSIZE,&fds,&timeout);
 
   /* We can only take signals when we are in the select - block them again here. */
 
@@ -1798,13 +1892,12 @@ BOOL listen_for_packets(BOOL run_election)
 
 /****************************************************************************
   Construct and send a netbios DGRAM.
-  Note that this currently sends all packets to port 138.
 **************************************************************************/
-
 BOOL send_mailslot(BOOL unique, char *mailslot,char *buf,int len,
                    char *srcname, int src_type,
                    char *dstname, int dest_type,
-                   struct in_addr dest_ip,struct in_addr src_ip)
+                   struct in_addr dest_ip,struct in_addr src_ip,
+                  int dest_port)
 {
   BOOL loopback_this_packet = False;
   struct packet_struct p;
@@ -1812,7 +1905,7 @@ BOOL send_mailslot(BOOL unique, char *mailslot,char *buf,int len,
   char *ptr,*p2;
   char tmp[4];
 
-  bzero((char *)&p,sizeof(p));
+  memset((char *)&p,'\0',sizeof(p));
 
   if(ismyip(dest_ip))
     loopback_this_packet = True;
@@ -1850,7 +1943,7 @@ BOOL send_mailslot(BOOL unique, char *mailslot,char *buf,int len,
   SSVAL(ptr,smb_vwv15,1);
   SSVAL(ptr,smb_vwv16,2);
   p2 = smb_buf(ptr);
-  strcpy(p2,mailslot);
+  pstrcpy(p2,mailslot);
   p2 = skip_string(p2,1);
 
   memcpy(p2,buf,len);
@@ -1859,14 +1952,14 @@ BOOL send_mailslot(BOOL unique, char *mailslot,char *buf,int len,
   dgram->datasize = PTR_DIFF(p2,ptr+4); /* +4 for tcp length. */
 
   p.ip = dest_ip;
-  p.port = DGRAM_PORT;
+  p.port = dest_port;
   p.fd = find_subnet_mailslot_fd_for_address( src_ip );
   p.timestamp = time(NULL);
   p.packet_type = DGRAM_PACKET;
 
   DEBUG(4,("send_mailslot: Sending to mailslot %s from %s IP %s ", mailslot,
-                    namestr(&dgram->source_name), inet_ntoa(src_ip)));
-  DEBUG(4,("to %s IP %s\n", namestr(&dgram->dest_name), inet_ntoa(dest_ip)));
+                    nmb_namestr(&dgram->source_name), inet_ntoa(src_ip)));
+  DEBUG(4,("to %s IP %s\n", nmb_namestr(&dgram->dest_name), inet_ntoa(dest_ip)));
 
   debug_browse_data(buf, len);