Explorar o código

netbios_ns: Documents and makes more usable

Julien 'Lta' BALLET %!s(int64=11) %!d(string=hai) anos
pai
achega
c9e3ca5e1f
Modificáronse 9 ficheiros con 309 adicións e 85 borrados
  1. 2 1
      Makefile.am
  2. 18 8
      bin/discover.c
  3. 5 4
      bin/inverse.c
  4. 0 2
      configure.ac
  5. 9 9
      doc/Doxyfile.in
  6. 29 0
      include/bdsm/debug.h
  7. 148 13
      include/bdsm/netbios_ns.h
  8. 59 45
      src/netbios_ns.c
  9. 39 3
      src/netbios_ns_entry.c

+ 2 - 1
Makefile.am

@@ -10,7 +10,8 @@ bdsmdir = $(includedir)/bdsm
 
 
 bdsm_HEADERS = \
 bdsm_HEADERS = \
     include/bdsm.h			\
     include/bdsm.h			\
-    include/bdsm/context.h		\
+    include/bdsm/debug.h    \
+    include/bdsm/context.h    \
     include/bdsm/netbios_defs.h		\
     include/bdsm/netbios_defs.h		\
     include/bdsm/netbios_query.h	\
     include/bdsm/netbios_query.h	\
     include/bdsm/netbios_utils.h	\
     include/bdsm/netbios_utils.h	\

+ 18 - 8
bin/discover.c

@@ -21,27 +21,37 @@
 #include <string.h>
 #include <string.h>
 #include <assert.h>
 #include <assert.h>
 
 
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
 #include "bdsm.h"
 #include "bdsm.h"
 
 
 int main(int ac, char **av)
 int main(int ac, char **av)
 {
 {
-  bdsm_context_t      *ctx;
-  netbios_ns_entry_t  *iter;
+  netbios_ns_t      *ns;
+  netbios_ns_iter_t iter;
 
 
-  ctx = bdsm_context_new();
-  assert(ctx);
+  ns = netbios_ns_new();
 
 
-  if (!netbios_ns_discover(ctx->ns))
+  if (!netbios_ns_discover(ns))
   {
   {
     fprintf(stderr, "Error while discovering local network\n");
     fprintf(stderr, "Error while discovering local network\n");
     exit(42);
     exit(42);
   }
   }
 
 
-  iter = ctx->ns->entries;
+  iter = netbios_ns_get_entries(ns);
   while (iter != NULL)
   while (iter != NULL)
   {
   {
-    printf("Found %s\n", iter->name);
-    iter = iter->next;
+    struct in_addr addr;
+
+    addr.s_addr = netbios_ns_iter_ip(iter);
+    printf("Ip: %s, name: %s<%x> \n",
+      inet_ntoa(addr),
+      netbios_ns_iter_name(iter),
+      netbios_ns_iter_type(iter));
+
+    iter = netbios_ns_iter_next(iter);
   }
   }
 
 
   return (0);
   return (0);

+ 5 - 4
bin/inverse.c

@@ -30,7 +30,8 @@
 int main(int ac, char **av)
 int main(int ac, char **av)
 {
 {
   bdsm_context_t      *ctx;
   bdsm_context_t      *ctx;
-  netbios_ns_entry_t  ns_entry;
+  struct in_addr      addr;
+  const char          *name;
 
 
   ctx = bdsm_context_new();
   ctx = bdsm_context_new();
   assert(ctx);
   assert(ctx);
@@ -42,14 +43,14 @@ int main(int ac, char **av)
     exit(1);
     exit(1);
   }
   }
 
 
-  inet_aton(av[1], &ns_entry.address);
-  if (!netbios_ns_inverse(ctx->ns, &ns_entry))
+  inet_aton(av[1], &addr);
+  if ((name = netbios_ns_inverse(ctx->ns, addr.s_addr)) == NULL)
   {
   {
     fprintf(stderr, "Unable to perform inverse name resolution for %s\n", av[1]);
     fprintf(stderr, "Unable to perform inverse name resolution for %s\n", av[1]);
     exit(42);
     exit(42);
   }
   }
 
 
-  printf("%s\n", ns_entry.name);
+  printf("%s\n", name);
 
 
   return (0);
   return (0);
 }
 }

