Listing 2.1.
Opening a Session
With an SMB Server


#include <stdio.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>

#include <sys/poll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>


/* NBT Session Service Packet Type Codes
 */
#define SESS_MSG          0x00
#define SESS_REQ          0x81
#define SESS_POS_RESP     0x82
#define SESS_NEG_RESP     0x83
#define SESS_RETARGET     0x84
#define SESS_KEEPALIVE    0x85

/* NBT Session Service Error Codes
 */
#define ErrNLCalled       0x80
#define ErrNLCalling      0x81
#define ErrCalledNotPrsnt 0x82
#define ErrInsResources   0x83
#define ErrUnspecified    0x8F


ushort nbt_GetShort( uchar *src, int offset )
  /* ---------------------------------------------------- **
   * Read two bytes from an NBT message and convert them
   * to an unsigned short int.
   *
   * Note that we read the bytes in NBT byte order, which
   * is the opposite of SMB byte order.
   * ---------------------------------------------------- **
   */
  {
  ushort tmp;

  tmp = src[offset];
  tmp = (tmp << 8) | src[offset+1];

  return( tmp );
  } /* nbt_GetShort */


void Fail( char *fmt, ... )
  /* ---------------------------------------------------- **
   * This function formats and prints an error to stdout,
   * then exits the program.
   * A nice quick way to abandon ship.
   * ---------------------------------------------------- **
   */
  {
  va_list ap;

  va_start( ap, fmt );
  (void)fprintf( stdout, "Error: " );
  (void)vfprintf( stdout, fmt, ap );
  exit( EXIT_FAILURE );
  } /* Fail */


void NegResponse( uchar *bufr, int len )
  /* ---------------------------------------------------- **
   * Negative Session Response error reporting.
   *
   * The Negative Session Response message should always
   * be five bytes in length.  The final byte (bufr[4])
   * contains the error code.
   * ---------------------------------------------------- **
   */
  {
  if( len < 5 )
    Fail( "Truncated Negative Session Response.\n" );

  printf( "Negative Session Response: " );

  switch( bufr[4] )
    {
    case ErrNLCalled:
      printf( "Not listening on Called Name.\n" );
      break;
    case ErrNLCalling:
      printf( "Not listening *for* Calling Name.\n" );
      break;
    case ErrCalledNotPrsnt:
      printf( "Called Name not present.\n" );
      break;
    case ErrInsResources:
      printf( "Insufficient resources on server.\n" );
      break;
    case ErrUnspecified:
      printf( "Unspecified error.\n" );
      break;
    default:
      printf( "Unknown error.\n" );
      break;
    }
  } /* NegResponse */


void Retarget( uchar *bufr, int result )
  /* ---------------------------------------------------- **
   * This function is called if we receive a RETARGET
   * SESSION RESPONSE from the server.  The correct thing
   * to do would be to retry the connection, using the
   * returned information.  This function simply reports
   * the retarget response so that the user can manually
   * retry.
   * ---------------------------------------------------- **
   */
  {
  if( result < 10 )
    Fail( "Truncated Retarget Session Response.\n" );

  printf( "Retarget Session Response: " );
  printf( "IP = %d.%d.%d.%d, ",
          bufr[4], bufr[5], bufr[6], bufr[7] );
  printf( "Port = %d\n", nbt_GetShort( bufr, 8 ) );
  } /* Retarget */


int MakeSessReq( uchar *bufr,
                 uchar *Called,
                 uchar *Calling )
  /* ---------------------------------------------------- **
   * Create an NBT SESSION REQUEST message.
   * ---------------------------------------------------- **
   */
  {
  /* Write the header.
   */
  bufr[0] = SESS_REQ;
  bufr[1] = 0;
  bufr[2] = 0;
  bufr[3] = 68;         /* 2x34 bytes in length. */

  /* Copy the Called and Calling names into the buffer.
   */
  (void)memcpy( &bufr[4],  Called,  34 );
  (void)memcpy( &bufr[38], Calling, 34 );

  /* Return the total message length.
   */
  return( 72 );
  } /* MakeSessReq */


int RecvTimeout( int    sock,
                 uchar *bufr,
                 int    bsize,
                 int    timeout )
  /* ---------------------------------------------------- **
   * Attempt to receive a TCP packet within a specified
   * period of time.
   * ---------------------------------------------------- **
   */
  {
  int           result;
  struct pollfd pollfd[1];

  /* Wait timeout/1000 seconds for a message to arrive.
   */
  pollfd->fd      = sock;
  pollfd->events  = POLLIN;
  pollfd->revents = 0;
  result = poll( pollfd, 1, timeout );

  /* A result less than zero is an error.
   */
  if( result < 0 )
    Fail( "Poll() error: %s\n", strerror( errno ) );

  /* A result of zero is a timeout.
   */
  if( result == 0 )
    return( 0 );

  /* A result greater than zero means a message arrived,
   * so we attempt to read the message.
   */
  result = recv( sock, bufr, bsize, 0 );
  if( result < 0 )
    Fail( "Recv() error: %s\n", strerror( errno ) );

  /* Return the number of bytes received.
   * (Zero or more.)
   */
  return( result );
  } /* RecvTimeout */


