#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <arpa/inet.h>
#include <assert.h>
#include <ctype.h>
#include <fcntl.h>
#include <signal.h>

unsigned char session_id[8];

int sym_started = 0;

static inline unsigned long symx() {
  sym_started = 1;
  return 0;
}

int s = -1;

unsigned char ibuf[2048];

int
readn(void *bufx, int n)
{
  char *buf = bufx;
  while(n > 0){
    int cc = read(s, buf, n);
    if(cc <= 0)
      return -1;
    n -= cc;
    buf += cc;
  }
  return 0;
}

int
readmsg(void *bufx)
{
  unsigned char *buf = bufx;
  if(readn(buf, 4) < 0)
    return -1;
  int n = (buf[2] << 8) | buf[3];
  if(readn(buf+4, n) < 0)
    return -1;
  return n + 4;
}

int
header(char *buf, int command, int do_sym)
{
  int ii = 0;

  unsigned int status = 0;
  if(do_sym)
    status ^= symx();
  unsigned int flags = 1; // 1=REDIRECT
  if(do_sym)
    flags ^= symx();

  // SMB-over-TCP 4-byte header
  buf[ii++] = 0; // must be zero
  buf[ii++] = 0; // high byte of len
  *(short*)(buf+ii) = htons(sizeof(buf)-4); // non-inclusive len, filled later
  ii += 2;
  
  // SMB2 SYNC Packet Header, MS-SMB2 2.2.1.2
  buf[ii++] = 0xfe;
  buf[ii++] = 'S';
  buf[ii++] = 'M';
  buf[ii++] = 'B';
  *(short*)(buf+ii) = 64; // StructureSize (of SMB2 header)
  ii += 2;
  *(short*)(buf+ii) = 0; // CreditCharge
  ii += 2;
  *(int*)(buf+ii) = status; // Status
  ii += 4;
  *(short*)(buf+ii) = command;
  ii += 2;
  *(short*)(buf+ii) = 6; // CreditRequest
  ii += 2;
  *(int*)(buf+ii) = flags; // Flags 
  ii += 4;
  *(int*)(buf+ii) = 0; // NextCommand
  ii += 4;
  static unsigned long long seq = 0;
  *(long long *)(buf+ii) = seq++; // MessageId
  ii += 8;
  *(int*)(buf+ii) = 0; // Reserved
  ii += 4;
  *(int*)(buf+ii) = 0; // TreeId
  ii += 4;
  memcpy(buf+ii, session_id, 8); // SessionId
  ii += 8;
  memset(buf+ii, 'd', 16); // Signature
  ii += 16;
  
  return ii;
}

void
negotiate(int do_sym)
{
  // SMB2 NEGOTIATE, MS-SMB2 2.2.3
  char buf[128+64];
  memset(buf, 1, sizeof(buf));

  int ii = header(buf, 0x0000, do_sym>1);
  int ii0 = ii;
  
  *(short*)(buf+ii) = 65; // StructureSize
  ii += 2;
  *(short*)(buf+ii) = 0; // SecurityMode
  ii += 2;
  *(short*)(buf+ii) = 0x0311; // DialectRevision
  ii += 2;
  *(short*)(buf+ii) = 1; // NegotiateContextCount
  ii += 2;
  ii += 16; // ServerGuid from MS-DTYP
  *(int*)(buf+ii) = 0x3e; // Capabilities
  ii += 4;
  *(int*)(buf+ii) = 8192; // MaxTransactSize
  ii += 4;
  *(int*)(buf+ii) = 8192; // MaxReadSize
  ii += 4;
  *(int*)(buf+ii) = 8192; // MaxWriteSize
  ii += 4;
  *(long long *)(buf+ii) = 1; // SystemTime
  ii += 8;
  *(long long *)(buf+ii) = 1; // ServerStartTime
  ii += 8;
  *(short*)(buf+ii) = 0; // SecurityBufferOffset
  ii += 2;
  *(short*)(buf+ii) = 0; // SecurityBufferLength
  ii += 2;
  int context_offset_i = ii;
  *(int*)(buf+ii) = 0; // NegotiateContextOffset
  ii += 4;

  // SecurityBuffer

  while((ii - 4) % 8)
    ii++;

  // NegotiateContextList
  *(int*)(buf+context_offset_i) = ii - 4; // NegotiateContextOffset
  
  {
    *(short*)(buf+ii) = 0x0001; // SMB2_PREAUTH_INTEGRITY_CAPABILITIES
    ii += 2;
    int data_length_i = ii;
    *(short*)(buf+ii) = 0; // DataLength
    ii += 2;
    *(int*)(buf+ii) = 0; // Reserved
    ii += 4;
    int data_field_i = ii;
    *(short*)(buf+ii) = 1; // HashAlgorithmCount
    ii += 2;
    *(short*)(buf+ii) = 2; // SaltLength
    ii += 2;
    *(short*)(buf+ii) = 1; // SHA-512
    ii += 2;
    *(short*)(buf+ii) = 1; // Salt
    ii += 2;
    *(short*)(buf+data_length_i) = ii - data_field_i;
  }
  
  assert(ii <= sizeof(buf));
  
  ii = sizeof(buf);
  
  if(do_sym){
    for(int i = ii0; i+8 <= sizeof(buf); i += 8)
      *(long long *)(buf+i) ^= symx();
  }
  
  *(short*)(buf+2) = htons(ii-4); // non-inclusive len
  
  printf("negotiate writing %d bytes\n", ii); fflush(stdout);
  
  if(write(s, buf, ii) <= 0){
    perror("write");
  }
}

