ソースを参照

Adds netbios session transport initial support. Implements local network netbios machines discovery

Julien 'Lta' BALLET 11 年 前
コミット
db5242ad07

+ 6 - 4
Makefile

@@ -7,10 +7,12 @@ CC          = clang
 UTIL				= dsm
 LIB					= libdsm.so
 
-UTIL_SRC		= dsm.c 							\
-							src/netbios_utils.c	\
-							src/netbios_ns.c		\
-							src/netbios_query.c \
+UTIL_SRC		= dsm.c 							    \
+							src/netbios_utils.c	    \
+							src/netbios_ns.c		    \
+							src/netbios_ns_entry.c	\
+							src/netbios_query.c     \
+							src/netbios_session.c		\
 							src/context.c
 
 UTIL_OBJS		= $(UTIL_SRC:.c=.o)

+ 21 - 4
dsm.c

@@ -18,16 +18,33 @@
 
 #include "bdsm.h"
 #include "bdsm/netbios_utils.h"
+#include "bdsm/netbios_session.h"
+#include "bdsm/smb_defs.h"
 
 int main(int ac, char **av)
 {
-  bdsm_context_t  *ctx;
-  uint32_t        ip;
+  struct sockaddr_in  addr;
+  bdsm_context_t      *ctx;
+
+  //printf("sizeof(smb_header_t) = %lu", sizeof(smb_header_t));
 
   ctx = bdsm_context_new();
   assert(ctx);
-  ip = netbios_ns_resolve(ctx->ns, av[1]);
-  printf("%s's IP addresse is : %lu\n", av[1], ip & 0xFF);
+  addr.sin_addr.s_addr = netbios_ns_resolve(ctx->ns, av[1], NETBIOS_FILESERVER);
+  printf("%s's IP address is : %s\n", av[1], inet_ntoa(addr.sin_addr));
+
+  netbios_ns_discover(ctx->ns);
+
+  exit(0);
+
+  netbios_session_t *session;
+  session = netbios_session_new(addr.sin_addr.s_addr);
+  if (netbios_session_connect(session, "Cerbere"))
+    printf("A NetBIOS session with %s has been established\n", av[1]);
+  else
+    printf("Unable to establish a NetBIOS session with %s\n", av[1]);
+  netbios_session_destroy(session);
+  bdsm_context_destroy(ctx);
   // struct sockaddr_in  addr;
   // int                 sock;
   // int                 sock_opt = 1;

+ 1 - 1
include/bdsm/context.h

@@ -16,6 +16,6 @@ typedef struct      bdsm_context_s {
 }                   bdsm_context_t;
 
 bdsm_context_t    *bdsm_context_new();
-void              bdsm_context_close(bdsm_context_t *ctx);
+void              bdsm_context_destroy(bdsm_context_t *ctx);
 
 #endif

+ 26 - 10
include/bdsm/netbios_defs.h

@@ -8,8 +8,8 @@
 
 #include <stdint.h>
 
-#define NETBIOS_UDP_PORT      137 // NS Port
-#define NETBIOS_TCP_PORT      138 // Session Port
+#define NETBIOS_PORT_NAME     137 // UDP
+#define NETBIOS_PORT_SESSION  139 // TCP
 
 #define NETBIOS_NAME_LENGTH   15
 
@@ -18,15 +18,28 @@
 #define NETBIOS_MESSENGER     0x03
 #define NETBIOS_FILESERVER    0x20
 #define NETBIOS_DOMAINMASTER  0x1b
+        // http://ubiqx.org/cifs/rfc-draft/rfc1001.html#s17.2
+#define NETBIOS_WILDCARD      { 32, 'C', 'K', 'A', 'A', 'A', 'A', 'A', 'A',    \
+    'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', \
+    'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 0 }
 
 #define NETBIOS_FLAG_QUERY      (1 << 15)
 #define NETBIOS_FLAG_TRUNCATED  (1 << 9)
 #define NETBIOS_FLAG_RECURSIVE  (1 << 8)
 #define NETBIOS_FLAG_BROADCAST  (1 << 4)
 
-#define NETBIOS_OPCODE_NAME_QUERY   0
-
-typedef struct              netbios_query_packet_s {
+// Name Service Query
+#define NETBIOS_OP_NAME_QUERY         0x00
+// Session Service
+#define NETBIOS_OP_SESSION_MSG        0x00
+#define NETBIOS_OP_SESSION_REQ        0x81
+#define NETBIOS_OP_SESSION_REQ_OK     0x82
+#define NETBIOS_OP_SESSION_REQ_NOK    0x83
+#define NETBIOS_OP_SESSION_RETARGET   0x84
+#define NETBIOS_OP_SESSION_KEEPALIVE  0x85
+
+typedef struct
+{
   uint16_t                    trn_id;     // Transaction ID
   uint16_t                    flags;      // Various flags
   uint16_t                    queries;    // Number of queries in this packet
@@ -36,10 +49,13 @@ typedef struct              netbios_query_packet_s {
   char                        payload[];
 } __attribute__((packed))   netbios_query_packet_t;
 
-typedef struct              netbios_query_s {
-  size_t                      payload_size;
-  size_t                      cursor;
-  netbios_query_packet_t      *packet;
-}                           netbios_query_t;
+typedef struct
+{
+  uint8_t                     opcode;     // 'TYPE'
+  uint8_t                     flags;      // 0-6 reserved (== 0), byte 7 is the
+                                          // beginning of the length field (!!)
+  uint16_t                    length;     // payload length;
+  uint8_t                     payload[];
+} __attribute__((packed))   netbios_session_packet_t;
 
 #endif

+ 30 - 2
include/bdsm/netbios_ns.h

@@ -7,19 +7,47 @@
 #define __BDSM_NETBIOS_NS_H_
 
 #include "bdsm/netbios_defs.h"
+#include "bdsm/netbios_query.h"
 
 #include <sys/socket.h>
 #include <netinet/ip.h>
 #include <netinet/udp.h>
 
-typedef struct      netbios_ns_s {
+
+typedef struct                netbios_ns_entry_s
+{
+  struct netbios_ns_entry_s     *next;
+  struct in_addr                address;
+  char                          name[NETBIOS_NAME_LENGTH + 2];
+}                             netbios_ns_entry_t;
+
+typedef struct {
   int                 socket;
   struct sockaddr_in  addr;
   uint16_t            last_trn_id;  // Last transaction id used;
+  netbios_ns_entry_t  *entries;     // Only used during discovery;
 }                   netbios_ns_t;
 
 netbios_ns_t  *netbios_ns_new();
 void          netbios_ns_destroy(netbios_ns_t *ns);
-uint32_t      netbios_ns_resolve(netbios_ns_t *ns, const char *name);
+uint32_t      netbios_ns_resolve(netbios_ns_t *ns, const char *name, char type);
+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);
+
+// 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
+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
+netbios_ns_entry_t  *netbios_ns_entry_find(netbios_ns_t *ns, const char *by_name,
+                                           uint32_t ip);
 
 #endif

+ 9 - 2
include/bdsm/netbios_query.h

@@ -11,8 +11,14 @@
 
 #include "bdsm/netbios_defs.h"
 
-netbios_query_t   *netbios_query_new(uint16_t trn_id, size_t payload_size,
-                                     int is_query, char opcode);
+typedef struct              netbios_query_s {
+  size_t                      payload_size;
+  size_t                      cursor;
+  netbios_query_packet_t      *packet;
+}                           netbios_query_t;
+
+netbios_query_t   *netbios_query_new(size_t payload_size, int is_query,
+                                     char opcode);
 void              netbios_query_destroy(netbios_query_t *q);
 void              netbios_query_set_flag(netbios_query_t *q,
                                          uint16_t flag, int value);
@@ -21,4 +27,5 @@ int               netbios_query_append(netbios_query_t *q, const char *data,
 
 void              netbios_query_print(netbios_query_t *q);
 
+
 #endif

+ 54 - 0
include/bdsm/netbios_session.h

@@ -0,0 +1,54 @@
+// This file is part of libdsm
+// Author: Julien 'Lta' BALLET <contact@lta.io>
+// Copyright VideoLabs 2014
+// License: MIT License
+
+#ifndef __BDSM_NETBIOS_SESSION_H_
+#define __BDSM_NETBIOS_SESSION_H_
+
+#include <stdint.h>
+#include <stddef.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "bdsm/netbios_defs.h"
+
+#define NETBIOS_SESSION_NEW         0
+#define NETBIOS_SESSION_CONNECTING  1
+#define NETBIOS_SESSION_CONNECTED   2
+#define NETBIOS_SESSION_ERROR       -1
+#define NETBIOS_SESSION_REFUSED     -2
+
+#define NETBIOS_SESSION_BUFFER      2048
+#define NETBIOS_SESSION_PAYLOAD     NETBIOS_SESSION_BUFFER
+
+
+typedef struct              netbios_session_s {
+  // The address of the remote peer;
+  struct sockaddr_in          remote_addr;
+  // The socket of the TCP connection to the HOST'
+  int                         socket;
+  // The current sessions state; See macro before (eg. NETBIOS_SESSION_ERROR)
+  int                         state;
+  // What is the size of the allocated payload;
+  size_t                      packet_payload_size;
+  // Where is the write cursor relative to the beginning of the payload
+  size_t                      packet_cursor;
+  // Our allocated packet, this is where the magic happen :)
+  netbios_session_packet_t    *packet;
+  // Some buffer space to receive message from peer;
+  uint8_t                     recv_buffer[NETBIOS_SESSION_BUFFER];
+}                           netbios_session_t;
+
+netbios_session_t *netbios_session_new(uint32_t ip_addr);
+void              netbios_session_destroy(netbios_session_t *);
+int               netbios_session_connect(netbios_session_t *s, char *name);
+void              netbios_session_packet_init(netbios_session_t *s,
+                                              uint8_t opcode);
+int               netbios_session_packet_append(netbios_session_t *s,
+                                                char *data, size_t size);
+int               netbios_session_packet_send(netbios_session_t *s);
+ssize_t           netbios_session_packet_recv(netbios_session_t *s);
+
+#endif

+ 58 - 0
include/bdsm/smb_defs.h

@@ -0,0 +1,58 @@
+// This file is part of libdsm
+// Author: Julien 'Lta' BALLET <contact@lta.io>
+// Copyright VideoLabs 2014
+// License: MIT License
+
+#ifndef __BSDM_SMB_DEFS_H_
+#define __BSDM_SMB_DEFS_H_
+
+#include <stdint.h>
+
+typedef struct
+{
+  uint8_t         wct; // zero
+  uint16_t        bct;
+  char            dialects[];
+
+} __attribute__((packed))   smb_negotiate_req_t;
+
+typedef struct
+{
+
+} __attribute__((packed))   smb_negotiate_resp_t;
+
+typedef struct
+{
+
+} __attribute__((packed))   smb_session_req_t;
+
+typedef struct
+{
+
+} __attribute__((packed))   smb_session_resp_t;
+
+typedef struct
+{
+  uint8_t         magic[4];     // { 0xff, 0x53, 0x4d, 0x42 } "\xffSMB"
+  uint8_t         command;      // The actual SMB command
+  uint32_t        status;       // 'NT Status'
+  uint8_t         flags;        // Packet flags
+  uint16_t        flags2;       // More flags ? (lol)
+  uint16_t        pid_high;     // Unused ?
+  uint64_t        signature;    // Unused ?
+  uint16_t        reserved;     // More usuned bit (we have so much BW :)
+  uint16_t        tree_id;      // SMB's file descriptor or service id ?
+  uint16_t        pid;          // Process ID.
+  uint16_t        uid;          // Process ID.
+  uint16_t        mux_id;       // Multiplex ID. Increment it sometimes.
+  //uint8_t         payload[];    // The yummy data inside. Remember to eat 5 fruits/day
+  union {
+    smb_negotiate_req_t     negotiate_req;
+    smb_negotiate_resp_t    negotiate_resp;
+    smb_session_req_t       session_req;
+    smb_session_resp_t      session_resp;
+  }               msg;
+} __attribute__((packed))   smb_header_t;
+
+
+#endif

+ 1 - 1
src/context.c

@@ -28,7 +28,7 @@ bdsm_context_t    *bdsm_context_new()
   return (ctx);
 }
 
-void              bdsm_context_close(bdsm_context_t *ctx)
+void              bdsm_context_destroy(bdsm_context_t *ctx)
 {
   if (!ctx)
     return;

+ 242 - 29
src/netbios_ns.c

@@ -14,6 +14,7 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#include <sys/select.h>
 
 #include "bdsm/netbios_ns.h"
 #include "bdsm/netbios_query.h"
@@ -62,74 +63,286 @@ netbios_ns_t  *netbios_ns_new()
     return (0);
   }
 
-  ns->last_trn_id = rand();
+  ns->entries       = NULL;
+  ns->last_trn_id   = rand();
 
   return (ns);
 }
 
 void          netbios_ns_destroy(netbios_ns_t *ns)
 {
+  netbios_ns_entry_t  *next;
+
   if (!ns)
     return;
 
+  netbios_ns_entry_clear(ns);
+
   close(ns->socket);
   free(ns);
 }
 
-uint32_t      netbios_ns_resolve(netbios_ns_t *ns, const char *name)
+int               netbios_ns_send_query(netbios_ns_t *ns, netbios_query_t *q,
+                                        uint32_t ip)
+{
+  struct sockaddr_in  addr;
+  ssize_t             sent;
+  uint16_t            trn_id;
+
+  assert(ns != NULL && q != NULL);
+
+  trn_id = ns->last_trn_id + 1; // Increment transaction ID, not to reuse them
+  q->packet->trn_id = htons(trn_id);
+
+  addr.sin_addr.s_addr  = ip;
+  addr.sin_family       = AF_INET;
+  addr.sin_port         = htons(NETBIOS_PORT_NAME);
+
+  sent = sendto(ns->socket, (void *)q->packet,
+                sizeof(netbios_query_packet_t) + q->cursor, 0,
+                (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
+  netbios_query_destroy(q);
+
+  if (sent < 0){
+    perror("netbios_ns_send_query: ");
+    return (0);
+  }
+
+  ns->last_trn_id = trn_id; // Remember the last transaction id.
+  return (1);
+}
+
+ssize_t           netbios_ns_recv(int sock, void *buf, size_t buf_size,
+                                 struct timeval *timeout, struct sockaddr *addr,
+                                 socklen_t *addr_len)
+{
+  fd_set        read_fds, error_fds;
+  int           res;
+
+  assert(sock && buf != NULL && buf_size > 0);
+
+  FD_ZERO(&read_fds);
+  FD_ZERO(&error_fds);
+  FD_SET(sock, &read_fds);
+  FD_SET(sock, &error_fds);
+
+  res = select(sock + 1, &read_fds, 0, &error_fds, timeout);
+
+  if (res < 0)
+    goto error;
+  if (FD_ISSET(sock, &error_fds))
+    goto error;
+
+  if (FD_ISSET(sock, &read_fds))
+  {
+    return (recvfrom(sock, buf, buf_size, 0, addr, addr_len));
+  }
+  else
+    return (0);
+
+  error:
+    perror("netbios_ns_recv: ");
+    return (-1);
+}
+
+uint32_t      netbios_ns_resolve(netbios_ns_t *ns, const char *name, char type)
 {
+  struct timeval      timeout;
   netbios_query_t     *q;
   char                *encoded_name;
   char                footer[4] = { 0x00, 0x20, 0x00, 0x01 };
   char                recv_buffer[512]; // Hu ?
-
-  struct sockaddr_in  bcast_addr;
-  ssize_t             sent;
   ssize_t             recv;
+  uint16_t            trn_id;
+  uint32_t            ip;
+
 
+  assert(ns != NULL);
 
-  assert(ns);
-  encoded_name = netbios_name_encode(name, 0, NETBIOS_WORKSTATION);
-  if (!encoded_name)
+  if ((encoded_name = netbios_name_encode(name, 0, type)) == NULL)
     return (0);
-  //q = netbios_query_new(ns->last_trn_id, 34, 1, NETBIOS_OPCODE_NAME_QUERY);
-  //printf("Encoded name (l2): %s.\n", encoded_name);
-  q = netbios_query_new(ns->last_trn_id, 34 + 4, 1, NETBIOS_OPCODE_NAME_QUERY);
-  //printf("Encoded name (l2): %s.\n", encoded_name);
+
+    // Prepare packet
+  q = netbios_query_new(34 + 4, 1, NETBIOS_OP_NAME_QUERY);
   netbios_query_set_flag(q, NETBIOS_FLAG_RECURSIVE, 1);
   netbios_query_set_flag(q, NETBIOS_FLAG_BROADCAST, 1);
+
+  // Append the queried name to the packet
   netbios_query_append(q, encoded_name, strlen(encoded_name) + 1);
-  free(encoded_name);
+
+  // Magic footer (i.e. Question type (Netbios) / class (IP)
   netbios_query_append(q, footer, 4);
   q->packet->queries = htons(1);
-  //netbios_query_print(q);
 
-  inet_aton("255.255.255.255", &(bcast_addr.sin_addr));
-  bcast_addr.sin_family = AF_INET;
-  bcast_addr.sin_port   = htons(137);
+  // We broadcast this query
+  inet_aton("255.255.255.255", (struct in_addr *)&ip);
 
-  sent = sendto(ns->socket, (void *)q->packet,
-                sizeof(netbios_query_packet_t) + q->cursor, 0,
-                (struct sockaddr *)&bcast_addr, sizeof(struct sockaddr_in));
-  netbios_query_destroy(q);
-
-  if (sent < 0)
-    goto error;
+  // Let's send it
+  if (!netbios_ns_send_query(ns, q, ip))
+    return (0);
   else if (BDSM_DEBUG)
-    printf("netbios_ns_resolve, name query sent for '%s' !\n", name);
+    fprintf(stderr, "netbios_ns_resolve, name query sent for '%s' !\n", name);
 
-  recv = recvfrom(ns->socket, (void *)recv_buffer, 512, 0, 0, 0);
+  free(encoded_name);
+
+  // Now wait for a reply and pray
+  timeout.tv_sec = 1;
+  timeout.tv_usec = 500;
+  recv = netbios_ns_recv(ns->socket, (void *)recv_buffer, 512, &timeout, 0, 0);
 
-  if (recv < 0)
+  if (recv <= 0)
     goto error;
   else if (BDSM_DEBUG)
-    printf("netbios_ns_resolve, received a reply for '%s' !\n", name);
+    fprintf(stderr, "netbios_ns_resolve, received a reply for '%s' !\n", name);
 
-  return (ntohl(*(uint32_t *)(recv_buffer + recv - 4)));
+  return (*(uint32_t *)(recv_buffer + recv - 4));
 
   error:
     perror("netbios_ns_resolve: ");
     return (0);
 }
 
+int           netbios_ns_discover(netbios_ns_t *ns)
+{
+  const char  broadcast_name[] = NETBIOS_WILDCARD;
+  char        footer[4]        = { 0x00, 0x20, 0x00, 0x01 };
+
+  struct sockaddr_in  recv_addr;
+  socklen_t           recv_addr_len;
+  struct timeval      timeout;
 
+  netbios_query_t     *q;
+  char                recv_buffer[512]; // Hu ?
+  ssize_t             recv;
+  uint32_t            ip;
+
+  assert (ns != NULL);
+
+  //
+  // First step, we broadcast a packet to receive a message from every
+  // NETBIOS nodes on the local network
+  //
+  q = netbios_query_new(34 + 4, 1, NETBIOS_OP_NAME_QUERY);
+  // Append the queried name to the packet
+  netbios_query_append(q, broadcast_name, strlen(broadcast_name) + 1);
+  // Magic footer (i.e. Question type (Netbios) / class (IP)
+  netbios_query_append(q, footer, 4);
+  q->packet->queries = htons(1);
+
+  // We broadcast this query
+  inet_aton("255.255.255.255", (struct in_addr *)&ip);
+
+  // Let's send it
+  if (!netbios_ns_send_query(ns, q, ip))
+  {
+    fprintf(stderr, "Unable to send netbios 'discovery query'.\n");
+    return (0);
+  }
+  else if (BDSM_DEBUG)
+    fprintf(stderr, "netbios_ns_discover, name query sent for '*'.\n");
+
+
+
+  //
+  // 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_usec = 420;
+  recv_addr_len = sizeof(recv_addr);
+  while (netbios_ns_recv(ns->socket, (void *)recv_buffer, 512,
+    &timeout, (struct sockaddr *)&recv_addr, &recv_addr_len) > 0)
+  {
+    // Verify this is a reply to our request
+    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);
+    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_entry_t *iter = ns->entries;
+  while(iter != NULL)
+  {
+    netbios_ns_inverse(ns, iter);
+    iter = iter->next;
+  }
+
+  return (0);
+}
+
+// Perform inverse name resolution. Grap an IP and return the first <20> field
+// returned by the host
+int           netbios_ns_inverse(netbios_ns_t *ns, netbios_ns_entry_t *entry)
+{
+  const char  broadcast_name[] = NETBIOS_WILDCARD;
+  char        footer[4]        = { 0x00, 0x21, 0x00, 0x01 }; // NBSTAT/IP
+
+  struct timeval      timeout;
+  netbios_query_t     *q;
+  char                recv_buffer[512]; // Hu ?
+  ssize_t             recv;
+
+  assert(ns != NULL && entry != NULL);
+
+  // Prepare NBSTAT query packet
+  q = netbios_query_new(34 + 4, 1, NETBIOS_OP_NAME_QUERY);
+  netbios_query_append(q, broadcast_name, strlen(broadcast_name) + 1);
+  netbios_query_append(q, footer, 4);
+  q->packet->queries = htons(1);
+
+  // 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));
+
+  // Now wait for a reply and pray
+  timeout.tv_sec = 1;
+  timeout.tv_usec = 500;
+  recv = netbios_ns_recv(ns->socket, (void *)recv_buffer, 512, &timeout, 0, 0);
+
+  if (recv <= 0)
+    goto error;
+  else if (BDSM_DEBUG)
+    fprintf(stderr, "netbios_ns_inverse, received a reply for '%s' !\n",
+            inet_ntoa(entry->address));
+
+
+  // Now we've got something, let's find the <20> name
+  netbios_query_packet_t  *p = (netbios_query_packet_t *)recv_buffer;
+  uint8_t                 name_count;
+  uint8_t                 name_idx;
+  char                    *names;
+  char                    *current_name;
+
+  printf("Queried name length: %u\n", p->payload[0]);
+  name_count = p->payload[p->payload[0] + 12];
+  printf("Number of names: %hhu\n", name_count);
+  names = p->payload + p->payload[0] + 13;
+
+  for(name_idx = 0; name_idx < name_count; name_idx++)
+  {
+    current_name = names + name_idx * 18;
+    if (current_name[15] == 0x20)
+    {
+      printf("Found name : %s\n", current_name);
+      memcpy(entry->name, current_name, NETBIOS_NAME_LENGTH + 2);
+      return (1);
+    }
+  }
+
+  return(0);
+
+  error:
+    perror("netbios_ns_inverse: ");
+    return (0);
+}

+ 69 - 0
src/netbios_ns_entry.c

@@ -0,0 +1,69 @@
+// This file is part of libdsm
+// Author: Julien 'Lta' BALLET <contact@lta.io>
+// Copyright VideoLabs 2014
+// License: MIT License
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "bdsm/netbios_ns.h"
+
+void                netbios_ns_entry_clear(netbios_ns_t *ns)
+{
+  netbios_ns_entry_t  *next;
+
+  assert(ns != NULL);
+
+  while (ns->entries != NULL)
+  {
+    next = ns->entries->next;
+    free(ns->entries->next);
+    ns->entries = next;
+  }
+}
+
+netbios_ns_entry_t *netbios_ns_entry_add(netbios_ns_t *ns, const char *name,
+                                         uint32_t ip)
+{
+  netbios_ns_entry_t  *entry;
+
+  entry = malloc(sizeof(netbios_ns_entry_t));
+  assert(entry != NULL);
+  memset((void *)entry, 0, sizeof(netbios_ns_entry_t));
+
+  if (name != NULL)
+    memcpy(entry->name, name, NETBIOS_NAME_LENGTH + 2);
+
+  entry->address.s_addr = ip;
+  entry->next           = ns->entries;
+
+  ns->entries = entry;
+
+  return (ns->entries);
+}
+// Find an entry in the list. Search by name if name is not NULL,
+// or by ip otherwise
+netbios_ns_entry_t *netbios_ns_entry_find(netbios_ns_t *ns, const char *by_name,
+                                          uint32_t ip)
+{
+  netbios_ns_entry_t  *found = NULL;
+  netbios_ns_entry_t  *iter;
+
+  assert(ns != NULL);
+
+  iter = ns->entries;
+  while(iter != NULL && found == NULL)
+  {
+    if (by_name != NULL)
+    {
+      if (!strncmp(by_name, iter->name, NETBIOS_NAME_LENGTH + 1))
+        found = iter;
+    }
+    else if (iter->address.s_addr == ip)
+      found = iter;
+  }
+
+  return (found);
+}

+ 1 - 2
src/netbios_query.c

@@ -11,7 +11,7 @@
 
 #include "bdsm/netbios_query.h"
 
-netbios_query_t   *netbios_query_new(uint16_t trn_id, size_t payload_size,
+netbios_query_t   *netbios_query_new(size_t payload_size,
                                      int is_query, char opcode)
 {
   netbios_query_t *q;
@@ -26,7 +26,6 @@ netbios_query_t   *netbios_query_new(uint16_t trn_id, size_t payload_size,
 
   q->payload_size = payload_size;
 
-  q->packet->trn_id = htons(trn_id);
   q->packet->flags  = htons(opcode << 11);
   netbios_query_set_flag(q, NETBIOS_FLAG_QUERY, !is_query);
 

+ 171 - 0
src/netbios_session.c

@@ -0,0 +1,171 @@
+// This file is part of libdsm
+// Author: Julien 'Lta' BALLET <contact@lta.io>
+// Copyright VideoLabs 2014
+// License: MIT License
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "bdsm/netbios_session.h"
+#include "bdsm/netbios_utils.h"
+
+static int        open_socket_and_connect(netbios_session_t *s)
+{
+  if ((s->socket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+    goto error;
+  if (connect(s->socket, (struct sockaddr *)&s->remote_addr, sizeof(s->remote_addr)) <0)
+    goto error;
+
+  return (1);
+
+  error:
+    perror("netbios_session_new, open_socket: ");
+    return (0);
+}
+
+netbios_session_t *netbios_session_new(uint32_t ip_addr)
+{
+  netbios_session_t *session;
+  size_t            packet_size;
+
+  session = (netbios_session_t *)malloc(sizeof(netbios_session_t));
+  assert(session);
+  memset((void *) session, 0, sizeof(netbios_session_t));
+
+  session->packet_payload_size = NETBIOS_SESSION_PAYLOAD;
+  packet_size = sizeof(netbios_session_packet_t) + session->packet_payload_size;
+  session->packet = (netbios_session_packet_t *)malloc(packet_size);
+  assert(session->packet);
+
+  session->remote_addr.sin_family       = AF_INET;
+  session->remote_addr.sin_port         = htons(NETBIOS_PORT_SESSION);
+  session->remote_addr.sin_addr.s_addr  = ip_addr;
+  if (!open_socket_and_connect(session))
+  {
+    netbios_session_destroy(session);
+    return (0);
+  }
+
+  return(session);
+}
+
+void              netbios_session_destroy(netbios_session_t *s)
+{
+  if (!s)
+    return;
+  close(s->socket);
+  if (s->packet)
+    free(s->packet);
+  free(s);
+}
+
+int               netbios_session_connect(netbios_session_t *s, char *name)
+{
+  netbios_session_packet_t  *received;
+  ssize_t                   recv_size;
+  ssize_t                   recv_payload_size;
+  char                      *encoded_name;
+
+  assert(s && s->packet && s->socket);
+
+  // Send the Session Request message
+  netbios_session_packet_init(s, NETBIOS_OP_SESSION_REQ);
+  encoded_name = netbios_name_encode(name, 0, NETBIOS_FILESERVER);
+  netbios_session_packet_append(s, encoded_name, strlen(encoded_name) + 1);
+  free(encoded_name);
+  encoded_name = netbios_name_encode("LIBDSM", 0, NETBIOS_FILESERVER);
+  netbios_session_packet_append(s, encoded_name, strlen(encoded_name) + 1);
+  free(encoded_name);
+
+  s->state = NETBIOS_SESSION_CONNECTING;
+  if (!netbios_session_packet_send(s))
+    goto error;
+
+  // Now receiving the reply from the server.
+  recv_size = netbios_session_packet_recv(s);
+  if (recv_size < sizeof(netbios_session_packet_t))
+    goto error;
+  recv_payload_size = recv_size - sizeof(netbios_session_packet_t);
+
+  received = (netbios_session_packet_t *)&s->recv_buffer;
+  // Reply was negative, we are not connected :(
+  if (received->opcode != NETBIOS_OP_SESSION_REQ_OK)
+  {
+    s->state = NETBIOS_SESSION_REFUSED;
+    return (0);
+  }
+
+  // Reply was OK, a netbios sessions has been established
+  s->state = NETBIOS_SESSION_CONNECTED;
+  return(1);
+
+  error:
+    s->state = NETBIOS_SESSION_ERROR;
+    return (0);
+}
+
+void              netbios_session_packet_init(netbios_session_t *s,
+                                              uint8_t opcode)
+{
+  size_t          packet_size;
+
+  assert(s);
+
+  packet_size = s->packet_payload_size + sizeof(netbios_session_packet_t);
+  memset((void *)s->packet, 0, packet_size);
+
+  s->packet_cursor = 0;
+  s->packet->opcode = opcode;
+}
+
+int               netbios_session_packet_append(netbios_session_t *s,
+                                                char *data, size_t size)
+{
+  char  *start;
+
+  assert(s && s->packet);
+
+  if (s->packet_payload_size - s->packet_cursor < size)
+    return (-1);
+
+  start = ((char *)&s->packet->payload) + s->packet_cursor;
+  memcpy(start, data, size);
+  s->packet_cursor += size;
+
+  return (0);
+}
+
+int               netbios_session_packet_send(netbios_session_t *s)
+{
+  ssize_t         to_send;
+  ssize_t         sent;
+
+  assert(s && s->packet && s->socket && s->state > 0);
+
+  s->packet->length = htons(s->packet_cursor);
+  to_send           = sizeof(netbios_session_packet_t) + s->packet_cursor;
+  sent              = send(s->socket, (void *)s->packet, to_send, 0);
+
+  if (sent != to_send)
+  {
+    perror("netbios_session_packet_send: Unable to send (full) packet");
+    return (0);
+  }
+
+  return (sent);
+}
+
+ssize_t           netbios_session_packet_recv(netbios_session_t *s)
+{
+  assert(s && s->socket && s->state > 0);
+
+  return (recv(s->socket, (void *)&(s->recv_buffer), NETBIOS_SESSION_BUFFER, 0));
+}
+

+ 1 - 0
src/netbios_utils.c

@@ -79,6 +79,7 @@ char  *netbios_name_encode(const char *name, char *domain,
   encoded_name = malloc(encoded_size);
   encoded_name[0] = 32; // length of the field;
   netbios_name_level1_encode(name, encoded_name + 1, type);
+  encoded_name[33] = 0;
 
   //printf("Encoded name (l2): %s.\n", encoded_name);