+ 0 - 2
configure.ac

@@ -35,8 +35,6 @@ AC_ARG_ENABLE([debug],
 
 
 AS_IF([test x"$enable_debug" != x"no"], [
 AS_IF([test x"$enable_debug" != x"no"], [
   AC_DEFINE([BDSM_DEBUG], [1])
   AC_DEFINE([BDSM_DEBUG], [1])
-], [
-  AC_DEFINE([BDSM_DEBUG], [0])
 ])
 ])
 
 
 AM_CONDITIONAL([PROGRAMS], [test x"$enable_programs" != x"no"])
 AM_CONDITIONAL([PROGRAMS], [test x"$enable_programs" != x"no"])

+ 9 - 9
doc/Doxyfile.in

@@ -44,7 +44,7 @@ PROJECT_NUMBER         = @BDSM_ABI_VERSION@
 # for a project that appears at the top of each page and should give viewer a
 # for a project that appears at the top of each page and should give viewer a
 # quick idea about the purpose of the project. Keep the description short.
 # quick idea about the purpose of the project. Keep the description short.
 
 
-PROJECT_BRIEF          = Defective SMb: A minimalist and read-only SMB client lib
+PROJECT_BRIEF          = "Minimalist and read-only SMB client lib"
 
 
 # With the PROJECT_LOGO tag one can specify an logo or icon that is included in
 # With the PROJECT_LOGO tag one can specify an logo or icon that is included in
 # the documentation. The maximum height of the logo should not exceed 55 pixels
 # the documentation. The maximum height of the logo should not exceed 55 pixels
@@ -404,7 +404,7 @@ EXTRACT_ALL            = NO
 # be included in the documentation.
 # be included in the documentation.
 # The default value is: NO.
 # The default value is: NO.
 
 
-EXTRACT_PRIVATE        = YES
+EXTRACT_PRIVATE        = NO
 
 
 # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
 # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
 # scope will be included in the documentation.
 # scope will be included in the documentation.
@@ -424,7 +424,7 @@ EXTRACT_STATIC         = NO
 # for Java sources.
 # for Java sources.
 # The default value is: YES.
 # The default value is: YES.
 
 
-EXTRACT_LOCAL_CLASSES  = YES
+EXTRACT_LOCAL_CLASSES  = NO
 
 
 # This flag is only useful for Objective-C code. When set to YES local methods,
 # This flag is only useful for Objective-C code. When set to YES local methods,
 # which are defined in the implementation section but not in the interface are
 # which are defined in the implementation section but not in the interface are
@@ -432,7 +432,7 @@ EXTRACT_LOCAL_CLASSES  = YES
 # included.
 # included.
 # The default value is: NO.
 # The default value is: NO.
 
 
-EXTRACT_LOCAL_METHODS  = YES
+EXTRACT_LOCAL_METHODS  = NO
 
 
 # If this flag is set to YES, the members of anonymous namespaces will be
 # If this flag is set to YES, the members of anonymous namespaces will be
 # extracted and appear in the documentation as a namespace called
 # extracted and appear in the documentation as a namespace called
@@ -441,7 +441,7 @@ EXTRACT_LOCAL_METHODS  = YES
 # are hidden.
 # are hidden.
 # The default value is: NO.
 # The default value is: NO.
 
 
-EXTRACT_ANON_NSPACES   = YES
+EXTRACT_ANON_NSPACES   = NO
 
 
 # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
 # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
 # undocumented members inside documented classes or files. If set to NO these
 # undocumented members inside documented classes or files. If set to NO these
@@ -449,7 +449,7 @@ EXTRACT_ANON_NSPACES   = YES
 # section is generated. This option has no effect if EXTRACT_ALL is enabled.
 # section is generated. This option has no effect if EXTRACT_ALL is enabled.
 # The default value is: NO.
 # The default value is: NO.
 
 
-HIDE_UNDOC_MEMBERS     = NO
+HIDE_UNDOC_MEMBERS     = YES
 
 
 # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
 # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
 # undocumented classes that are normally visible in the class hierarchy. If set
 # undocumented classes that are normally visible in the class hierarchy. If set
@@ -457,7 +457,7 @@ HIDE_UNDOC_MEMBERS     = NO
 # no effect if EXTRACT_ALL is enabled.
 # no effect if EXTRACT_ALL is enabled.
 # The default value is: NO.
 # The default value is: NO.
 
 
-HIDE_UNDOC_CLASSES     = NO
+HIDE_UNDOC_CLASSES     = YES
 
 
 # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
 # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
 # (class|struct|union) declarations. If set to NO these declarations will be
 # (class|struct|union) declarations. If set to NO these declarations will be
@@ -478,7 +478,7 @@ HIDE_IN_BODY_DOCS      = NO
 # will be excluded. Set it to YES to include the internal documentation.
 # will be excluded. Set it to YES to include the internal documentation.
 # The default value is: NO.
 # The default value is: NO.
 
 
-INTERNAL_DOCS          = YES
+INTERNAL_DOCS          = NO
 
 
 # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
 # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
 # names in lower-case letters. If set to YES upper-case letters are also
 # names in lower-case letters. If set to YES upper-case letters are also
@@ -699,7 +699,7 @@ WARNINGS               = YES
 # will automatically be disabled.
 # will automatically be disabled.
 # The default value is: YES.
 # The default value is: YES.
 
 
-WARN_IF_UNDOCUMENTED   = YES
+WARN_IF_UNDOCUMENTED   = NO
 
 
 # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
 # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
 # potential errors in the documentation, such as not documenting some parameters
 # potential errors in the documentation, such as not documenting some parameters

+ 29 - 0
include/bdsm/debug.h

@@ -0,0 +1,29 @@
+//---------------------------------------------------------------------------
+//  __________________    _________  _____            _____  .__         ._.
+//  \______   \______ \  /   _____/ /     \          /  _  \ |__| ____   | |
+//   |    |  _/|    |  \ \_____  \ /  \ /  \        /  /_\  \|  _/ __ \  | |
+//   |    |   \|    `   \/        /    Y    \      /    |    |  \  ___/   \|
+//   |______  /_______  /_______  \____|__  / /\   \____|__  |__|\___ |   __
+//          \/        \/        \/        \/  )/           \/        \/   \/
+//
+// This file is part of libdsm. Copyright © 2014 VideoLabs SAS
+//
+// Author: Julien 'Lta' BALLET <contact@lta.io>
+//
+// This program is free software. It comes without any warranty, to the extent
+// permitted by applicable law. You can redistribute it and/or modify it under
+// the terms of the Do What The Fuck You Want To Public License, Version 2, as
+// published by Sam Hocevar. See the COPYING file for more details.
+//----------------------------------------------------------------------------
+
+#ifndef __BDSM_DEBUG_H__
+# define __BDSM_DEBUG_H__
+
+# ifdef BDSM_DEBUG
+#  include <stdio.h>
+#  define BDSM_dbg(...) fprintf(stderr, __VA_ARGS__)
+# else
+#  define BDSM_dbg(...) (0)
+# endif
+
+#endif

+ 148 - 13
include/bdsm/netbios_ns.h

@@ -26,14 +26,74 @@
 #include <netinet/ip.h>
 #include <netinet/ip.h>
 #include <netinet/udp.h>
 #include <netinet/udp.h>
 
 
+/**
+ * @file netbios_ns.h
+ * @brief Netbios name service
+ */
 
 
+/**
+ * @internal
+ * @brief Represents an correspondance between an IP address and a Netbios name.
+ *
+ * @details Consider it as an opaque data structure whose internal layout might
+ * change at any time, please use the provided accessors functions
+ */
 typedef struct                netbios_ns_entry_s
 typedef struct                netbios_ns_entry_s
 {
 {
   struct netbios_ns_entry_s     *next;
   struct netbios_ns_entry_s     *next;
   struct in_addr                address;
   struct in_addr                address;
-  char                          name[NETBIOS_NAME_LENGTH + 2];
+  char                          name[NETBIOS_NAME_LENGTH + 1];
+  char                          type;
 }                             netbios_ns_entry_t;
 }                             netbios_ns_entry_t;
 
 
+/**
+ * @struct netbios_ns_iter_t
+ * @brief An iterator over name service cached entries.
+ * @details Can be compared againt NULL to test for validity
+ */
+typedef netbios_ns_entry_t    *netbios_ns_iter_t;
+
+/**
+ * @brief Iterates over netbios name service entries
+ *
+ * @param[in] iter An iterator opaque object.
+ * @return An interator referencing the next item in the item set, or NULL.
+ */
+netbios_ns_iter_t   netbios_ns_iter_next(netbios_ns_iter_t iter);
+
+/**
+ * @brief Get the name of the entry referenced by the iterator iter.
+ * @details The pointer points to an area of memory owned by the netbios name
+ * service
+ *
+ * @return A null-terminated ASCII string representing the name of a netbios machine.
+ */
+const char          *netbios_ns_iter_name(netbios_ns_iter_t iter);
+
+/**
+ * @brief Return the IP address of the correspondance referenced by the iterator
+ *
+ * @return The ip address of this entry, in network byte order.
+ */
+uint32_t            netbios_ns_iter_ip(netbios_ns_iter_t iter);
+
+/**
+ * @brief Return the type of record
+ *
+ * @return The type of netbios record (.ie 0x20 for FileServer,
+ * 0 for workstation, etc.) or a value < 0 if the iterator is invalid or an
+ * error occured.
+ */
+char                netbios_ns_iter_type(netbios_ns_iter_t iter);
+
+/**
+ * @brief The netbios name service object.
+ *
+ * @details Holds all the necessary data structure to perform resolution and
+ * discovery, and to stores the results. Consider it as an opaque data
+ * structure, use the related functions to interact with it.
+ */
+
 typedef struct {
 typedef struct {
   int                 socket;
   int                 socket;
   struct sockaddr_in  addr;
   struct sockaddr_in  addr;
@@ -41,25 +101,100 @@ typedef struct {
   netbios_ns_entry_t  *entries;     // Only used during discovery;
   netbios_ns_entry_t  *entries;     // Only used during discovery;
 }                   netbios_ns_t;
 }                   netbios_ns_t;
 
 
+/**
+ * @brief Allocate and initialize the Netbios name service client object.
+ * @return A newly allocated netbios_ns_t ready for querying.
+ * Deallocate with netbios_ns_destroy().
+ */
 netbios_ns_t  *netbios_ns_new();
 netbios_ns_t  *netbios_ns_new();
+
+/**
+ * @brief Destroy the netbios name service object
+ * @param[in] ns A pointer on the netbios_ns_t to destroy and deallocate
+ */
 void          netbios_ns_destroy(netbios_ns_t *ns);
 void          netbios_ns_destroy(netbios_ns_t *ns);
+
+/**
+ * @brief Resolve a Netbios name
+ * @details This function tries to resolves the given NetBIOS name with the
+ * given type on the LAN, using broadcast queries. No WINS server is called.
+ *
+ * @param ns the netbios name service object.
+ * @param name the null-terminated ASCII netbios name to resolve. If it's
+ * longer than 15 chars, it'll be truncated.
+ * @param type The type of the name to look for. @see netbios_defs.h
+ * @return the ipv4 address in network byte-order or 0 if it wasn't successfull.
+ */
 uint32_t      netbios_ns_resolve(netbios_ns_t *ns, const char *name, char type);
 uint32_t      netbios_ns_resolve(netbios_ns_t *ns, const char *name, char type);
+
+/**
+ * @brief Try to discover all the Netbios/SMB speaking machine on the LAN.
+ * @details This functions sends a message to '*' Netbios name, and waits for
+ * the machine on the LAN to answer. It then performs a reverse lookup on all
+ * the ip he received packet from. It stores the results inside of the name
+ * service, allowing you to list them
+ *
+ *
+ * @param ns The name service object.
+ * @return It returns 0 in case of error.
+ */
 int           netbios_ns_discover(netbios_ns_t *ns);
 int           netbios_ns_discover(netbios_ns_t *ns);
-int           netbios_ns_inverse(netbios_ns_t *ns, netbios_ns_entry_t *entry);
 
 
-int           netbios_ns_send_query(netbios_ns_t *ns, netbios_query_t *q,
-                                    uint32_t ip);
-ssize_t       netbios_ns_recv(int sock, void *buf, size_t buf_size,
-                              struct timeval *timeout, struct sockaddr *addr,
-                              socklen_t *addr_len);
+/**
+ * @brief List all know machines
+ * @details Returns an iterator for the list of known machine of this name
+ * service object. You might want to call discover before-hand if you don't want
+ * the lit to be empty
+ *
+ * @param ns The nameservice object.
+ * @return An iterator for the list of known machine
+ */
+netbios_ns_iter_t netbios_ns_get_entries(netbios_ns_t *ns);
+
+/**
+ * @brief Perform an inverse netbios lookup (get name from ip)
+ * @details This function does a NBSTAT and stores all the returned entry in
+ * the internal list of entries. It returns one of the name found. (Normally
+ * the <20> or <0> name)
+ *
+ * @param ns The name service object.
+ * @param ip The ip address in network byte order.
+ *
+ * @return A null-terminated ASCII string containing the NETBIOS name. You don't
+ * own the it (it'll be freed when destroying/clearing the name service)
+ */
+const char          *netbios_ns_inverse(netbios_ns_t *ns, uint32_t ip);
+
+/**
+ * @brief Clear all the existing entries from the name service
+ *
+ * @param ns The nameservice object
+ */
+void                netbios_ns_clear(netbios_ns_t *ns);
 
 
-// Remove all the entry from the name service
-void                netbios_ns_entry_clear(netbios_ns_t *ns);
-// Add an entry to the nameservice list with name and/or ip
+/**
+ * @internal
+ * @brief Add an entry to the name service list.
+ * @details You can provide a name and/or an ip
+ *
+ * @param ns The name service object.
+ * @param name The ASCII name of the entry or NULL
+ * @param type the <X> type for this entry or -1
+ * @param ip The IP address in network byte order (or 0)
+ * @return The added entry
+ */
 netbios_ns_entry_t  *netbios_ns_entry_add(netbios_ns_t *ns, const char *name,
 netbios_ns_entry_t  *netbios_ns_entry_add(netbios_ns_t *ns, const char *name,
-                                          uint32_t ip);
-// Find an entry in the list. Search by name if name is not NULL,
-// or by ip otherwise
+                                          char type, uint32_t ip);
+/**
+ * @internal
+ * @brief Find an entry in
+ * @details [long description]
+ *
+ * @param ns [description]
+ * @param by_name [description]
+ * @param ip [description]
+ * @return [description]
+ */
 netbios_ns_entry_t  *netbios_ns_entry_find(netbios_ns_t *ns, const char *by_name,
 netbios_ns_entry_t  *netbios_ns_entry_find(netbios_ns_t *ns, const char *by_name,
                                            uint32_t ip);
                                            uint32_t ip);
 
 

+ 59 - 45
src/netbios_ns.c

@@ -29,10 +29,12 @@
 #include <arpa/inet.h>
 #include <arpa/inet.h>
 #include <sys/select.h>
 #include <sys/select.h>
 
 
+#include "bdsm/debug.h"
 #include "bdsm/netbios_ns.h"
 #include "bdsm/netbios_ns.h"
 #include "bdsm/netbios_query.h"
 #include "bdsm/netbios_query.h"
 #include "bdsm/netbios_utils.h"
 #include "bdsm/netbios_utils.h"
 
 
+
 static int    ns_open_socket(netbios_ns_t *ns)
 static int    ns_open_socket(netbios_ns_t *ns)
 {
 {
   int sock_opt;
   int sock_opt;
@@ -89,7 +91,7 @@ void          netbios_ns_destroy(netbios_ns_t *ns)
   if (!ns)
   if (!ns)
     return;
     return;
 
 
-  netbios_ns_entry_clear(ns);
+  netbios_ns_clear(ns);
 
 
   close(ns->socket);
   close(ns->socket);
   free(ns);
   free(ns);
@@ -193,8 +195,8 @@ uint32_t      netbios_ns_resolve(netbios_ns_t *ns, const char *name, char type)
   // Let's send it
   // Let's send it
   if (!netbios_ns_send_query(ns, q, ip))
   if (!netbios_ns_send_query(ns, q, ip))
     return (0);
     return (0);
-  else if (BDSM_DEBUG)
-    fprintf(stderr, "netbios_ns_resolve, name query sent for '%s' !\n", name);
+  else
+    BDSM_dbg("netbios_ns_resolve, name query sent for '%s' !\n", name);
 
 
   free(encoded_name);
   free(encoded_name);
 
 
@@ -205,8 +207,8 @@ uint32_t      netbios_ns_resolve(netbios_ns_t *ns, const char *name, char type)
 
 
   if (recv <= 0)
   if (recv <= 0)
     goto error;
     goto error;
-  else if (BDSM_DEBUG)
-    fprintf(stderr, "netbios_ns_resolve, received a reply for '%s' !\n", name);
+  else
+    BDSM_dbg("netbios_ns_resolve, received a reply for '%s' !\n", name);
 
 
   return (*(uint32_t *)(recv_buffer + recv - 4));
   return (*(uint32_t *)(recv_buffer + recv - 4));
 
 
@@ -215,6 +217,11 @@ uint32_t      netbios_ns_resolve(netbios_ns_t *ns, const char *name, char type)
     return (0);
     return (0);
 }
 }
 
 
+static void   _netbios_ns_sync(netbios_ns_t *ns)
+{
+
+}
+
 int           netbios_ns_discover(netbios_ns_t *ns)
 int           netbios_ns_discover(netbios_ns_t *ns)
 {
 {
   const char  broadcast_name[] = NETBIOS_WILDCARD;
   const char  broadcast_name[] = NETBIOS_WILDCARD;
@@ -248,11 +255,11 @@ int           netbios_ns_discover(netbios_ns_t *ns)
   // Let's send it
   // Let's send it
   if (!netbios_ns_send_query(ns, q, ip))
   if (!netbios_ns_send_query(ns, q, ip))
   {
   {
-    fprintf(stderr, "Unable to send netbios 'discovery query'.\n");
+    BDSM_dbg("Unable to send netbios 'discovery query'.\n");
     return (0);
     return (0);
   }
   }
-  else if (BDSM_DEBUG)
-    fprintf(stderr, "netbios_ns_discover, name query sent for '*'.\n");
+  else
+    BDSM_dbg("netbios_ns_discover, name query sent for '*'.\n");
 
 
 
 
 
 
@@ -260,8 +267,6 @@ int           netbios_ns_discover(netbios_ns_t *ns)
   // Second step, we list every IP that answered to our broadcast.
   // Second step, we list every IP that answered to our broadcast.
   //
   //
 
 
-  netbios_ns_entry_clear(ns); // Let's reset our internal host list
-
   timeout.tv_sec = 1;
   timeout.tv_sec = 1;
   timeout.tv_usec = 420;
   timeout.tv_usec = 420;
   recv_addr_len = sizeof(recv_addr);
   recv_addr_len = sizeof(recv_addr);
@@ -270,30 +275,31 @@ int           netbios_ns_discover(netbios_ns_t *ns)
   {
   {
     // Verify this is a reply to our request
     // Verify this is a reply to our request
     if (ntohs(*(uint16_t *)recv_buffer) != ns->last_trn_id)
     if (ntohs(*(uint16_t *)recv_buffer) != ns->last_trn_id)
-      fprintf(stderr, "Not a reply to our query: %u (our trn_id was: %u)\n",
-        ntohs(*(uint16_t * )recv_buffer), ns->last_trn_id);
+    {
+      BDSM_dbg("Not a reply to our query: %u (our trn_id was: %u)\n",
+              ntohs(*(uint16_t * )recv_buffer), ns->last_trn_id);
+    }
     else // Add the ip to the list of IP to check
     else // Add the ip to the list of IP to check
     {
     {
-      netbios_ns_entry_add(ns, NULL, recv_addr.sin_addr.s_addr);
-      if (BDSM_DEBUG)
-        fprintf(stderr, "Discover: received a reply from %s\n",
-                inet_ntoa(recv_addr.sin_addr));
+      netbios_ns_inverse(ns, recv_addr.sin_addr.s_addr);
+      BDSM_dbg("Discover: received a reply from %s\n",
+              inet_ntoa(recv_addr.sin_addr));
     }
     }
   }
   }
 
 
-  netbios_ns_entry_t *iter = ns->entries;
-  while(iter != NULL)
-  {
-    netbios_ns_inverse(ns, iter);
-    iter = iter->next;
-  }
-
   return (1);
   return (1);
 }
 }
 
 
+netbios_ns_iter_t netbios_ns_get_entries(netbios_ns_t *ns)
+{
+  assert(ns != NULL);
+
+  return (ns->entries);
+}
+
 // Perform inverse name resolution. Grap an IP and return the first <20> field
 // Perform inverse name resolution. Grap an IP and return the first <20> field
 // returned by the host
 // returned by the host
-int           netbios_ns_inverse(netbios_ns_t *ns, netbios_ns_entry_t *entry)
+const char        *netbios_ns_inverse(netbios_ns_t *ns, uint32_t ip)
 {
 {
   const char  broadcast_name[] = NETBIOS_WILDCARD;
   const char  broadcast_name[] = NETBIOS_WILDCARD;
   char        footer[4]        = { 0x00, 0x21, 0x00, 0x01 }; // NBSTAT/IP
   char        footer[4]        = { 0x00, 0x21, 0x00, 0x01 }; // NBSTAT/IP
@@ -303,7 +309,7 @@ int           netbios_ns_inverse(netbios_ns_t *ns, netbios_ns_entry_t *entry)
   char                recv_buffer[512]; // Hu ?
   char                recv_buffer[512]; // Hu ?
   ssize_t             recv;
   ssize_t             recv;
 
 
-  assert(ns != NULL && entry != NULL);
+  assert(ns != NULL && ip != 0);
 
 
   // Prepare NBSTAT query packet
   // Prepare NBSTAT query packet
   q = netbios_query_new(34 + 4, 1, NETBIOS_OP_NAME_QUERY);
   q = netbios_query_new(34 + 4, 1, NETBIOS_OP_NAME_QUERY);
@@ -312,11 +318,11 @@ int           netbios_ns_inverse(netbios_ns_t *ns, netbios_ns_entry_t *entry)
   q->packet->queries = htons(1);
   q->packet->queries = htons(1);
 
 
   // Let's send it
   // Let's send it
-  if (!netbios_ns_send_query(ns, q, entry->address.s_addr))
-    return (0);
-  else if (BDSM_DEBUG)
-    fprintf(stderr, "netbios_ns_inverse, reverse name query sent for '%s' !\n",
-            inet_ntoa(entry->address));
+  if (!netbios_ns_send_query(ns, q, ip))
+    return (NULL);
+  else
+    BDSM_dbg("netbios_ns_inverse, reverse name query sent for '%s' !\n",
+            inet_ntoa(*(struct in_addr *)&ip));
 
 
   // Now wait for a reply and pray
   // Now wait for a reply and pray
   timeout.tv_sec = 1;
   timeout.tv_sec = 1;
@@ -325,38 +331,46 @@ int           netbios_ns_inverse(netbios_ns_t *ns, netbios_ns_entry_t *entry)
 
 
   if (recv <= 0)
   if (recv <= 0)
     goto error;
     goto error;
-  else if (BDSM_DEBUG)
-    fprintf(stderr, "netbios_ns_inverse, received a reply for '%s' !\n",
-            inet_ntoa(entry->address));
+  else
+    BDSM_dbg("netbios_ns_inverse, received a reply for '%s' !\n",
+            inet_ntoa(*(struct in_addr*)&ip));
 
 
 
 
-  // Now we've got something, let's find the <20> name
+  // Now we've got something, let's find the <20>/<0> name
   netbios_query_packet_t  *p = (netbios_query_packet_t *)recv_buffer;
   netbios_query_packet_t  *p = (netbios_query_packet_t *)recv_buffer;
   uint8_t                 name_count;
   uint8_t                 name_count;
   uint8_t                 name_idx;
   uint8_t                 name_idx;
   char                    *names;
   char                    *names;
   char                    *current_name;
   char                    *current_name;
+  char                    current_type;
+  netbios_ns_entry_t      *entry = NULL, *res = NULL;
 
 
-  printf("Queried name length: %u\n", p->payload[0]);
+  BDSM_dbg("Queried name length: %u\n", p->payload[0]);
   name_count = p->payload[p->payload[0] + 12];
   name_count = p->payload[p->payload[0] + 12];
-  printf("Number of names: %hhu\n", name_count);
+  BDSM_dbg("Number of names: %hhu\n", name_count);
   names = p->payload + p->payload[0] + 13;
   names = p->payload + p->payload[0] + 13;
 
 
   for(name_idx = 0; name_idx < name_count; name_idx++)
   for(name_idx = 0; name_idx < name_count; name_idx++)
   {
   {
     current_name = names + name_idx * 18;
     current_name = names + name_idx * 18;
-    if (current_name[15] == 0x20)
-    {
-      if (BDSM_DEBUG)
-        fprintf(stderr, "Found name : %s\n", current_name);
-      memcpy(entry->name, current_name, NETBIOS_NAME_LENGTH + 2);
-      return (1);
-    }
+    current_type = current_name[15];
+
+    BDSM_dbg("Found name : %s (type == 0x%x)\n", current_name, current_type);
+    if (current_type == 0x20 || current_type == 0)
+      entry = netbios_ns_entry_add(ns, current_name, current_type, ip);
+    if (current_type == 0x20)
+      res = entry;
   }
   }
 
 
-  return(0);
+  if (res)        // We prefer a <20> name.
+    return(res->name);
+  else if (entry) // Did we found a <0> or <20> name ?
+    return(entry->name);
+  else
+    return (NULL);
 
 
   error:
   error:
     perror("netbios_ns_inverse: ");
     perror("netbios_ns_inverse: ");
-    return (0);
+    return (NULL);
 }
 }
+

+ 39 - 3
src/netbios_ns_entry.c

@@ -23,7 +23,39 @@
 
 
 #include "bdsm/netbios_ns.h"
 #include "bdsm/netbios_ns.h"
 
 
-void                netbios_ns_entry_clear(netbios_ns_t *ns)
+netbios_ns_iter_t   netbios_ns_iter_next(netbios_ns_iter_t iter)
+{
+  if (iter != NULL)
+    return (iter->next);
+  else
+    return (NULL);
+}
+
+const char          *netbios_ns_iter_name(netbios_ns_iter_t iter)
+{
+  if (iter != NULL)
+    return (iter->name);
+  else
+    return (NULL);
+}
+
+uint32_t            netbios_ns_iter_ip(netbios_ns_iter_t iter)
+{
+  if (iter != NULL)
+    return (iter->address.s_addr);
+  else
+    return (0);
+}
+
+char                netbios_ns_iter_type(netbios_ns_iter_t iter)
+{
+  if (iter != NULL)
+    return (iter->type);
+  else
+    return (-1);
+}
+
+void                netbios_ns_clear(netbios_ns_t *ns)
 {
 {
   netbios_ns_entry_t  *next;
   netbios_ns_entry_t  *next;
 
 
@@ -38,7 +70,7 @@ void                netbios_ns_entry_clear(netbios_ns_t *ns)
 }
 }
 
 
 netbios_ns_entry_t *netbios_ns_entry_add(netbios_ns_t *ns, const char *name,
 netbios_ns_entry_t *netbios_ns_entry_add(netbios_ns_t *ns, const char *name,
-                                         uint32_t ip)
+                                         char type, uint32_t ip)
 {
 {
   netbios_ns_entry_t  *entry;
   netbios_ns_entry_t  *entry;
 
 
@@ -47,8 +79,12 @@ netbios_ns_entry_t *netbios_ns_entry_add(netbios_ns_t *ns, const char *name,
   memset((void *)entry, 0, sizeof(netbios_ns_entry_t));
   memset((void *)entry, 0, sizeof(netbios_ns_entry_t));
 
 
   if (name != NULL)
   if (name != NULL)
-    memcpy(entry->name, name, NETBIOS_NAME_LENGTH + 2);
+  {
+    memcpy(entry->name, name, NETBIOS_NAME_LENGTH);
+    entry->name[NETBIOS_NAME_LENGTH] = 0;
+  }
 
 
+  entry->type           = type;
   entry->address.s_addr = ip;
   entry->address.s_addr = ip;
   entry->next           = ns->entries;
   entry->next           = ns->entries;