void
setup(int do_sym)
{
  // SMB2 SESSION_SETUP, MS-SMB2 2.2.6
  char buf[128+64];
  memset(buf, 1, sizeof(buf));

  session_id[7] = 1;

  int ii = header(buf, 0x0001, do_sym>1);
  int ii0 = ii;
  
  *(short*)(buf+ii) = 9; // StructureSize
  ii += 2;
  *(short*)(buf+ii) = 1; // SessionFlags, 1=guest
  ii += 2;
  *(short*)(buf+ii) = (ii - 4 + 4); // SecurityBufferOffset
  ii += 2;
  *(short*)(buf+ii) = 16; // SecurityBufferLength
  ii += 2;

  memcpy(buf+ii, "NTLMSSP", 8);
  
  assert(ii <= sizeof(buf));
  
  ii = sizeof(buf);
  
  if(do_sym){
    for(int i = ii0; i+8 <= sizeof(buf); i += 8)
      *(long long *)(buf+i) ^= symx();
  }
  
  *(short*)(buf+2) = htons(ii-4); // non-inclusive len
  
  printf("setup writing %d bytes\n", ii); fflush(stdout);
  
  if(write(s, buf, ii) <= 0){
    perror("write");
  }
}

void
tree_connect(int do_sym)
{
  // SMB2 TREE_CONNECT, MS-SMB2 2.2.10
  char buf[128+64];
  memset(buf, 1, sizeof(buf));

  int ii = header(buf, 0x0003, do_sym>1);
  int ii0 = ii;
  
  *(short*)(buf+ii) = 16; // StructureSize
  ii += 2;
  buf[ii++] = 0x02; // ShareType, 1=disk, 2=pipe, 3=print
  buf[ii++] = 0; // Reserved
  *(int*)(buf+ii) = 0x33; // ShareFlags
  ii += 4;
  // *(int*)(buf+ii) = 0x1f8; // Capabilities
  *(int*)(buf+ii) = 0xfff8; // Capabilities
  ii += 4;
  *(int*)(buf+ii) = 0xffffffff; // MaximalAccess
  ii += 4;
  
  assert(ii <= sizeof(buf));
  
  ii = sizeof(buf);
  
  if(do_sym){
    for(int i = ii0; i+8 <= sizeof(buf); i += 8)
      *(long long *)(buf+i) ^= symx();
  }
  
  *(short*)(buf+2) = htons(ii-4); // non-inclusive len
  
  printf("tree_connect writing %d bytes\n", ii); fflush(stdout);
  
  if(write(s, buf, ii) <= 0){
    perror("write");
  }
}

