# ip65 technical reference

# File : ip65/cifs.s

    a simple NETBIOS over TCP server
    aka "Common Internet File System"

     refs: RFC1001, RFC1002, "Implementing CIFS" - http://ubiqx.org/cifs/

## functions

function | description
--- | ---
cifs_l1_decode |
    given a 'level 1 encoded' hostname, decode to ASCII .

    inputs:
     AX: pointer to encoded hostname to be decoded
    outputs:
     AX: pointer to decoded hostname (will be 16 byte hostname, right padded with spaces, nul terminated)
cifs_l1_encode |
    given an ASCII (or PETSCII) hostname, convert to
    canonical 'level 1 encoded' form.

    only supports the default scope (' ' : 0x20)
    inputs:
     AX: pointer to null terminated hostname to be encoded
    outputs:
     AX: pointer to decoded hostname
cifs_start |
    start a CIFS (SMB) server process, and advertise the specified hostname on the local LAN

    inputs:
    AX = ptr to hostname to be used
    outputs:
     none

## implementation

    ;a simple NETBIOS over TCP server
    ;aka "Common Internet File System"
    ;
    ; refs: RFC1001, RFC1002, "Implementing CIFS" - http://ubiqx.org/cifs/

    .include "../inc/common.i"

    .ifndef KPR_API_VERSION_NUMBER
      .define EQU     =
      .include "../inc/kipper_constants.i"
    .endif

    DEFAULT_CIFS_CMD_BUFFER = $6800
    DEFAULT_SMB_RESPONSE_BUFFER=$6000
    .export cifs_l1_encode
    .export cifs_l1_decode
    .export cifs_start

    .import copymem
    .importzp copy_src
    .importzp copy_dest

    .import cfg_ip
    .import output_buffer
    .importzp udp_data
    .import udp_send
    .import udp_inp
    .importzp udp_data
    .import udp_send_dest
    .import udp_send_src_port
    .import udp_send_dest_port
    .import udp_send_len
    .importzp ip_src
    .importzp ip_dest
    .import ip_data
    .import ip_inp
    .import tcp_close
    .import tcp_listen
    .import tcp_callback
    .import tcp_inbound_data_length
    .import tcp_inbound_data_ptr
    .import tcp_send
    .import tcp_send_data_len

    .import ip65_process
    .import udp_add_listener
    .import udp_callback

    nbns_txn_id = 0
    nbns_opcode=2
    nbns_flags_rcode=3
    nbns_qdcount=4
    nbns_ancount=6
    nbns_nscount=8
    nbns_arcount=10
    nbns_question_name=12
    nbns_service_type=43

    nbns_my_ip=64
    nbns_registration_message_length=68

    ;given an ASCII (or PETSCII) hostname, convert to
    ;canonical 'level 1 encoded' form.
    ;
    ;only supports the default scope (' ' : 0x20)
    ;inputs:
    ; AX: pointer to null terminated hostname to be encoded
    ;outputs:
    ; AX: pointer to decoded hostname
    cifs_l1_encode:
      stax copy_src
      lda #0
      tax
      sta hostname_buffer+32
    @empty_buffer_loop:
      lda  #$43
      sta  hostname_buffer,x
      inx
      lda #$41
      sta  hostname_buffer,x
      inx
      cpx  #$20
      bmi  @empty_buffer_loop
      ldy  #0
      ldx  #0
    @copy_loop:

      lda  (copy_src),y
      beq  @done
      lsr
      lsr
      lsr
      lsr
      clc
      adc #$41
      sta  hostname_buffer,x

      inx
      lda  (copy_src),y
      and #$0F
      clc
      adc #$41
      sta  hostname_buffer,x
      inx
      iny
      cpx  #$1D
      bmi  @copy_loop
    @done:
      ldax #hostname_buffer
      rts

    ;given a 'level 1 encoded' hostname, decode to ASCII .
    ;
    ;inputs:
    ; AX: pointer to encoded hostname to be decoded
    ;outputs:
    ; AX: pointer to decoded hostname (will be 16 byte hostname, right padded with spaces, nul terminated)
    cifs_l1_decode:
      stax copy_src
      ldy  #0
      ldx  #0
    @decode_loop:
      lda  (copy_src),y
      sec
      sbc  #$41
      asl
      asl
      asl
      asl
      sta hi_nibble
      iny
      lda  (copy_src),y
      sec
      sbc  #$41
      clc
      adc  hi_nibble
      sta  hostname_buffer,x
      iny
      inx
      cpx  #$10
      bmi  @decode_loop
      lda  #0
      sta  hostname_buffer,x
      ldax #hostname_buffer
      rts

    ;start a CIFS (SMB) server process, and advertise the specified hostname on the local LAN
    ;
    ;inputs:
    ;AX = ptr to hostname to be used
    ;outputs:
    ; none
    cifs_start:

      ;save the hostname in 'raw' form
      stax  copy_src
      ldax  #raw_local_hostname
      stax  copy_dest
      ldax  #$0f
      stx raw_local_hostname+15
      jsr copymem

      ;set up callbacks
      ldax #nbns_callback
      stax udp_callback
      ldax  #137
      jsr udp_add_listener

      ldax #nbns_callback
      stax udp_callback
      ldax  #137
      jsr udp_add_listener

      ldax  #raw_local_hostname
      jsr cifs_l1_encode
      ldx #0
    @copy_hostname_loop:
      lda hostname_buffer,x
      sta local_hostname,x
      inx
      cpx #$21
      bmi @copy_hostname_loop

      jsr cifs_advertise_hostname
      jsr cifs_advertise_hostname

      ldax  #nb_session_callback
      stax  tcp_callback
    @listen:

      ldax  #-4                 ;start at -4, to skip the NBT header length
      stax cifs_cmd_length

      ldax   cifs_cmd_buffer
      stax  cifs_cmd_buffer_ptr
      ldax  #139
      stx connection_closed

      jsr tcp_listen

    @loop:
      inc $d020 ;FIXME
      jsr ip65_process
      lda connection_closed
      beq @loop

      jmp @listen
      rts

    ;broadcast a Name Registration Request message to the local LAN
    cifs_advertise_hostname:

      ; advertise the 'server' service for own hostname
      ;overwrite the hostname in 'DNS compressed form'
      ;we assume this hostname ends with <20>
      lda #$20  ;indicates what follows is a netbios name
      sta output_buffer+nbns_question_name

      ldx #0
    @copy_hostname_loop:
      lda local_hostname,x
      sta registration_request_servername+1,x
      inx
      cpx #$21
      bmi @copy_hostname_loop

      jsr  @send_nbns_message

      ;copy our encode hostname to the host announcment
      ldax #local_hostname
      stax  copy_src
      ldax #host_announce_hostname
      stax  copy_dest
      ldax #$20
      jsr copymem

      ;copy our raw hostname to the host announcment
      ldax #raw_local_hostname
      stax  copy_src
      ldax #host_announce_servername
      stax  copy_dest
      ldax #$10
      jsr copymem

    ;copy the local IP address to the 'sender' field of the host announcment
      ldx #03
    @copy_sending_address_loop:
      lda  cfg_ip,x
      sta host_announce_my_ip,x
      dex
      bpl @copy_sending_address_loop

      ldax  #138
      stax  udp_send_dest_port
      stax  udp_send_src_port

      ldax  #host_announce_message_length
      stax  udp_send_len

      ldax  #host_announce_message
      jsr udp_send
      rts

    @send_nbns_message:
    ;copy the local IP address
      ldx #03
    @copy_my_address_loop:
      lda  cfg_ip,x
      sta nbns_my_ip,x
      dex
      bpl @copy_my_address_loop

      ;send to the broadcast address
      lda #$ff
      ldx #03
    @copy_broadcast_address_loop:
      sta  udp_send_dest,x
      dex
      bpl @copy_broadcast_address_loop

      ldax  #137
      stax  udp_send_dest_port
      stax  udp_send_src_port

      ldax  #nbns_registration_message_length
      stax  udp_send_len

      ldax  #registration_request
      jsr udp_send

      rts

    nbns_callback:

      lda udp_inp+udp_data+nbns_opcode
      and #$f8    ;mask the lower three bits
      beq @name_request
      rts
    @name_request:

      ;this is a NB NAME REQUEST.
      ;is it a unicast message?
      ldx  #3
    @check_unicast_loop:
      lda  ip_inp+ip_dest,x
      cmp  cfg_ip,x
      bne @not_unicast
      dex
      bpl @check_unicast_loop

      jmp @looking_for_us

    @not_unicast:
      ;is it looking for our local hostname?
      ldax  #udp_inp+udp_data+nbns_question_name+1
      stax  copy_src
      ldy #0
    @cmp_loop:
      lda (copy_src),y
      cmp local_hostname,y
      bne @not_us
      iny
      cpy #30
      bne @cmp_loop

    @looking_for_us:
      ;this is a request for our name!
      ;copy the txn id
      ldax udp_inp+udp_data ;first 2 bytes of data are txn id
      stax  netbios_name_query_response

     ;set the sender & recipients IP address
      ldx #03
    @copy_address_loop:
      lda  ip_inp+ip_src,x
      sta  udp_send_dest,x
      lda  cfg_ip,x
      sta netbios_name_query_response_ip,x
      dex
      bpl @copy_address_loop

      ;copy our encoded hostname
      ldax #local_hostname
      stax  copy_src
      ldax #netbios_name_query_response_hostname
      stax  copy_dest
      ldax #30
      jsr copymem

      ;copy the service identifier - last 2 bytes in the query hostname
      .import eth_inp
      ldax  eth_inp+$55
      stax netbios_name_query_response_hostname+30

      ldax  #137
      stax  udp_send_dest_port
      stax  udp_send_src_port

      ldax  #netbios_name_query_response_length
      stax  udp_send_len

      ldax  #netbios_name_query_response
      jmp udp_send

    @not_us:
      rts

    nb_session_callback:

      lda tcp_inbound_data_length+1
      cmp #$ff
      bne @not_eof
    @eof:
      inc connection_closed
    @done:
      rts
    @not_eof:

    ;copy this chunk to our input buffer
      ldax cifs_cmd_buffer_ptr
      stax copy_dest
      ldax tcp_inbound_data_ptr
      stax copy_src
      ldax tcp_inbound_data_length
      jsr copymem

    ;increment the pointer into the input buffer
      clc
      lda cifs_cmd_buffer_ptr
      adc tcp_inbound_data_length
      sta cifs_cmd_buffer_ptr
      lda cifs_cmd_buffer_ptr+1
      adc tcp_inbound_data_length+1
      sta cifs_cmd_buffer_ptr+1

    ;increment the cmd buffer length
      clc
      lda cifs_cmd_length
      adc tcp_inbound_data_length
      sta cifs_cmd_length
      lda cifs_cmd_length+1
      adc tcp_inbound_data_length+1
      sta cifs_cmd_length+1

    ;have we got a complete message?
      ldax  cifs_cmd_buffer
      stax  copy_src
      ldy #3
      lda (copy_src),y
      cmp cifs_cmd_length
      bne @not_got_full_message
      dey
      lda (copy_src),y
      cmp cifs_cmd_length+1
      bne @not_got_full_message

      ;we have a complete message!
      ldy #0
      lda (copy_src),y  ;get the message type
      cmp #$81    ;is it a session request?
      bne @not_session_request
      ldax  #positive_session_response_packet_length
      stax  tcp_send_data_len
      ldax  #positive_session_response_packet
      jsr tcp_send

      jmp @message_handled
      @not_session_request:
      cmp #$00    ;is it a session message?
      bne @not_session_message

      ;this SHOULD be a SMB - best check the header

      ldy #4
      lda (copy_src),y  ;get the message data
      cmp #$FF    ;start of SMB header
      bne @not_smb
      iny
      lda (copy_src),y  ;get the message data
      cmp #'S'    ;start of SMB header
      bne @not_smb

      jsr smb_handler

      jmp @message_handled

      ;this doesn't look like a NBT session message or SMB, so give up
      @not_session_message:

      @not_smb:

       jsr tcp_close
      jmp @eof

    @message_handled:
      ;reset ready for next message on this connection
      ldax  #-4                 ;start at -4, to skip the NBT header length
      stax cifs_cmd_length

      ldax   cifs_cmd_buffer
      stax  cifs_cmd_buffer_ptr

      @not_got_full_message:
      rts

    smb_handler:
    ;  at this point, copy_src points to an SMB block encapsulated in an NBT session header

      clc
      lda  copy_src
      adc #4
      sta  smb_ptr    ;skip past the NBT header
      lda  copy_src+1
      adc #00
      sta  smb_ptr+1

      ldy #8
      lda (copy_src),y  ;get the SMB type
      cmp #$72
      bne @not_negotiate_protocol
      jmp @negotiate_protocol
    @not_negotiate_protocol:
      ;we assume it is an "AndX"   command

      sta andx_opcode
      lda smb_ptr
      clc
      adc #$20     ;skip over SMB header
      sta andx_ptr

      lda smb_ptr+1
      adc #00
      sta andx_ptr+1

      jsr start_smb_response

    @parse_andx_block:
      ldax  andx_ptr
      stax  copy_src
      lda andx_opcode

      cmp #$ff
      beq @done_all_andx_blocks

      ldy #0
    @andx_opcode_scan:
      lda andx_opcodes,y
      beq @opcode_not_found
      cmp andx_opcode
      beq @opcode_found
      iny
      iny
      iny
      jmp @andx_opcode_scan
    @opcode_found:
      lda andx_opcodes+1,y
      sta andx_handler+1
      lda andx_opcodes+2,y
      sta andx_handler+2
      jsr andx_handler
      jmp @go_to_next_andx_block

    @opcode_not_found:
      jsr unknown_andx

    @go_to_next_andx_block:
      ldy #3
      lda (copy_src),y  ;get the AndX offset low byte
      clc
      adc smb_ptr
      sta andx_ptr
      iny
      lda (copy_src),y  ;get the AndX offset high byte
      adc smb_ptr+1
      sta andx_ptr+1
      ldy #1
      lda (copy_src),y  ;get the subsequent AndX opcode
      sta andx_opcode

      jmp @parse_andx_block

    @done_all_andx_blocks:
      ldax smb_response_length
      inx ;FIXME! this is to force wireshark to dump as SMB even tho packet is incorrect
      stax tcp_send_data_len
      ldax smb_response_buffer
      jsr tcp_send

      rts
    @negotiate_protocol:
    ;copy the request TID,PID,UID,MID into the response
      ldy #28 ;offset of TID from start of message
      ldx #0
    :
      lda (copy_src),y
      sta negotiate_protocol_response_tid,x
      inx
      iny
      cpx #8
      bne :-

      lda  #$ff
      sta dialect_index
      sta dialect_index+1
      clc
      lda cifs_cmd_buffer
      adc #$26
      sta copy_src
      lda cifs_cmd_buffer+1
      adc #$00
      sta copy_src+1

      ldy #$0
    @dialect_scan_loop:
      iny
      bmi @end_of_dialects ;if we go to offset $80, we have gone too far
      lda dialect_index
      cmp #$10    ;if we've scanned more than $10 dialects, something is wrong
      beq @end_of_dialects
      lda (copy_src),y
      cmp #$02
      bne @test_dialect
      inc dialect_index
      jmp @dialect_scan_loop
    @test_dialect:

      tya
      clc
      adc copy_src
      sta copy_src
      bcc :+
      inc copy_src+1
    :
      ldy #0

    @test_dialect_loop:
      lda (copy_src),y
      cmp preferred_dialect_id,y
      bne @dialect_scan_loop
      iny
      cpy #preferred_dialect_id_length
      bne @test_dialect_loop
      inc dialect_index+1
      jmp @found_preferred_dialect

    @end_of_dialects:
      lda #$ff
      sta dialect_index

     @found_preferred_dialect:

      ldax #negotiate_protocol_response_message_length
      stax tcp_send_data_len
      ldax #negotiate_protocol_response_message
      jsr tcp_send

      rts

    start_smb_response:
      ldax  smb_response_buffer
      stax  copy_dest
      ldy #0
    @copy_header_loop:
      lda (copy_src),y  ; copy_src should be the SMB request - cloning this will set PID / MID etc
      sta (copy_dest),y
      iny
      cpy #smb_response_header_length
      bne @copy_header_loop
      lda #0
      sta smb_response_length+1
      lda #smb_response_header_length
      sta smb_response_length
      ldy #3
      sta (copy_dest),y

      ;set the flags correctly
      ldy #smb_response_flags_offset
      lda #$82   ;FLAGS byte
      sta (copy_dest),y
      iny
      lda #$01   ;FLAGS2 low byte
      sta (copy_dest),y
      iny
      lda #$00   ;FLAGS2 hi byte
      sta (copy_dest),y

      rts

    add_andx_response:
      rts

    .import print_a
    .import print_hex
    session_setup_andx:
      lda #'S'
      jsr print_a
      rts

    tree_connect_andx:
      lda #'T'
      jsr print_a
      rts

    unknown_andx:
      lda andx_opcode
      jsr print_hex
      lda #'?'
      jsr print_a
      rts

    .rodata

    andx_opcodes:
      .byte $73
      .word session_setup_andx
      .byte $75
      .word tree_connect_andx
      .byte $00

    .data

    andx_handler:
      jmp $FFFF   ;filled in later

    smb_response_header:
    negotiate_protocol_response_message:
      .byte $00 ;message type = session message
      .byte $00,$00,negotiate_protocol_response_message_length-4  ;NBT header
      .byte $FF,"SMB"  ;SMB header
      .byte $72   ;command = negotiate protocol
      .byte $00,$00,$00,$00 ;status = OK
    smb_response_flags_offset  =*-smb_response_header
      .byte $82   ;flags : oplocks not supported, paths are case sensitive
      .byte $01,$00 ;flags 2 - long file names allowed
      .byte $00, $00 ;PID HIGH
      .byte $00,$00,$00,$00,$00,$00,$00,$00 ; signature
      .byte $00, $00 ;reserved
    negotiate_protocol_response_tid:
      .byte $00,$00 ;tree ID
      .byte $98,$76  ;PID - to be overwritten
      .byte $65,$64 ;USER ID - to be overwritten
      .byte $ab,$cd ;multiplex ID - to be overwritten
    smb_response_header_length=*-smb_response_header
      .byte $11 ;word count
    dialect_index: .res 2 ;index of selected dialect
      .byte $00 ;security mode: share, no encryption
      .byte $01,$00 ;Max MPX count
      .byte $01,$00 ;Max VCs count
      .byte $00,$08,$00,$00 ;max buffer size
      .byte $00,$08,$00,$00 ;max raw size
      .byte $12,$34,$56,$78 ;session key
      .byte $00,$00,$00,$00 ;capabilities
      .byte $00,$00,$00,$00 ;server time low
      .byte $00,$00,$00,$00 ;server time high
      .byte $00,$00 ;server GMT offset
      .byte $00 ;encryption key length
      .word negotiate_protocol_response_message_data_length ;data length
    negotiate_protocol_response_message_data:
      .byte "WORKGROUP",0
      .byte "SERVERNAME",0

    negotiate_protocol_response_message_length=*-negotiate_protocol_response_message
    negotiate_protocol_response_message_data_length=*-negotiate_protocol_response_message_data

    host_announce_message:
      .byte $11 ;message type = direct group datagram
      .byte $02 ;no more fragments, this is first fragment, node type = B
      .byte $ab,$cd  ;txn id
    host_announce_my_ip:
      .byte $0,0,0,0  ;source IP
      .byte $0,138    ;source port
      .byte $00,<(host_announce_message_length-4) ;datagram length
      .byte $00,$00 ;packet offset
      .byte $20       ;hostname length
    host_announce_hostname:
      .res 32           ;hostname
      .byte $0          ;nul at end of hostname
      ;now WORKGROUP<1D> encoded

      .byte $20, $46, $48, $45, $50, $46, $43, $45, $4c, $45, $48, $46, $43, $45, $50, $46
      .byte $46, $46, $41, $43, $41, $43, $41, $43, $41, $43, $41, $43, $41, $43, $41, $42, $4E, $00

      .byte $ff,"SMB" ;Server Message Block header
      .byte $25 ;SMB command = Transaction
      .byte $00 ;error class = success
      .byte $00 ;reserved
      .byte $00,$00 ;no error
      .byte $00 ;flags
      .byte $00,$00 ;flags2
      .byte $00,$00 ;PID high
      .byte $00,$00,$00,$00,$00,$00,$00,$00 ;Signature
      .byte $00,$00 ;reserved
      .byte $00,$00 ;tree ID
      .byte $00,$00 ;process ID
      .byte $00,$00 ;user ID
      .byte $00,$00 ;multiplex ID
      .byte $11 ;txn word count
      .byte $00,$00 ;txn paramater count
      .byte $21,$00 ;txn total data count
      .byte $00,$00 ;txn max paramater count
      .byte $00,$00 ;txn max data count
      .byte $00 ;txn max setup count
      .byte $00 ;reserved
      .byte $00,$00 ;flags
      .byte $ed,$03,$00,$00 ;timeout = 1 second
      .byte $00,$00 ;reserved
      .byte $00,$00 ;paramater count
      .byte $00,$00 ;paramater offset
      .byte $21,$00 ;data count
      .byte $56,$00 ;data offset
      .byte $03 ;setup count
      .byte $00 ;reserved

      .byte $01,$00 ;opcode = WRITE MAIL SLOT
      .byte $00,$00 ;priority 0
      .byte $02,$00 ;class = unreliable & broadcast
      .byte $32,$00 ;byte count
      .byte "\MAILSLOT\BROWSE", 0
      .byte $01 ;command - HOST ANNOUNCEMENT
      .byte $0  ;update count 0
      .byte $80,$fc,03,00 ;update period
      host_announce_servername:
      .res 16
      .byte $01 ;OS major version
      .byte $64 ;OS minor version
      .byte $03,$02,$0,$0 ;advertise as a workstation, server & print host
      .byte $0F ;browser major version
      .byte $01 ;browser minor version
      .byte $55,$aa ;signature
      .byte $0    ;host comment
      host_announce_message_length=*-host_announce_message

    netbios_name_query_response:
      .byte $12,$34 ;transaction id
      .byte $85,$00 ;flags: name query response, no error
      .byte $00,$00  ;questions = 0
      .byte $00,$01  ;answers = 1
      .byte $00,$00  ;authority records = 0
      .byte $00,$00  ;additional records = 0
      .byte $20       ;
    netbios_name_query_response_hostname:
      .res 30       ;will be replaced with encoded hostname
      .byte $43,$41   ;
      .byte $00       ;
      .byte $00,$20 ; type = NB
      .byte $00,$01   ;class = IN
      .byte $00,$00,$01,$40 ; TTL = 64 seconds
      .byte $00,$06 ;data length
      .byte $00,$00 ;FLAGS = B-NODE, UNIQUE NAME
    netbios_name_query_response_ip:
      .res 4            ;filled in with our IP
      netbios_name_query_response_length=*-netbios_name_query_response

    registration_request:

      .byte $0c, $64  ;txn ID
      .byte $29,$10   ;Registration Request opcode & flags
      .byte $00,$01   ;questions = 1
      .byte $00,$00   ;answers = 0
      .byte $00,$00   ;authority records = 0
      .byte $00,$01   ;additional records = 1
    registration_request_servername:
      ;now WORKGROUP<00> encoded
      .byte $20, $46, $48, $45, $50, $46, $43, $45, $4c, $45, $48, $46, $43, $45, $50, $46
      .byte $46, $46, $41, $43, $41, $43, $41, $43, $41, $43, $41, $43, $41, $43, $41, $41, $41, $00

      .byte $00,$20   ;question_type = NB
      .byte $00,$01   ;question_class = IN
      .byte $c0,$0c   ;additional record name : ptr to string in QUESTION NAME
      .byte $00,$20   ;question_type = NB
      .byte $00,$01   ;question_class = IN
      .byte $00,$00,$01,$40 ; TTL = 64 seconds
      .byte $00,$06 ;data length
      .byte $00,$00 ;FLAGS = B-NODE, UNIQUE NAME

    .rodata
    preferred_dialect_id: .byte "NT LM 0.12"
    preferred_dialect_id_length=*-preferred_dialect_id

    positive_session_response_packet:
      .byte $82     ;packet type = Positive session response
      .byte $00     ;flags
      .byte $00, $00  ;message length
    positive_session_response_packet_length=*-positive_session_response_packet

    .data

    cifs_cmd_buffer: .word DEFAULT_CIFS_CMD_BUFFER

    smb_response_buffer: .word DEFAULT_SMB_RESPONSE_BUFFER

    .bss
    hostname_buffer:  .res 33

    local_hostname:    .res 33

    raw_local_hostname:
      .res 16

    hi_nibble: .res 1

    connection_closed: .res 1

    cifs_cmd_buffer_ptr: .res 2
    cifs_cmd_length: .res 2

    andx_opcode: .res 1
    andx_ptr: .res 2          ;pointer to next 'AndX' command
    andx_length: .res 2
    smb_ptr: .res 2
    smb_response_length: .res 2

    ; The contents of this file are subject to the Mozilla Public License
    ; Version 1.1 (the "License"); you may not use this file except in
    ; compliance with the License. You may obtain a copy of the License at
    ; http://www.mozilla.org/MPL/
    ;
    ; Software distributed under the License is distributed on an "AS IS"
    ; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
    ; License for the specific language governing rights and limitations
    ; under the License.
    ;
    ; The Original Code is ip65.
    ;
    ; The Initial Developer of the Original Code is Jonno Downes,
    ; jonno@jamtronix.com.
    ; Portions created by the Initial Developer are Copyright (C) 2009
    ; Jonno Downes. All Rights Reserved.
    ; -- LICENSE END --