void RequestNBTSession( int sock,
                        uchar *Called,
                        uchar *Calling )
  /* ---------------------------------------------------- **
   * Send an NBT SESSION REQUEST over the TCP connection,
   * then wait for a reply.
   * ---------------------------------------------------- **
   */
  {
  uchar bufr[128];
  int   result;

  /* Create the NBT Session Request message.
   */
  result = MakeSessReq( bufr, Called, Calling );

  /* Send the NBT Session Request message.
   */
  result = send( sock, bufr, result, 0 );
  if( result < 0 )
    Fail( "Error sending Session Request message: %s\n",
          strerror( errno ) );

  /* Now wait for and handle the reply (2 seconds).
   */
  result = RecvTimeout( sock, bufr, 128, 2000 );
  if( result == 0 )
    {
    printf( "Timeout waiting for NBT Session Response.\n" );
    return;
    }

  switch( *bufr )
    {
    case SESS_POS_RESP:
      /* We got what we wanted. */
      printf( "Positive Session Response.\n" );
      return;
    case SESS_NEG_RESP:
      /* Report an error. */
      NegResponse( bufr, result );
      exit( EXIT_FAILURE );
    case SESS_RETARGET:
      /* We've been retargeted. */
      Retarget( bufr, result );
      exit( EXIT_FAILURE );
    default:
      /* Not a response we expected. */
      Fail( "Unexpected response from server.\n" );
      break;
    }
  } /* RequestNBTSession */


int OpenTCPSession( struct in_addr dst_IP, ushort dst_port )
  /* ---------------------------------------------------- **
   * Open a TCP session with the specified server.
   * Return the connected socket.
   * ---------------------------------------------------- **
   */
  {
  int                sock;
  int                result;
  struct sockaddr_in sock_addr;

  /* Create the socket.
   */
  sock = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP );
  if( sock < 0 )
    Fail( "Failed to create socket(); %s.\n",
          strerror( errno ) );

  /* Connect the socket to the server at the other end.
   */
  sock_addr.sin_addr   = dst_IP;
  sock_addr.sin_family = AF_INET;
  sock_addr.sin_port   = htons( dst_port );
  result = connect( sock,
                    (struct sockaddr *)&sock_addr,
                    sizeof(struct sockaddr_in) );
  if( result < 0 )
    Fail( "Failed to create socket(); %s.\n",
          strerror( errno ) );

  return( sock );
  } /* OpenTCPSession */


int main( int argc, char *argv[] )
  /* ---------------------------------------------------- **
   * Program mainline.
   * Parse the command-line input and open the connection
   * to the server.
   * ---------------------------------------------------- **
   */
  {
  uchar          Called[34];
  uchar          Calling[34];
  struct in_addr dst_addr;
  int            dst_port = 139;
  int            sock;

  /* Check for the correct number of arguments.
   */
  if( argc < 3 || argc > 4 )
    {
    printf( "Usage:  %s <NAME> <IP> [<PORT>]\n",
            argv[0] );
    exit( EXIT_FAILURE );
    }

  /* Encode the destination name.
   */
  if( '*' == *(argv[1]) )
    (void)L2_Encode( Called, "*SMBSERVER", 0x20, 0x20, "" );
  else
    (void)L2_Encode( Called, argv[1], 0x20, 0x20, "" );

  /* Create a (bogus) Calling Name.
   */
  (void)L2_Encode( Calling, "SMBCLIENT", 0x20, 0x00, "" );

  /* Read the destination IP address.
   * We could do a little more work and resolve
   * the Called Name, but that would add a lot
   * of code to the example.
   */
  if( 0 == inet_aton( argv[2], &dst_addr ) )
    {
    printf( "Invalid IP.\n" );
    printf( "Usage:  %s <NAME> <IP> [<PORT>]\n",
            argv[0] );
    exit( EXIT_FAILURE );
    }

  /* Read the (optional) port number.
   */
  if( argc == 4 )
    {
    dst_port = atoi( argv[3] );
    if( 0 == dst_port )
      {
      printf( "Invalid Port number.\n" );
      printf( "Usage:  %s <NAME> <IP> [<PORT>]\n",
              argv[0] );
      exit( EXIT_FAILURE );
      }
    }

  /* Open the session.
   */
  sock = OpenTCPSession( dst_addr, dst_port );

  /* Comment out the next call for raw TCP.
   */
  RequestNBTSession( sock, Called, Calling );

  /* ** Do real work here. ** */

  return( EXIT_SUCCESS );
  } /* main */


$Revision: 1.9 $
$Date: 2003/02/23 02:09:20 $
[W3C Validated] Copyright © 2002 Christopher R. Hertel 
Released under the terms of the LGPL