void
smb_ioctl(int do_sym)
{
  // SMB2 IOCTL, MS-SMB2 2.2.32
  char buf[256];
  memset(buf, 0x99, sizeof(buf));

  int ii = header(buf, 0x000b, do_sym>1);
  int ii0 = ii;
  
  *(short*)(buf+ii) = 49; // StructureSize
  ii += 2;
  *(short*)(buf+ii) = 0; // Reserved
  ii += 2;
  *(int*)(buf+ii) = 0x0011c017; // CtlCode
  ii += 4;
  memset(buf+ii, 0xff, 16);
  ii += 16; // FileId
  *(int*)(buf+ii) = (ii-4+24); // InputOffset
  ii += 4;
  *(int*)(buf+ii) = 0; // InputCount
  ii += 4;
  *(int*)(buf+ii) = (ii-4+16); // OutputOffset
  ii += 4;
  int count_i = ii;
  *(int*)(buf+ii) = 128; // OutputCount
  ii += 4;
  *(int*)(buf+ii) = 0; // Flags
  ii += 4;
  *(int*)(buf+ii) = 0; // Reserved
  ii += 4;

  int ostart = ii;

#if 0
  *(short*)(buf+ii) = 2; // consumed_ucs
  ii += 2;
  *(short*)(buf+ii) = 0; // num_referrals
  ii += 2;
  buf[ii++] = 0;
  buf[ii++] = 'x';
#endif

#if 1
  // DCE RPC connection-oriented reply.
  buf[ii++] = 5; // rpc_vers
  buf[ii++] = 0; // rpc_vers_minor
  buf[ii++] = 12; // ptype, 12=bind ack
  buf[ii++] = 3; // flags, first and last fragment
  buf[ii++] = 0x10; // packed_drep?
  buf[ii++] = 0x00; // packed_drep?
  buf[ii++] = 0x00; // packed_drep?
  buf[ii++] = 0x00; // packed_drep?
  *(short*)(buf+ii) = 64; // frag_length
  ii += 2;
  *(short*)(buf+ii) = 0; // auth_length
  ii += 2;
  *(int*)(buf+ii) = 1; // call_id, XXX
  ii += 4;

  // bind_ack reply
  *(short*)(buf+ii) = 1024; // max_xmit_frag
  ii += 2;
  *(short*)(buf+ii) = 1024; // max_recv_frag
  ii += 2;
  *(int*)(buf+ii) = 1; // assoc_group_id
  ii += 4;
  *(short*)(buf+ii) = 0; // port_any_t
  ii += 2;
  while(ii % 4)
    buf[ii++] = 0;
  // p_result_list_t
  buf[ii++] = 1; // n_results
  buf[ii++] = 0; // reserved
  *(short*)(buf+ii) = 0; // reserved
  ii += 2;
  // p_result_t
  *(short*)(buf+ii) = 0; // result
  ii += 2;
  *(short*)(buf+ii) = 0; // reason
  ii += 2;
  // p_syntax_id_t
  unsigned char uuid[] = {
    0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f,
    0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60,
  };
  memcpy(buf+ii, uuid, sizeof(uuid));
  ii += sizeof(uuid);
  *(int*)(buf+ii) = 2; // if_version
  ii += 4;
#endif

  *(int*)(buf+count_i) = ii - ostart;

  int ii1 = ii;

  assert(ii <= sizeof(buf));
  
  ii = sizeof(buf);
  
  if(do_sym){
    for(int i = ii1; i+8 <= sizeof(buf); i += 8)
      *(long long *)(buf+i) ^= symx();
  }
  
  *(short*)(buf+2) = htons(ii-4); // non-inclusive len
  
  printf("ioctl writing %d bytes\n", ii); fflush(stdout);
  
  if(write(s, buf, ii) <= 0){
    perror("write");
  }
}

void
smb_create(int do_sym)
{
  // SMB2 CREATE, MS-SMB2 2.2.14
  char buf[128+64];
  memset(buf, 0xff, sizeof(buf));

  int ii = header(buf, 0x0005, do_sym>1);
  int ii0 = ii;
  
  *(short*)(buf+ii) = 89; // StructureSize
  ii += 2;
  buf[ii++] = 0; // OplockLevel
  buf[ii++] = 0; // Flags
  *(int*)(buf+ii) = 0; // CreateAction
  ii += 4;
  *(long long*)(buf+ii) = 1; // CreationTime
  ii += 8;
  *(long long*)(buf+ii) = 1; // LastAccessTime
  ii += 8;
  *(long long*)(buf+ii) = 1; // LastWriteTime
  ii += 8;
  *(long long*)(buf+ii) = 1; // ChangeTime
  ii += 8;
  *(long long*)(buf+ii) = 1; // AllocationSize
  ii += 8;
  *(long long*)(buf+ii) = 1; // EndofFile
  ii += 8;
  // 0x10 directory
  *(int*)(buf+ii) = 0x10; // FileAttributes
  ii += 4;
  *(int*)(buf+ii) = 0; // Reserved2
  ii += 4;
  ii += 16; // FileId
  *(int*)(buf+ii) = (ii - 4 + 8); // CreateContextsOffset
  ii += 4;
  *(int*)(buf+ii) = 32; // CreateContextsLength
  ii += 4;

  // one context
  *(int*)(buf+ii) = 0; // Next
  ii += 4;
  *(short*)(buf+ii) = 16; // NameOffset
  ii += 2;
  *(short*)(buf+ii) = 4; // NameLength
  ii += 2;
  *(short*)(buf+ii) = 0; // Reserved
  ii += 2;
  *(short*)(buf+ii) = 24; // DataOffset
  ii += 2;
  *(int*)(buf+ii) = 8; // DataLength
  ii += 4;
  

  // SMB2_CREATE_QUERY_MAXIMAL_ACCESS_RESPONSE
  *(int*)(buf+ii) = 0x4d784163; // Name
  ii += 4;
  ii += 4; // pad
  *(int*)(buf+ii) = 0; // QueryStatus
  ii += 4;
  *(int*)(buf+ii) = 0xffffffff; // MaximalAccess
  ii += 4;

  assert(ii <= sizeof(buf));
  
  ii = sizeof(buf);
  
  if(do_sym){
    for(int i = ii0; i+8 <= sizeof(buf); i += 8)
      *(long long *)(buf+i) ^= symx();
  }
  
  *(short*)(buf+2) = htons(ii-4); // non-inclusive len
  
  printf("create writing %d bytes\n", ii); fflush(stdout);
  
  if(write(s, buf, ii) <= 0){
    perror("write");
  }
}

void
smb_close(int do_sym)
{
  char buf[128+64];
  memset(buf, 0xff, sizeof(buf));

  int ii = header(buf, 0x0006, do_sym>1);
  int ii0 = ii;
  
  *(short*)(buf+ii) = 60; // StructureSize
  ii += 2;
  *(short*)(buf+ii) = 0; // Flags
  ii += 2;
  *(int*)(buf+ii) = 0; // Reserved
  ii += 4;
  *(long long*)(buf+ii) = 1; // CreationTime
  ii += 8;
  *(long long*)(buf+ii) = 1; // LastAccessTime
  ii += 8;
  *(long long*)(buf+ii) = 1; // LastWriteTime
  ii += 8;
  *(long long*)(buf+ii) = 1; // ChangeTime
  ii += 8;
  *(long long*)(buf+ii) = 1; // AllocationSize
  ii += 8;
  *(long long*)(buf+ii) = 1; // EndofFile
  ii += 8;
  // 0x10 directory
  *(int*)(buf+ii) = 0x10; // FileAttributes
  ii += 4;

  assert(ii <= sizeof(buf));
  
  ii = sizeof(buf);
  
  if(do_sym){
    for(int i = ii0; i+8 <= sizeof(buf); i += 8)
      *(long long *)(buf+i) ^= symx();
  }
  
  *(short*)(buf+2) = htons(ii-4); // non-inclusive len
  
  printf("close writing %d bytes\n", ii); fflush(stdout);
  
  if(write(s, buf, ii) <= 0){
    perror("write");
  }
}
  
void
query_directory(int any, int do_sym)
{
  char buf[256+128];
  memset(buf, 0xff, sizeof(buf));

  int ii = header(buf, 0x000e, do_sym>1);
  int ii0 = ii;

  if(any == 0){
    *(int*)(buf+12) = 0x80000006; // STATUS_NO_MORE_FILES
  }
  
  *(short*)(buf+ii) = 9; // StructureSize
  ii += 2;
  *(short*)(buf+ii) = (ii - 4 + 6); // OutputBufferOffset
  ii += 2;
  *(int*)(buf+ii) = any ? 106 : 0; // OutputBufferLength
  ii += 4;

  // probably FileBothDirectoryInformation, MS-FSCC 2.4.8
  *(int*)(buf+ii) = 0; // NextEntryOffset
  ii += 4;
  *(int*)(buf+ii) = 1; // FileIndex
  ii += 4;
  *(long long*)(buf+ii) = 1; // CreationTime
  ii += 8;
  *(long long*)(buf+ii) = 1; // LastAccessTime
  ii += 8;
  *(long long*)(buf+ii) = 1; // LastWriteTime
  ii += 8;
  *(long long*)(buf+ii) = 1; // ChangeTime
  ii += 8;
  *(long long*)(buf+ii) = 1; // EndofFile
  ii += 8;
  *(long long*)(buf+ii) = 1; // AllocationSize
  ii += 8;
  // 0x10 directory
  *(int*)(buf+ii) = 0x10; // FileAttributes
  ii += 4;
  *(int*)(buf+ii) = 2; // FileNameLength
  ii += 4;
  *(int*)(buf+ii) = 0; // EaSize
  ii += 4;
  buf[ii++] = 2; // ShortNameLength
  buf[ii++] = 0; // Reserved
  buf[ii++] = 'f'; // ShortName
  buf[ii++] = 0;
  ii += 22;
  buf[ii++] = 'f'; // FileName
  buf[ii++] = 0;

  assert(ii <= sizeof(buf));
  
  ii = sizeof(buf);
  
  if(do_sym){
    for(int i = ii0; i+8 <= sizeof(buf); i += 8)
      *(long long *)(buf+i) ^= symx();
  }
  
  *(short*)(buf+2) = htons(ii-4); // non-inclusive len
  
  printf("query_directory writing %d bytes\n", ii); fflush(stdout);
  
  if(write(s, buf, ii) <= 0){
    perror("write");
  }
}
  
void
query_info(int do_sym)
{
  char buf[256+128];
  memset(buf, 0xff, sizeof(buf));

  int ii = header(buf, 0x0010, do_sym>1);
  int ii0 = ii;

  *(short*)(buf+ii) = 9; // StructureSize
  ii += 2;
  *(short*)(buf+ii) = (ii - 4 + 6); // OutputBufferOffset
  ii += 2;
  *(int*)(buf+ii) = 106; // OutputBufferLength
  ii += 4;

  // probably FileBothDirectoryInformation, MS-FSCC 2.4.8
  *(int*)(buf+ii) = 0; // NextEntryOffset
  ii += 4;
  *(int*)(buf+ii) = 1; // FileIndex
  ii += 4;
  *(long long*)(buf+ii) = 1; // CreationTime
  ii += 8;
  *(long long*)(buf+ii) = 1; // LastAccessTime
  ii += 8;
  *(long long*)(buf+ii) = 1; // LastWriteTime
  ii += 8;
  *(long long*)(buf+ii) = 1; // ChangeTime
  ii += 8;
  *(long long*)(buf+ii) = 1; // EndofFile
  ii += 8;
  *(long long*)(buf+ii) = 1; // AllocationSize
  ii += 8;
  // 0x10 directory
  *(int*)(buf+ii) = 0x10; // FileAttributes
  ii += 4;
  *(int*)(buf+ii) = 2; // FileNameLength
  ii += 4;
  *(int*)(buf+ii) = 0; // EaSize
  ii += 4;
  buf[ii++] = 2; // ShortNameLength
  buf[ii++] = 0; // Reserved
  buf[ii++] = 'f'; // ShortName
  buf[ii++] = 0;
  ii += 22;
  buf[ii++] = 'f'; // FileName
  buf[ii++] = 0;

  assert(ii <= sizeof(buf));
  
  ii = sizeof(buf);
  
  if(do_sym){
    for(int i = ii0; i+8 <= sizeof(buf); i += 8)
      *(long long *)(buf+i) ^= symx();
  }
  
  *(short*)(buf+2) = htons(ii-4); // non-inclusive len
  
  printf("query_info writing %d bytes\n", ii); fflush(stdout);
  
  if(write(s, buf, ii) <= 0){
    perror("write");
  }
}
  
void
smb_read(int do_sym)
{
  char buf[256];
  memset(buf, 1, sizeof(buf));

  int ii = header(buf, 0x0008, do_sym>1);
  int ii0 = ii;

  *(short*)(buf+ii) = 17; // StructureSize
  ii += 2;
  buf[ii] = ii - 4 + 14; // DataOffset
  ii++;
  buf[ii++] = 0; // Reserved
  *(int*)(buf+ii) = 128; // DataLength
  ii += 4;
  *(int*)(buf+ii) = 0; // DataRemaining
  ii += 4;
  *(int*)(buf+ii) = 0; // Flags
  ii += 4;

  unsigned char data[] = {
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x05, 0x00, 0x02,
    0x03, 0x10, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x02, 0x00,
    0x00, 0x00, 0x00, 0x00, 0xf2, 0x01, 0x01, 0x01, 0x01, 0x01, 0x21,
    0x01, 0x00, 0x00, 0x21, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
    0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
  };
  memcpy(buf+ii, data, sizeof(data));
  ii += sizeof(data);

  assert(ii <= sizeof(buf));
  
  ii = sizeof(buf);
  
  *(short*)(buf+2) = htons(ii-4); // non-inclusive len
  
  printf("read writing %d bytes\n", ii); fflush(stdout);

  if(write(s, buf, ii) <= 0){
    perror("write");
  }
}
  
void
change_notify(int do_sym)
{
  char buf[128];
  memset(buf, 0xff, sizeof(buf));

  int ii = header(buf, 0x000f, do_sym>1);
  int ii0 = ii;

  *(short*)(buf+ii) = 9; // StructureSize
  ii += 2;
  *(short*)(buf+ii) = (ii - 4 + 6); // OutputBufferOffset
  ii += 2;
  *(int*)(buf+ii) = 14; // OutputBufferLength
  ii += 4;

  // FILE_NOTIFY_INFORMATION
  *(int*)(buf+ii) = 0; // NextEntryOffset
  ii += 4;
  *(int*)(buf+ii) = 7; // Action
  ii += 4;
  *(int*)(buf+ii) = 2; // FileNameLength
  ii += 4;
  buf[ii++] = 'd';
  buf[ii++] = 0;

  assert(ii <= sizeof(buf));
  
  ii = sizeof(buf);
  
  if(do_sym){
    for(int i = ii0; i+8 <= sizeof(buf); i += 8)
      *(long long *)(buf+i) ^= symx();
  }
  
  *(short*)(buf+2) = htons(ii-4); // non-inclusive len
  
  printf("change_notify writing %d bytes\n", ii); fflush(stdout);
  
  if(write(s, buf, ii) <= 0){
    perror("write");
  }
}

char *command_names[] = {
  "NEGOTIATE",
  "SESSION_SETUP",
  "LOGOFF",
  "TREE_CONNECT",
  "TREE_DISCONNECT",
  "CREATE",
  "CLOSE",
  "FLUSH",
  "READ",
  "WRITE",
  "LOCK",
  "IOCTL",
  "CANCEL",
  "ECHO",
  "QUERY_DIRECTORY",
  "CHANGE_NOTIFY",
  "QUERY_INFO",
  "SET_INFO",
  "OPLOCK_BREAK",
  "0x0012",
};

int
main()
{
  signal(SIGPIPE, SIG_IGN);

  struct sockaddr_in sin;
  memset(&sin, 0, sizeof(sin));
  sin.sin_family = AF_INET;
  sin.sin_port = htons(445); // SMB over TCP

  int ss = socket(AF_INET, SOCK_STREAM, 0);
  int yes = 1;
  setsockopt(ss, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
  if(bind(ss, (struct sockaddr *)&sin, sizeof(sin)) < 0){
    perror("bind");
    exit(1);
  }
  listen(ss, 10);

  socklen_t sinlen = sizeof(sin);
  s = accept(ss, (struct sockaddr *)&sin, &sinlen);
  if(s < 0){
    perror("accept");
    exit(1);
  }
  close(ss);

  sleep(1);

  unsigned int command;
  int n;

  memset(ibuf, 0, sizeof(ibuf));
  n = readmsg(ibuf);
  command = *(short*)(ibuf+16);
  printf("read %d, command %d %s\n", n, command, command_names[command]);
  negotiate(0);

  memset(ibuf, 0, sizeof(ibuf));
  n = readmsg(ibuf);
  command = *(short*)(ibuf+16);
  printf("read %d, command %d %s\n", n, command, command_names[command]);
  if(command == 1){
    setup(0);
  }

  memset(ibuf, 0, sizeof(ibuf));
  n = readmsg(ibuf);
  command = *(short*)(ibuf+16);
  printf("read %d, command %d %s\n", n, command, command_names[command]);
  if(command == 3){
    tree_connect(0);
  }

  while(1){
    memset(ibuf, 0, sizeof(ibuf));
    n = readmsg(ibuf);
    if(n < 0)
      break;
    command = *(short*)(ibuf+16);
    printf("command 0x%02x %s\n", command, command_names[command]);
    if(command == 0xb){
      smb_ioctl(0);
    } else if(command == 5){
      smb_create(0);
    } else if(command == 6){
      smb_close(0);
    } else if(command == 0xe){
      static int nn = 0;
      query_directory(nn == 0 ? 1 : 0, 0);
      nn++;
    } else if(command == 0x10){
      query_info(0);
    } else if(command == 0x08){
      smb_read(1);
      break;
    } else if(command == 0x0f){
      change_notify(1);
      break;
    } else {
      break;
    }
  }
  
  sleep(1);
  close(s);
}
