ruby_smb 0.0.18 → 0.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (298) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/README.md +247 -7
  5. data/examples/anonymous_auth.rb +6 -3
  6. data/examples/append_file.rb +40 -0
  7. data/examples/authenticate.rb +12 -7
  8. data/examples/delete_file.rb +40 -0
  9. data/examples/list_directory.rb +45 -0
  10. data/examples/negotiate.rb +0 -1
  11. data/examples/negotiate_with_netbios_service.rb +36 -0
  12. data/examples/net_share_enum_all.rb +30 -0
  13. data/examples/read_file.rb +39 -0
  14. data/examples/rename_file.rb +41 -0
  15. data/examples/tree_connect.rb +2 -4
  16. data/examples/write_file.rb +40 -0
  17. data/lib/ruby_smb.rb +5 -0
  18. data/lib/ruby_smb/client.rb +196 -43
  19. data/lib/ruby_smb/client/authentication.rb +89 -48
  20. data/lib/ruby_smb/client/echo.rb +1 -4
  21. data/lib/ruby_smb/client/negotiation.rb +46 -45
  22. data/lib/ruby_smb/client/signing.rb +9 -16
  23. data/lib/ruby_smb/client/tree_connect.rb +8 -13
  24. data/lib/ruby_smb/client/utils.rb +79 -0
  25. data/lib/ruby_smb/dcerpc.rb +30 -0
  26. data/lib/ruby_smb/dcerpc/bind.rb +60 -0
  27. data/lib/ruby_smb/dcerpc/handle.rb +60 -0
  28. data/lib/ruby_smb/dcerpc/ndr.rb +41 -0
  29. data/lib/ruby_smb/dcerpc/request.rb +43 -0
  30. data/lib/ruby_smb/dcerpc/response.rb +46 -0
  31. data/lib/ruby_smb/dcerpc/srvsvc.rb +17 -0
  32. data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +93 -0
  33. data/lib/ruby_smb/dcerpc/uuid.rb +28 -0
  34. data/lib/ruby_smb/dispatcher.rb +7 -3
  35. data/lib/ruby_smb/dispatcher/base.rb +23 -14
  36. data/lib/ruby_smb/dispatcher/socket.rb +71 -51
  37. data/lib/ruby_smb/dispositions.rb +32 -0
  38. data/lib/ruby_smb/error.rb +24 -18
  39. data/lib/ruby_smb/field.rb +5 -2
  40. data/lib/ruby_smb/field/extended_attribute_flag.rb +1 -1
  41. data/lib/ruby_smb/field/file_time.rb +3 -1
  42. data/lib/ruby_smb/field/security_descriptor.rb +6 -6
  43. data/lib/ruby_smb/field/smb2_fileid.rb +11 -0
  44. data/lib/ruby_smb/field/smb_fea.rb +3 -3
  45. data/lib/ruby_smb/field/smb_fea_list.rb +3 -3
  46. data/lib/ruby_smb/field/smb_gea.rb +12 -0
  47. data/lib/ruby_smb/field/smb_gea_list.rb +13 -0
  48. data/lib/ruby_smb/field/string16.rb +14 -0
  49. data/lib/ruby_smb/field/stringz16.rb +3 -7
  50. data/lib/ruby_smb/field/utime.rb +11 -10
  51. data/lib/ruby_smb/fscc.rb +12 -0
  52. data/lib/ruby_smb/fscc/control_codes.rb +26 -0
  53. data/lib/ruby_smb/{field → fscc}/ea_info_array.rb +12 -14
  54. data/lib/ruby_smb/fscc/file_attributes.rb +29 -0
  55. data/lib/ruby_smb/{field → fscc}/file_full_ea_info.rb +5 -5
  56. data/lib/ruby_smb/fscc/file_information.rb +60 -0
  57. data/lib/ruby_smb/fscc/file_information/file_both_directory_information.rb +29 -0
  58. data/lib/ruby_smb/fscc/file_information/file_directory_information.rb +25 -0
  59. data/lib/ruby_smb/fscc/file_information/file_disposition_information.rb +15 -0
  60. data/lib/ruby_smb/fscc/file_information/file_full_directory_information.rb +26 -0
  61. data/lib/ruby_smb/fscc/file_information/file_id_both_directory_information.rb +31 -0
  62. data/lib/ruby_smb/fscc/file_information/file_id_full_directory_information.rb +28 -0
  63. data/lib/ruby_smb/fscc/file_information/file_names_information.rb +18 -0
  64. data/lib/ruby_smb/fscc/file_information/file_rename_information.rb +44 -0
  65. data/lib/ruby_smb/generic_packet.rb +40 -18
  66. data/lib/ruby_smb/gss.rb +49 -56
  67. data/lib/ruby_smb/impersonation_levels.rb +7 -2
  68. data/lib/ruby_smb/nbss.rb +16 -0
  69. data/lib/ruby_smb/nbss/negative_session_response.rb +30 -0
  70. data/lib/ruby_smb/nbss/session_header.rb +13 -0
  71. data/lib/ruby_smb/nbss/session_request.rb +13 -0
  72. data/lib/ruby_smb/smb1.rb +20 -17
  73. data/lib/ruby_smb/smb1/bit_field.rb +3 -0
  74. data/lib/ruby_smb/smb1/bit_field/create_options.rb +5 -5
  75. data/lib/ruby_smb/smb1/bit_field/file_status_flags.rb +18 -0
  76. data/lib/ruby_smb/smb1/bit_field/open2_access_mode.rb +11 -11
  77. data/lib/ruby_smb/smb1/bit_field/open2_open_mode.rb +1 -1
  78. data/lib/ruby_smb/smb1/bit_field/optional_support.rb +0 -1
  79. data/lib/ruby_smb/smb1/bit_field/security_flags.rb +15 -0
  80. data/lib/ruby_smb/smb1/bit_field/share_access.rb +2 -3
  81. data/lib/ruby_smb/smb1/bit_field/smb_ext_file_attributes.rb +21 -24
  82. data/lib/ruby_smb/smb1/bit_field/smb_file_attributes.rb +1 -1
  83. data/lib/ruby_smb/smb1/commands.rb +5 -0
  84. data/lib/ruby_smb/smb1/create_actions.rb +3 -5
  85. data/lib/ruby_smb/smb1/file.rb +289 -0
  86. data/lib/ruby_smb/smb1/oplock_levels.rb +2 -4
  87. data/lib/ruby_smb/smb1/packet.rb +10 -0
  88. data/lib/ruby_smb/smb1/packet/close_request.rb +31 -0
  89. data/lib/ruby_smb/smb1/packet/close_response.rb +28 -0
  90. data/lib/ruby_smb/smb1/packet/echo_request.rb +5 -7
  91. data/lib/ruby_smb/smb1/packet/echo_response.rb +5 -7
  92. data/lib/ruby_smb/smb1/packet/empty_packet.rb +1 -2
  93. data/lib/ruby_smb/smb1/packet/logoff_request.rb +1 -4
  94. data/lib/ruby_smb/smb1/packet/logoff_response.rb +1 -4
  95. data/lib/ruby_smb/smb1/packet/negotiate_response.rb +22 -0
  96. data/lib/ruby_smb/smb1/packet/negotiate_response_extended.rb +22 -0
  97. data/lib/ruby_smb/smb1/packet/nt_create_andx_request.rb +62 -0
  98. data/lib/ruby_smb/smb1/packet/nt_create_andx_response.rb +66 -0
  99. data/lib/ruby_smb/smb1/packet/nt_trans.rb +1 -2
  100. data/lib/ruby_smb/smb1/packet/nt_trans/create_request.rb +19 -13
  101. data/lib/ruby_smb/smb1/packet/nt_trans/create_response.rb +8 -10
  102. data/lib/ruby_smb/smb1/packet/nt_trans/request.rb +11 -11
  103. data/lib/ruby_smb/smb1/packet/nt_trans/response.rb +10 -10
  104. data/lib/ruby_smb/smb1/packet/nt_trans/subcommands.rb +8 -1
  105. data/lib/ruby_smb/smb1/packet/read_andx_request.rb +84 -0
  106. data/lib/ruby_smb/smb1/packet/read_andx_response.rb +47 -0
  107. data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +2 -6
  108. data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +1 -4
  109. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +1 -6
  110. data/lib/ruby_smb/smb1/packet/session_setup_response.rb +2 -4
  111. data/lib/ruby_smb/smb1/packet/trans2.rb +8 -2
  112. data/lib/ruby_smb/smb1/packet/trans2/data_block.rb +6 -7
  113. data/lib/ruby_smb/smb1/packet/trans2/find_first2_request.rb +77 -0
  114. data/lib/ruby_smb/smb1/packet/trans2/find_first2_response.rb +87 -0
  115. data/lib/ruby_smb/smb1/packet/trans2/find_information_level.rb +32 -0
  116. data/lib/ruby_smb/smb1/packet/trans2/find_information_level/find_file_full_directory_info.rb +45 -0
  117. data/lib/ruby_smb/smb1/packet/trans2/find_next2_request.rb +77 -0
  118. data/lib/ruby_smb/smb1/packet/trans2/find_next2_response.rb +86 -0
  119. data/lib/ruby_smb/smb1/packet/trans2/open2_request.rb +10 -10
  120. data/lib/ruby_smb/smb1/packet/trans2/open2_response.rb +10 -12
  121. data/lib/ruby_smb/smb1/packet/trans2/request.rb +15 -17
  122. data/lib/ruby_smb/smb1/packet/trans2/request_secondary.rb +8 -10
  123. data/lib/ruby_smb/smb1/packet/trans2/response.rb +11 -13
  124. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_request.rb +66 -0
  125. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_response.rb +57 -0
  126. data/lib/ruby_smb/smb1/packet/trans2/subcommands.rb +5 -2
  127. data/lib/ruby_smb/smb1/packet/tree_connect_request.rb +4 -6
  128. data/lib/ruby_smb/smb1/packet/tree_connect_response.rb +5 -7
  129. data/lib/ruby_smb/smb1/packet/tree_disconnect_request.rb +2 -4
  130. data/lib/ruby_smb/smb1/packet/tree_disconnect_response.rb +2 -4
  131. data/lib/ruby_smb/smb1/packet/write_andx_request.rb +68 -0
  132. data/lib/ruby_smb/smb1/packet/write_andx_response.rb +35 -0
  133. data/lib/ruby_smb/smb1/resource_type.rb +18 -0
  134. data/lib/ruby_smb/smb1/tree.rb +188 -5
  135. data/lib/ruby_smb/smb2.rb +16 -11
  136. data/lib/ruby_smb/smb2/bit_field.rb +1 -0
  137. data/lib/ruby_smb/smb2/bit_field/file_access_mask.rb +1 -1
  138. data/lib/ruby_smb/smb2/bit_field/session_flags.rb +1 -2
  139. data/lib/ruby_smb/smb2/bit_field/share_flags.rb +4 -6
  140. data/lib/ruby_smb/smb2/create_context.rb +29 -0
  141. data/lib/ruby_smb/smb2/file.rb +251 -0
  142. data/lib/ruby_smb/smb2/info_type.rb +21 -0
  143. data/lib/ruby_smb/smb2/packet.rb +16 -0
  144. data/lib/ruby_smb/smb2/packet/close_request.rb +22 -0
  145. data/lib/ruby_smb/smb2/packet/close_response.rb +29 -0
  146. data/lib/ruby_smb/smb2/packet/create_request.rb +54 -0
  147. data/lib/ruby_smb/smb2/packet/create_response.rb +35 -0
  148. data/lib/ruby_smb/smb2/packet/echo_request.rb +1 -3
  149. data/lib/ruby_smb/smb2/packet/echo_response.rb +1 -3
  150. data/lib/ruby_smb/smb2/packet/error_packet.rb +1 -3
  151. data/lib/ruby_smb/smb2/packet/ioctl_request.rb +54 -0
  152. data/lib/ruby_smb/smb2/packet/ioctl_response.rb +39 -0
  153. data/lib/ruby_smb/smb2/packet/logoff_request.rb +1 -4
  154. data/lib/ruby_smb/smb2/packet/logoff_response.rb +1 -4
  155. data/lib/ruby_smb/smb2/packet/negotiate_request.rb +1 -1
  156. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +1 -1
  157. data/lib/ruby_smb/smb2/packet/query_directory_request.rb +35 -0
  158. data/lib/ruby_smb/smb2/packet/query_directory_response.rb +45 -0
  159. data/lib/ruby_smb/smb2/packet/read_request.rb +30 -0
  160. data/lib/ruby_smb/smb2/packet/read_response.rb +26 -0
  161. data/lib/ruby_smb/smb2/packet/session_setup_request.rb +2 -5
  162. data/lib/ruby_smb/smb2/packet/session_setup_response.rb +4 -7
  163. data/lib/ruby_smb/smb2/packet/set_info_request.rb +58 -0
  164. data/lib/ruby_smb/smb2/packet/set_info_response.rb +20 -0
  165. data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +3 -4
  166. data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +5 -9
  167. data/lib/ruby_smb/smb2/packet/tree_disconnect_request.rb +1 -4
  168. data/lib/ruby_smb/smb2/packet/tree_disconnect_response.rb +1 -4
  169. data/lib/ruby_smb/smb2/packet/write_request.rb +29 -0
  170. data/lib/ruby_smb/smb2/packet/write_response.rb +26 -0
  171. data/lib/ruby_smb/smb2/tree.rb +163 -7
  172. data/lib/ruby_smb/version.rb +1 -1
  173. data/spec/lib/ruby_smb/client_spec.rb +459 -120
  174. data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +14 -0
  175. data/spec/lib/ruby_smb/dcerpc/handle_spec.rb +31 -0
  176. data/spec/lib/ruby_smb/dcerpc/request_spec.rb +21 -0
  177. data/spec/lib/ruby_smb/dcerpc/response_spec.rb +15 -0
  178. data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +13 -0
  179. data/spec/lib/ruby_smb/dcerpc/uuid_spec.rb +12 -0
  180. data/spec/lib/ruby_smb/dispatcher/base_spec.rb +26 -0
  181. data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +118 -18
  182. data/spec/lib/ruby_smb/field/extended_attribute_flag_spec.rb +0 -3
  183. data/spec/lib/ruby_smb/field/file_time_spec.rb +4 -2
  184. data/spec/lib/ruby_smb/field/security_descriptor.rb +0 -1
  185. data/spec/lib/ruby_smb/field/smb2_fileid_spec.rb +10 -0
  186. data/spec/lib/ruby_smb/field/smb_fea_list_spec.rb +3 -5
  187. data/spec/lib/ruby_smb/field/smb_gea_list_spec.rb +37 -0
  188. data/spec/lib/ruby_smb/field/smb_gea_spec.rb +22 -0
  189. data/spec/lib/ruby_smb/field/stringz16_spec.rb +7 -9
  190. data/spec/lib/ruby_smb/field/utime_spec.rb +4 -2
  191. data/spec/lib/ruby_smb/{field → fscc}/ea_info_array_spec.rb +7 -9
  192. data/spec/lib/ruby_smb/{field → fscc}/file_full_ea_info_spec.rb +2 -3
  193. data/spec/lib/ruby_smb/fscc/file_information/file_both_directory_information_spec.rb +71 -0
  194. data/spec/lib/ruby_smb/fscc/file_information/file_directory_information_spec.rb +68 -0
  195. data/spec/lib/ruby_smb/fscc/file_information/file_disposition_information_spec.rb +26 -0
  196. data/spec/lib/ruby_smb/fscc/file_information/file_full_directory_information_spec.rb +69 -0
  197. data/spec/lib/ruby_smb/fscc/file_information/file_id_both_directory_information_spec.rb +72 -0
  198. data/spec/lib/ruby_smb/fscc/file_information/file_id_full_directory_information_spec.rb +70 -0
  199. data/spec/lib/ruby_smb/fscc/file_information/file_names_information_spec.rb +41 -0
  200. data/spec/lib/ruby_smb/fscc/file_information/file_rename_information_spec.rb +133 -0
  201. data/spec/lib/ruby_smb/fscc/fscc_file_attributes_spec.rb +143 -0
  202. data/spec/lib/ruby_smb/generic_packet_spec.rb +46 -21
  203. data/spec/lib/ruby_smb/nbss/negative_session_response_spec.rb +29 -0
  204. data/spec/lib/ruby_smb/nbss/session_header_spec.rb +30 -0
  205. data/spec/lib/ruby_smb/nbss/session_request_spec.rb +30 -0
  206. data/spec/lib/ruby_smb/smb1/bit_field/create_options_spec.rb +9 -1
  207. data/spec/lib/ruby_smb/smb1/bit_field/directory_access_mask_spec.rb +0 -2
  208. data/spec/lib/ruby_smb/smb1/bit_field/file_access_mask_spec.rb +0 -2
  209. data/spec/lib/ruby_smb/smb1/bit_field/file_status_flags_spec.rb +35 -0
  210. data/spec/lib/ruby_smb/smb1/bit_field/open2_access_mode_spec.rb +1 -3
  211. data/spec/lib/ruby_smb/smb1/bit_field/open2_flags_spec.rb +0 -2
  212. data/spec/lib/ruby_smb/smb1/bit_field/open2_open_mode_spec.rb +0 -1
  213. data/spec/lib/ruby_smb/smb1/bit_field/optional_support_spec.rb +0 -1
  214. data/spec/lib/ruby_smb/smb1/bit_field/security_flags_spec.rb +26 -0
  215. data/spec/lib/ruby_smb/smb1/bit_field/share_access_spec.rb +0 -3
  216. data/spec/lib/ruby_smb/smb1/bit_field/smb_ext_file_attributes_spec.rb +20 -39
  217. data/spec/lib/ruby_smb/smb1/bit_field/smb_file_attributes_spec.rb +0 -6
  218. data/spec/lib/ruby_smb/smb1/bit_field/smb_nmpipe_status_spec.rb +0 -2
  219. data/spec/lib/ruby_smb/smb1/bit_field/trans2_flags_spec.rb +0 -2
  220. data/spec/lib/ruby_smb/smb1/bit_field/tree_connect_flags_spec.rb +0 -2
  221. data/spec/lib/ruby_smb/smb1/file_spec.rb +469 -0
  222. data/spec/lib/ruby_smb/smb1/packet/close_request_spec.rb +54 -0
  223. data/spec/lib/ruby_smb/smb1/packet/close_response_spec.rb +45 -0
  224. data/spec/lib/ruby_smb/smb1/packet/echo_request_spec.rb +1 -4
  225. data/spec/lib/ruby_smb/smb1/packet/echo_response_spec.rb +1 -4
  226. data/spec/lib/ruby_smb/smb1/packet/error_packet_spec.rb +1 -3
  227. data/spec/lib/ruby_smb/smb1/packet/logoff_request_spec.rb +1 -4
  228. data/spec/lib/ruby_smb/smb1/packet/logoff_response_spec.rb +1 -4
  229. data/spec/lib/ruby_smb/smb1/packet/negotiate_request_spec.rb +1 -1
  230. data/spec/lib/ruby_smb/smb1/packet/negotiate_response_extended_spec.rb +37 -0
  231. data/spec/lib/ruby_smb/smb1/packet/negotiate_response_spec.rb +37 -0
  232. data/spec/lib/ruby_smb/smb1/packet/nt_create_andx_request_spec.rb +151 -0
  233. data/spec/lib/ruby_smb/smb1/packet/nt_create_andx_response_spec.rb +157 -0
  234. data/spec/lib/ruby_smb/smb1/packet/nt_trans/create_request_spec.rb +13 -6
  235. data/spec/lib/ruby_smb/smb1/packet/nt_trans/create_response_spec.rb +1 -7
  236. data/spec/lib/ruby_smb/smb1/packet/nt_trans/request_spec.rb +1 -6
  237. data/spec/lib/ruby_smb/smb1/packet/nt_trans/response_spec.rb +1 -5
  238. data/spec/lib/ruby_smb/smb1/packet/read_andx_request_spec.rb +149 -0
  239. data/spec/lib/ruby_smb/smb1/packet/read_andx_response_spec.rb +93 -0
  240. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_request_spec.rb +1 -5
  241. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_response_spec.rb +1 -5
  242. data/spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb +3 -8
  243. data/spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb +2 -4
  244. data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_request_spec.rb +180 -0
  245. data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_response_spec.rb +104 -0
  246. data/spec/lib/ruby_smb/smb1/packet/trans2/find_information_level/find_file_full_directory_info_spec.rb +128 -0
  247. data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_request_spec.rb +174 -0
  248. data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_response_spec.rb +102 -0
  249. data/spec/lib/ruby_smb/smb1/packet/trans2/open2_request_spec.rb +1 -6
  250. data/spec/lib/ruby_smb/smb1/packet/trans2/open2_response_spec.rb +2 -7
  251. data/spec/lib/ruby_smb/smb1/packet/trans2/request_secondary_spec.rb +1 -5
  252. data/spec/lib/ruby_smb/smb1/packet/trans2/request_spec.rb +1 -5
  253. data/spec/lib/ruby_smb/smb1/packet/trans2/response_spec.rb +1 -4
  254. data/spec/lib/ruby_smb/smb1/packet/trans2/set_file_information_request_spec.rb +98 -0
  255. data/spec/lib/ruby_smb/smb1/packet/trans2/set_file_information_response_spec.rb +56 -0
  256. data/spec/lib/ruby_smb/smb1/packet/tree_connect_request_spec.rb +1 -5
  257. data/spec/lib/ruby_smb/smb1/packet/tree_connect_response_spec.rb +1 -6
  258. data/spec/lib/ruby_smb/smb1/packet/tree_disconnect_request_spec.rb +1 -4
  259. data/spec/lib/ruby_smb/smb1/packet/tree_disconnect_response_spec.rb +1 -4
  260. data/spec/lib/ruby_smb/smb1/packet/write_andx_request_spec.rb +148 -0
  261. data/spec/lib/ruby_smb/smb1/packet/write_andx_response_spec.rb +54 -0
  262. data/spec/lib/ruby_smb/smb1/tree_spec.rb +409 -7
  263. data/spec/lib/ruby_smb/smb2/bit_field/directory_access_mask_spec.rb +0 -2
  264. data/spec/lib/ruby_smb/smb2/bit_field/file_access_mask_spec.rb +0 -2
  265. data/spec/lib/ruby_smb/smb2/bit_field/session_flags_spec.rb +1 -1
  266. data/spec/lib/ruby_smb/smb2/bit_field/share_capabilities_spec.rb +0 -1
  267. data/spec/lib/ruby_smb/smb2/bit_field/share_flags_spec.rb +0 -2
  268. data/spec/lib/ruby_smb/smb2/create_context_spec.rb +42 -0
  269. data/spec/lib/ruby_smb/smb2/file_spec.rb +233 -0
  270. data/spec/lib/ruby_smb/smb2/packet/close_request_spec.rb +40 -0
  271. data/spec/lib/ruby_smb/smb2/packet/close_response_spec.rb +40 -0
  272. data/spec/lib/ruby_smb/smb2/packet/create_request_spec.rb +101 -0
  273. data/spec/lib/ruby_smb/smb2/packet/create_response_spec.rb +64 -0
  274. data/spec/lib/ruby_smb/smb2/packet/echo_request_spec.rb +1 -3
  275. data/spec/lib/ruby_smb/smb2/packet/echo_response_spec.rb +1 -3
  276. data/spec/lib/ruby_smb/smb2/packet/ioctl_request_spec.rb +48 -0
  277. data/spec/lib/ruby_smb/smb2/packet/logoff_request_spec.rb +1 -3
  278. data/spec/lib/ruby_smb/smb2/packet/logoff_response_spec.rb +1 -3
  279. data/spec/lib/ruby_smb/smb2/packet/query_directory_request_spec.rb +80 -0
  280. data/spec/lib/ruby_smb/smb2/packet/query_directory_response_spec.rb +64 -0
  281. data/spec/lib/ruby_smb/smb2/packet/read_request_spec.rb +43 -0
  282. data/spec/lib/ruby_smb/smb2/packet/read_response_spec.rb +50 -0
  283. data/spec/lib/ruby_smb/smb2/packet/session_setup_request_spec.rb +3 -4
  284. data/spec/lib/ruby_smb/smb2/packet/session_setup_response_spec.rb +2 -3
  285. data/spec/lib/ruby_smb/smb2/packet/set_info_request_spec.rb +205 -0
  286. data/spec/lib/ruby_smb/smb2/packet/set_info_response_spec.rb +32 -0
  287. data/spec/lib/ruby_smb/smb2/packet/tree_connect_request_spec.rb +3 -5
  288. data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +1 -2
  289. data/spec/lib/ruby_smb/smb2/packet/tree_disconnect_request_spec.rb +1 -3
  290. data/spec/lib/ruby_smb/smb2/packet/tree_disconnect_response_spec.rb +1 -3
  291. data/spec/lib/ruby_smb/smb2/packet/write_request_spec.rb +51 -0
  292. data/spec/lib/ruby_smb/smb2/packet/write_response_spec.rb +38 -0
  293. data/spec/lib/ruby_smb/smb2/tree_spec.rb +191 -5
  294. data/spec/spec_helper.rb +1 -1
  295. metadata +195 -12
  296. metadata.gz.sig +0 -0
  297. data/lib/ruby_smb/smb1/dispositions.rb +0 -36
  298. data/spec/lib/ruby_smb/dispatcher/dispatcher_base_spec.rb +0 -22
@@ -0,0 +1,29 @@
1
+ RSpec.describe RubySMB::Nbss::NegativeSessionResponse do
2
+ subject(:negative_session_response) { described_class.new }
3
+
4
+ it { is_expected.to respond_to :session_header }
5
+ it { is_expected.to respond_to :error_code }
6
+
7
+ it 'is big endian' do
8
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :big
9
+ end
10
+
11
+ describe '#session_header' do
12
+ it 'is a SessionHeader field' do
13
+ expect(negative_session_response.session_header).to be_a RubySMB::Nbss::SessionHeader
14
+ end
15
+ end
16
+
17
+ describe '#error_code' do
18
+ it 'is a 8-bit Unsigned Integer' do
19
+ expect(negative_session_response.error_code).to be_a BinData::Uint8
20
+ end
21
+ end
22
+
23
+ describe '#error_msg' do
24
+ it 'returns a string describing the error' do
25
+ negative_session_response.error_code = 0x80
26
+ expect(negative_session_response.error_msg).to eq 'Not listening on called name'
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,30 @@
1
+ RSpec.describe RubySMB::Nbss::SessionHeader do
2
+ subject(:session_header) { described_class.new }
3
+
4
+ it { is_expected.to respond_to :session_packet_type }
5
+ it { is_expected.to respond_to :flags }
6
+ it { is_expected.to respond_to :packet_length }
7
+
8
+ it 'is big endian' do
9
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :big
10
+ end
11
+
12
+ describe '#session_packet_type' do
13
+ it 'is a 8-bit Unsigned Integer' do
14
+ expect(session_header.session_packet_type).to be_a BinData::Uint8
15
+ end
16
+ end
17
+
18
+ describe '#flags' do
19
+ it 'is a 8-bit Unsigned Integer' do
20
+ expect(session_header.flags).to be_a BinData::Uint8
21
+ end
22
+ end
23
+
24
+ describe '#packet_length' do
25
+ it 'is a 16-bit Unsigned Integer' do
26
+ expect(session_header.packet_length).to be_a BinData::Uint16be
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,30 @@
1
+ RSpec.describe RubySMB::Nbss::SessionRequest do
2
+ subject(:session_request) { described_class.new }
3
+
4
+ it { is_expected.to respond_to :session_header }
5
+ it { is_expected.to respond_to :called_name }
6
+ it { is_expected.to respond_to :calling_name }
7
+
8
+ it 'is big endian' do
9
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :big
10
+ end
11
+
12
+ describe '#session_header' do
13
+ it 'is a SessionHeader field' do
14
+ expect(session_request.session_header).to be_a RubySMB::Nbss::SessionHeader
15
+ end
16
+ end
17
+
18
+ describe '#called_name' do
19
+ it 'is a string' do
20
+ expect(session_request.called_name).to be_a BinData::String
21
+ end
22
+ end
23
+
24
+ describe '#calling_name' do
25
+ it 'is a string' do
26
+ expect(session_request.calling_name).to be_a BinData::String
27
+ end
28
+ end
29
+ end
30
+
@@ -171,6 +171,14 @@ RSpec.describe RubySMB::SMB1::BitField::CreateOptions do
171
171
  it_behaves_like 'bit field with one flag set', :open_no_recall, 'V', 0x00400000
172
172
  end
173
173
 
174
+ describe '#open_reparse_point' do
175
+ it 'is a 1-bit flag' do
176
+ expect(options.reserve_opfilter).to be_a BinData::Bit1
177
+ end
178
+
179
+ it_behaves_like 'bit field with one flag set', :open_reparse_point, 'V', 0x00200000
180
+ end
181
+
174
182
  describe '#reserve_opfilter' do
175
183
  it 'is a 1-bit flag' do
176
184
  expect(options.reserve_opfilter).to be_a BinData::Bit1
@@ -178,4 +186,4 @@ RSpec.describe RubySMB::SMB1::BitField::CreateOptions do
178
186
 
179
187
  it_behaves_like 'bit field with one flag set', :reserve_opfilter, 'V', 0x00100000
180
188
  end
181
- end
189
+ end
@@ -34,7 +34,6 @@ RSpec.describe RubySMB::SMB1::BitField::DirectoryAccessMask do
34
34
  it_behaves_like 'bit field with one flag set', :list, 'V', 0x00000001
35
35
  end
36
36
 
37
-
38
37
  describe '#add_file' do
39
38
  it 'should be a 1-bit field per the SMB spec' do
40
39
  expect(flags.add_file).to be_a BinData::Bit1
@@ -186,5 +185,4 @@ RSpec.describe RubySMB::SMB1::BitField::DirectoryAccessMask do
186
185
 
187
186
  it_behaves_like 'bit field with one flag set', :generic_read, 'V', 0x80000000
188
187
  end
189
-
190
188
  end
@@ -34,7 +34,6 @@ RSpec.describe RubySMB::SMB1::BitField::FileAccessMask do
34
34
  it_behaves_like 'bit field with one flag set', :read_data, 'V', 0x00000001
35
35
  end
36
36
 
37
-
38
37
  describe '#write_data' do
39
38
  it 'should be a 1-bit field per the SMB spec' do
40
39
  expect(flags.write_data).to be_a BinData::Bit1
@@ -186,5 +185,4 @@ RSpec.describe RubySMB::SMB1::BitField::FileAccessMask do
186
185
 
187
186
  it_behaves_like 'bit field with one flag set', :generic_read, 'V', 0x80000000
188
187
  end
189
-
190
188
  end
@@ -0,0 +1,35 @@
1
+ RSpec.describe RubySMB::SMB1::BitField::FileStatusFlags do
2
+ subject(:options) { described_class.new }
3
+
4
+ it { is_expected.to respond_to :reparse_tag }
5
+ it { is_expected.to respond_to :no_substreams }
6
+ it { is_expected.to respond_to :no_eas }
7
+
8
+ it 'is little endian' do
9
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
10
+ end
11
+
12
+ describe '#reparse_tag' do
13
+ it 'is a 1-bit flag' do
14
+ expect(options.reparse_tag).to be_a BinData::Bit1
15
+ end
16
+
17
+ it_behaves_like 'bit field with one flag set', :reparse_tag, 'v', 0x0004
18
+ end
19
+
20
+ describe '#no_substreams' do
21
+ it 'is a 1-bit flag' do
22
+ expect(options.no_substreams).to be_a BinData::Bit1
23
+ end
24
+
25
+ it_behaves_like 'bit field with one flag set', :no_substreams, 'v', 0x0002
26
+ end
27
+
28
+ describe '#no_eas' do
29
+ it 'is a 1-bit flag' do
30
+ expect(options.no_eas).to be_a BinData::Bit1
31
+ end
32
+
33
+ it_behaves_like 'bit field with one flag set', :no_eas, 'v', 0x0001
34
+ end
35
+ end
@@ -73,9 +73,7 @@ RSpec.describe RubySMB::SMB1::BitField::Open2AccessMode do
73
73
  end
74
74
 
75
75
  it 'raises an ArgumentError if given an invalid mode' do
76
- expect{flags.set_access_mode('abcd')}.to raise_error(ArgumentError)
76
+ expect { flags.set_access_mode('abcd') }.to raise_error(ArgumentError)
77
77
  end
78
78
  end
79
-
80
-
81
79
  end
@@ -57,6 +57,4 @@ RSpec.describe RubySMB::SMB1::BitField::Open2Flags do
57
57
 
58
58
  it_behaves_like 'bit field with one flag set', :req_easize, 'v', 0x0008
59
59
  end
60
-
61
-
62
60
  end
@@ -23,5 +23,4 @@ RSpec.describe RubySMB::SMB1::BitField::Open2OpenMode do
23
23
 
24
24
  it_behaves_like 'bit field with one flag set', :create_file, 'v', 0x0010
25
25
  end
26
-
27
26
  end
@@ -48,5 +48,4 @@ RSpec.describe RubySMB::SMB1::BitField::OptionalSupport do
48
48
 
49
49
  it_behaves_like 'bit field with one flag set', :extended_signature, 'v', 0x0020
50
50
  end
51
-
52
51
  end
@@ -0,0 +1,26 @@
1
+ RSpec.describe RubySMB::SMB1::BitField::SecurityFlags do
2
+ subject(:options) { described_class.new }
3
+
4
+ it { is_expected.to respond_to :effective_only }
5
+ it { is_expected.to respond_to :context_tracking }
6
+
7
+ it 'is little endian' do
8
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
9
+ end
10
+
11
+ describe '#effective_only' do
12
+ it 'is a 1-bit flag' do
13
+ expect(options.effective_only).to be_a BinData::Bit1
14
+ end
15
+
16
+ it_behaves_like 'bit field with one flag set', :effective_only, 'C', 0x02
17
+ end
18
+
19
+ describe '#context_tracking' do
20
+ it 'is a 1-bit flag' do
21
+ expect(options.context_tracking).to be_a BinData::Bit1
22
+ end
23
+
24
+ it_behaves_like 'bit field with one flag set', :context_tracking, 'C', 0x01
25
+ end
26
+ end
@@ -32,7 +32,4 @@ RSpec.describe RubySMB::SMB1::BitField::ShareAccess do
32
32
 
33
33
  it_behaves_like 'bit field with one flag set', :share_delete, 'V', 0x00000004
34
34
  end
35
-
36
-
37
-
38
35
  end
@@ -9,13 +9,11 @@ RSpec.describe RubySMB::SMB1::BitField::SmbExtFileAttributes do
9
9
  it { is_expected.to respond_to :read_only }
10
10
  it { is_expected.to respond_to :compressed }
11
11
  it { is_expected.to respond_to :temporary }
12
- it { is_expected.to respond_to :write_through }
13
- it { is_expected.to respond_to :no_buffering }
14
- it { is_expected.to respond_to :random_access }
15
- it { is_expected.to respond_to :sequential_scan }
16
- it { is_expected.to respond_to :delete_on_close }
17
- it { is_expected.to respond_to :backup_semantics }
18
- it { is_expected.to respond_to :posix_semantics }
12
+ it { is_expected.to respond_to :encrypted }
13
+ it { is_expected.to respond_to :not_content_indexed }
14
+ it { is_expected.to respond_to :offline }
15
+ it { is_expected.to respond_to :reparse_point }
16
+ it { is_expected.to respond_to :sparse }
19
17
 
20
18
  it 'is little endian' do
21
19
  expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
@@ -85,60 +83,43 @@ RSpec.describe RubySMB::SMB1::BitField::SmbExtFileAttributes do
85
83
  it_behaves_like 'bit field with one flag set', :compressed, 'V', 0x00000800
86
84
  end
87
85
 
88
- describe 'posix_semantics' do
86
+ describe 'encrypted' do
89
87
  it 'should be a 1-bit field per the SMB spec' do
90
- expect(attrs.posix_semantics).to be_a BinData::Bit1
88
+ expect(attrs.encrypted).to be_a BinData::Bit1
91
89
  end
92
90
 
93
- it_behaves_like 'bit field with one flag set', :posix_semantics, 'V', 0x01000000
91
+ it_behaves_like 'bit field with one flag set', :encrypted, 'V', 0x00004000
94
92
  end
95
93
 
96
- describe 'backup_semantics' do
94
+ describe 'not_content_indexed' do
97
95
  it 'should be a 1-bit field per the SMB spec' do
98
- expect(attrs.backup_semantics).to be_a BinData::Bit1
96
+ expect(attrs.not_content_indexed).to be_a BinData::Bit1
99
97
  end
100
98
 
101
- it_behaves_like 'bit field with one flag set', :backup_semantics, 'V', 0x02000000
99
+ it_behaves_like 'bit field with one flag set', :not_content_indexed, 'V', 0x00002000
102
100
  end
103
101
 
104
- describe 'delete_on_close' do
102
+ describe 'offline' do
105
103
  it 'should be a 1-bit field per the SMB spec' do
106
- expect(attrs.delete_on_close).to be_a BinData::Bit1
104
+ expect(attrs.offline).to be_a BinData::Bit1
107
105
  end
108
106
 
109
- it_behaves_like 'bit field with one flag set', :delete_on_close, 'V', 0x04000000
107
+ it_behaves_like 'bit field with one flag set', :offline, 'V', 0x00001000
110
108
  end
111
109
 
112
- describe 'sequential_scan' do
110
+ describe 'reparse_point' do
113
111
  it 'should be a 1-bit field per the SMB spec' do
114
- expect(attrs.sequential_scan).to be_a BinData::Bit1
112
+ expect(attrs.reparse_point).to be_a BinData::Bit1
115
113
  end
116
114
 
117
- it_behaves_like 'bit field with one flag set', :sequential_scan, 'V', 0x08000000
115
+ it_behaves_like 'bit field with one flag set', :reparse_point, 'V', 0x00000400
118
116
  end
119
117
 
120
- describe 'random_access' do
118
+ describe 'sparse' do
121
119
  it 'should be a 1-bit field per the SMB spec' do
122
- expect(attrs.random_access).to be_a BinData::Bit1
120
+ expect(attrs.sparse).to be_a BinData::Bit1
123
121
  end
124
122
 
125
- it_behaves_like 'bit field with one flag set', :random_access, 'V', 0x10000000
123
+ it_behaves_like 'bit field with one flag set', :sparse, 'V', 0x00000200
126
124
  end
127
-
128
- describe 'no_buffering' do
129
- it 'should be a 1-bit field per the SMB spec' do
130
- expect(attrs.no_buffering).to be_a BinData::Bit1
131
- end
132
-
133
- it_behaves_like 'bit field with one flag set', :no_buffering, 'V', 0x20000000
134
- end
135
-
136
- describe 'write_through' do
137
- it 'should be a 1-bit field per the SMB spec' do
138
- expect(attrs.write_through).to be_a BinData::Bit1
139
- end
140
-
141
- it_behaves_like 'bit field with one flag set', :write_through, 'V', 0x80000000
142
- end
143
-
144
125
  end
@@ -104,10 +104,4 @@ RSpec.describe RubySMB::SMB1::BitField::SmbFileAttributes do
104
104
 
105
105
  it_behaves_like 'bit field with one flag set', :search_archive, 'v', 0x2000
106
106
  end
107
-
108
-
109
-
110
-
111
-
112
-
113
107
  end
@@ -48,6 +48,4 @@ RSpec.describe RubySMB::SMB1::BitField::SmbNmpipeStatus do
48
48
 
49
49
  it_behaves_like 'bit field with one flag set', :nonblocking, 'v', 0x8000
50
50
  end
51
-
52
-
53
51
  end
@@ -23,6 +23,4 @@ RSpec.describe RubySMB::SMB1::BitField::Trans2Flags do
23
23
 
24
24
  it_behaves_like 'bit field with one flag set', :disconnect, 'C', 0x01
25
25
  end
26
-
27
-
28
26
  end
@@ -32,6 +32,4 @@ RSpec.describe RubySMB::SMB1::BitField::TreeConnectFlags do
32
32
 
33
33
  it_behaves_like 'bit field with one flag set', :disconnect, 'C', 0x01
34
34
  end
35
-
36
-
37
35
  end
@@ -0,0 +1,469 @@
1
+ RSpec.describe RubySMB::SMB1::File do
2
+
3
+ let(:dispatcher) { RubySMB::Dispatcher::Socket.new(double('socket')) }
4
+ let(:client) { RubySMB::Client.new(dispatcher, username: 'msfadmin', password: 'msfadmin') }
5
+ let(:connect_response) {
6
+ packet = RubySMB::SMB1::Packet::TreeConnectResponse.new
7
+ packet.smb_header.tid = 2051
8
+ packet.parameter_block.guest_access_rights.read("\xff\x01\x1f\x00")
9
+ packet.parameter_block.access_rights.read("\xff\x01\x1f\x01")
10
+ packet
11
+ }
12
+ let(:tree) { RubySMB::SMB1::Tree.new(client: client, share: '\\1.2.3.4\Share', response: connect_response) }
13
+ let(:nt_create_andx_response) {
14
+ response = RubySMB::SMB1::Packet::NtCreateAndxResponse.new
15
+ response.parameter_block.ext_file_attributes = { normal: 1 }
16
+ response.parameter_block.fid = 0x4000
17
+ response.parameter_block.last_access_time = DateTime.parse("2017-09-20T1:1:1")
18
+ response.parameter_block.last_change_time = DateTime.parse("2017-09-22T2:2:2")
19
+ response.parameter_block.last_write_time = DateTime.parse("2017-09-25T3:3:3")
20
+ response.parameter_block.end_of_file = 53
21
+ response.parameter_block.allocation_size = 4096
22
+ response
23
+ }
24
+ let(:filename) { 'specfile.txt' }
25
+
26
+ subject(:file) {
27
+ described_class.new(tree: tree, response: nt_create_andx_response, name: filename)
28
+ }
29
+
30
+ it { is_expected.to respond_to :tree }
31
+ it { is_expected.to respond_to :name }
32
+ it { is_expected.to respond_to :attributes }
33
+ it { is_expected.to respond_to :fid }
34
+ it { is_expected.to respond_to :last_access }
35
+ it { is_expected.to respond_to :last_change }
36
+ it { is_expected.to respond_to :last_write }
37
+ it { is_expected.to respond_to :size }
38
+ it { is_expected.to respond_to :size_on_disk }
39
+
40
+ it 'raises an exception when tree is no provided' do
41
+ expect { described_class.new(tree: nil, response: nt_create_andx_response, name: filename) }.to raise_error(ArgumentError)
42
+ end
43
+
44
+ it 'raises an exception when response is no provided' do
45
+ expect { described_class.new(tree: tree, response: nil, name: filename) }.to raise_error(ArgumentError)
46
+ end
47
+
48
+ it 'raises an exception when name is no provided' do
49
+ expect { described_class.new(tree: tree, response: nt_create_andx_response, name: nil) }.to raise_error(ArgumentError)
50
+ end
51
+
52
+ it 'inherits the tree that spawned it' do
53
+ expect(file.tree).to eq tree
54
+ end
55
+
56
+ it 'sets the file name to the name passed as argument' do
57
+ expect(file.name).to eq filename
58
+ end
59
+
60
+ it 'inherits the attributes from the response packet' do
61
+ expect(file.attributes).to eq nt_create_andx_response.parameter_block.ext_file_attributes
62
+ end
63
+
64
+ it 'inherits the file ID from the response packet' do
65
+ expect(file.fid).to eq nt_create_andx_response.parameter_block.fid
66
+ end
67
+
68
+ it 'inherits the last_access from the response packet' do
69
+ expect(file.last_access).to eq nt_create_andx_response.parameter_block.last_access_time.to_datetime
70
+ end
71
+
72
+ it 'inherits the last_change from the response packet' do
73
+ expect(file.last_change).to eq nt_create_andx_response.parameter_block.last_change_time.to_datetime
74
+ end
75
+
76
+ it 'inherits the last_write from the response packet' do
77
+ expect(file.last_write).to eq nt_create_andx_response.parameter_block.last_write_time.to_datetime
78
+ end
79
+
80
+ it 'inherits the size from the response packet' do
81
+ expect(file.size).to eq nt_create_andx_response.parameter_block.end_of_file
82
+ end
83
+
84
+ it 'inherits the size_on_disk from the response packet' do
85
+ expect(file.size_on_disk).to eq nt_create_andx_response.parameter_block.allocation_size
86
+ end
87
+
88
+ describe '#set_header_fields' do
89
+ let(:request) { RubySMB::SMB1::Packet::ReadAndxRequest.new }
90
+ it 'calls the set_header_field method from the Tree' do
91
+ expect(tree).to receive(:set_header_fields).with(request).and_call_original
92
+ file.set_header_fields(request)
93
+ end
94
+
95
+ it 'sets the packet file_id from the guid' do
96
+ expect(file.set_header_fields(request).parameter_block.fid).to eq file.fid
97
+ end
98
+ end
99
+
100
+ describe '#read_packet' do
101
+ it 'creates a new ReadAndxRequest packet' do
102
+ expect(RubySMB::SMB1::Packet::ReadAndxRequest).to receive(:new).and_call_original
103
+ file.read_packet
104
+ end
105
+
106
+ it 'calls #set_header_fields to set ReadAndxRequest header fields' do
107
+ request = RubySMB::SMB1::Packet::ReadAndxRequest.new
108
+ allow(RubySMB::SMB1::Packet::ReadAndxRequest).to receive(:new).and_return(request)
109
+ expect(file).to receive(:set_header_fields).with(request).and_call_original
110
+ file.read_packet
111
+ end
112
+
113
+ it 'sets the read_length of the packet' do
114
+ expect(file.read_packet(read_length: 55).parameter_block.max_count_of_bytes_to_return).to eq 55
115
+ end
116
+
117
+ it 'sets the offset of the packet' do
118
+ expect(file.read_packet(offset: 55).parameter_block.offset).to eq 55
119
+ end
120
+ end
121
+
122
+ describe '#read' do
123
+ let(:read_data) { 'read data' }
124
+ let(:raw_response) { double('fake raw response data') }
125
+ let(:read_andx_response) {
126
+ res = RubySMB::SMB1::Packet::ReadAndxResponse.new
127
+ res.data_block.data = read_data
128
+ res
129
+ }
130
+
131
+ before :example do
132
+ allow(client).to receive(:send_recv).and_return(raw_response)
133
+ allow(RubySMB::SMB1::Packet::ReadAndxResponse).to receive(:read).with(raw_response).and_return(read_andx_response)
134
+ end
135
+
136
+ context 'when the number of bytes to read is not provided' do
137
+ it 'reads #size bytes by default' do
138
+ expect(file).to receive(:read_packet).with(read_length: file.size, offset: 0).once.and_call_original
139
+ expect(file.read).to eq(read_data)
140
+ end
141
+ end
142
+
143
+ context 'when the number of bytes to read is less than or equal to max_buffer_size' do
144
+ it 'reads only one packet with the number of bytes provided as argument' do
145
+ client.max_buffer_size = read_data.size
146
+ expect(file).to receive(:read_packet).with(read_length: read_data.size, offset: 0).once.and_call_original
147
+ expect(file.read(bytes: read_data.size)).to eq(read_data)
148
+ end
149
+ end
150
+
151
+ context 'when the number of bytes to read is greater than max_buffer_size' do
152
+ it 'reads multiple packets with at most max_buffer_size bytes per chunk' do
153
+ client.max_buffer_size = read_data.size - 1
154
+ read_io = StringIO.new(read_data)
155
+ expect(file).to receive(:read_packet).with(read_length: client.max_buffer_size, offset: 0).once.ordered.and_call_original
156
+ expect(file).to receive(:read_packet).with(read_length: (read_data.size - client.max_buffer_size), offset: client.max_buffer_size).once.ordered.and_call_original
157
+ allow(RubySMB::SMB1::Packet::ReadAndxResponse).to receive(:read).with(raw_response) do
158
+ read_andx_response.data_block.data = read_io.read(client.max_buffer_size)
159
+ read_andx_response
160
+ end
161
+ expect(file.read(bytes: read_data.size)).to eq(read_data)
162
+ end
163
+ end
164
+
165
+ context 'when sending the request packet and gets a response back' do
166
+ context 'when the response is not a ReadAndxResponse packet' do
167
+ it 'raise an InvalidPacket exception' do
168
+ read_andx_response.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_ECHO
169
+ expect { file.read }.to raise_error(RubySMB::Error::InvalidPacket)
170
+ end
171
+ end
172
+
173
+ context 'when the response status code is not STATUS_SUCCESS' do
174
+ it 'raise an UnexpectedStatusCode exception' do
175
+ read_andx_response.smb_header.nt_status = WindowsError::NTStatus::STATUS_INVALID_HANDLE.value
176
+ expect { file.read }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
177
+ end
178
+ end
179
+
180
+ context 'when the response is an EmptyPacket with the SMB_COM_READ_ANDX command and STATUS_SUCCESS status code' do
181
+ let(:empty_packet) do
182
+ empty_packet = RubySMB::SMB1::Packet::EmptyPacket.new
183
+ empty_packet.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_READ_ANDX
184
+ empty_packet
185
+ end
186
+
187
+ it 'returns an empty string if it is the first request' do
188
+ allow(RubySMB::SMB1::Packet::ReadAndxResponse).to receive(:read).with(raw_response).and_return(empty_packet)
189
+ expect(file.read).to eq('')
190
+ end
191
+
192
+ it 'returns the current read data if it happens after the first request' do
193
+ partial_data = read_data[0..-2]
194
+ client.max_buffer_size = partial_data.size
195
+ first_request = true
196
+ allow(RubySMB::SMB1::Packet::ReadAndxResponse).to receive(:read).with(raw_response).twice do
197
+ if first_request
198
+ read_andx_response.data_block.data = partial_data
199
+ first_request = false
200
+ read_andx_response
201
+ else
202
+ empty_packet
203
+ end
204
+ end
205
+ expect(file.read(bytes: read_data.size)).to eq(partial_data)
206
+ end
207
+ end
208
+ end
209
+ end
210
+
211
+ describe '#write_packet' do
212
+ it 'creates a new WriteAndxRequest packet' do
213
+ expect(RubySMB::SMB1::Packet::WriteAndxRequest).to receive(:new).and_call_original
214
+ file.write_packet
215
+ end
216
+
217
+ it 'calls #set_header_fields to set WriteAndxRequest headers' do
218
+ request = RubySMB::SMB1::Packet::WriteAndxRequest.new
219
+ allow(RubySMB::SMB1::Packet::WriteAndxRequest).to receive(:new).and_return(request)
220
+ expect(file).to receive(:set_header_fields).with(request).and_call_original
221
+ file.write_packet
222
+ end
223
+
224
+ it 'sets the offset of the packet' do
225
+ expect(file.write_packet(offset: 55).parameter_block.offset).to eq 55
226
+ end
227
+
228
+ it 'sets the write_mode to writethrough_mode' do
229
+ expect(file.write_packet.parameter_block.write_mode.writethrough_mode).to eq 1
230
+ end
231
+
232
+ it 'sets the remaining number of bytes to data_length value' do
233
+ data = 'write data'
234
+ write_andx_request = file.write_packet(data: data)
235
+ expect(write_andx_request.parameter_block.remaining).to eq write_andx_request.parameter_block.data_length
236
+ expect(write_andx_request.parameter_block.remaining).to eq data.size
237
+ end
238
+
239
+ it 'sets the data of the packet' do
240
+ data = 'write data'
241
+ expect(file.write_packet(data: data).data_block.data).to eq data
242
+ end
243
+ end
244
+
245
+ describe '#write' do
246
+ let(:write_data) { 'write data' }
247
+ let(:raw_response) { double('fake raw response data') }
248
+ let(:write_andx_response) do
249
+ response = RubySMB::SMB1::Packet::WriteAndxResponse.new
250
+ response.parameter_block.count_low = write_data.size
251
+ response
252
+ end
253
+
254
+ before :example do
255
+ allow(client).to receive(:send_recv).and_return(raw_response)
256
+ allow(RubySMB::SMB1::Packet::WriteAndxResponse).to receive(:read).with(raw_response).and_return(write_andx_response)
257
+ end
258
+
259
+ describe 'offset' do
260
+ it 'writes from offset 0 by default' do
261
+ expect(file).to receive(:write_packet).with(data: write_data, offset: 0).and_call_original
262
+ file.write(data: write_data)
263
+ end
264
+
265
+ it 'writes from the offset passed as arguement' do
266
+ offset = 10
267
+ expect(file).to receive(:write_packet).with(data: write_data, offset: offset).and_call_original
268
+ file.write(data: write_data, offset: offset)
269
+ end
270
+ end
271
+
272
+ context 'when the buffer size is less than or equal to max_buffer_size' do
273
+ it 'sends only one packet with the entire buffer' do
274
+ client.max_buffer_size = write_data.size
275
+ expect(file).to receive(:write_packet).with(data: write_data, offset: 0).once.and_call_original
276
+ expect(file.write(data: write_data)).to eq write_data.size
277
+ end
278
+ end
279
+
280
+ context 'when the buffer size is greater than max_buffer_size' do
281
+ it 'sends multiple packets with at most max_buffer_size bytes per chunk' do
282
+ client.max_buffer_size = write_data.size - 1
283
+ first_data_chunk = write_data[0, client.max_buffer_size]
284
+ second_data_chunk = write_data[client.max_buffer_size..-1]
285
+ original_write_packet = file.method(:write_packet)
286
+
287
+ expect(file).to receive(:write_packet).with(data: first_data_chunk , offset: 0).once do
288
+ allow(RubySMB::SMB1::Packet::WriteAndxResponse).to receive(:read).with(raw_response) do
289
+ write_andx_response.parameter_block.count_low = first_data_chunk.size
290
+ write_andx_response
291
+ end
292
+ original_write_packet.call(data: first_data_chunk, offset: 0)
293
+ end
294
+ expect(file).to receive(:write_packet).with(data: second_data_chunk, offset: client.max_buffer_size).once do
295
+ allow(RubySMB::SMB1::Packet::WriteAndxResponse).to receive(:read).with(raw_response) do
296
+ write_andx_response.parameter_block.count_low = second_data_chunk.size
297
+ write_andx_response
298
+ end
299
+ original_write_packet.call(data: second_data_chunk, offset: client.max_buffer_size)
300
+ end
301
+ expect(file.write(data: write_data)).to eq write_data.size
302
+ end
303
+ end
304
+
305
+ context 'when sending the request packet and gets a response back' do
306
+ context 'when the response is not a WriteAndxResponse packet' do
307
+ it 'raise an InvalidPacket exception' do
308
+ write_andx_response.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_ECHO
309
+ expect { file.write(data: write_data) }.to raise_error(RubySMB::Error::InvalidPacket)
310
+ end
311
+ end
312
+
313
+ context 'when the response status code is not STATUS_SUCCESS' do
314
+ it 'raise an UnexpectedStatusCode exception' do
315
+ write_andx_response.smb_header.nt_status = WindowsError::NTStatus::STATUS_INVALID_HANDLE.value
316
+ expect { file.write(data: write_data) }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
317
+ end
318
+ end
319
+ end
320
+ end
321
+
322
+ describe '#append' do
323
+ it 'calls #write with the expected data and the offset set to the end of the file (file size)' do
324
+ data = 'append data'
325
+ expect(file).to receive(:write).with(data: data, offset: file.size)
326
+ file.append(data: data)
327
+ end
328
+ end
329
+
330
+ describe '#delete' do
331
+ let(:set_info_res) { RubySMB::SMB1::Packet::Trans2::SetFileInformationResponse.new }
332
+
333
+ before :example do
334
+ allow(client).to receive(:send_recv)
335
+ allow(RubySMB::SMB1::Packet::Trans2::SetFileInformationResponse).to receive(:read).and_return(set_info_res)
336
+ end
337
+
338
+ it 'calls #delete_packet' do
339
+ expect(file).to receive(:delete_packet)
340
+ file.delete
341
+ end
342
+
343
+ it 'returns the response NTStatus code' do
344
+ status = WindowsError::NTStatus::STATUS_SUCCESS
345
+ set_info_res.smb_header.nt_status = status.value
346
+ expect(file.delete).to eq status
347
+ end
348
+ end
349
+
350
+ describe '#delete_packet' do
351
+ let(:request) do
352
+ request = RubySMB::SMB1::Packet::Trans2::SetFileInformationRequest.new
353
+ passthrough_info_level = RubySMB::Fscc::FileInformation::FILE_DISPOSITION_INFORMATION +
354
+ RubySMB::Fscc::FileInformation::SMB_INFO_PASSTHROUGH
355
+ request.data_block.trans2_parameters.information_level = passthrough_info_level
356
+ request
357
+ end
358
+
359
+ before :example do
360
+ allow(RubySMB::SMB1::Packet::Trans2::SetFileInformationRequest).to receive(:new).and_return(request)
361
+ end
362
+
363
+ it 'creates a new Trans2 SetFileInformationRequest packet' do
364
+ expect(RubySMB::SMB1::Packet::Trans2::SetFileInformationRequest).to receive(:new).and_return(request)
365
+ file.delete_packet
366
+ end
367
+
368
+ it 'calls Tree #set_header_fields to set SetFileInformationRequest headers' do
369
+ expect(tree).to receive(:set_header_fields).with(request).and_call_original
370
+ file.delete_packet
371
+ end
372
+
373
+ it 'sets the Trans2 parameter #fid of the packet with the File FID' do
374
+ fid = 0x1000
375
+ file.fid = fid
376
+ expect(file.delete_packet.data_block.trans2_parameters.fid).to eq fid
377
+ end
378
+
379
+ it 'sets the Trans2 parameter #information_level of the packet' do
380
+ request.data_block.trans2_parameters.information_level = 0
381
+ passthrough_info_level = RubySMB::Fscc::FileInformation::FILE_DISPOSITION_INFORMATION +
382
+ RubySMB::Fscc::FileInformation::SMB_INFO_PASSTHROUGH
383
+ expect(file.delete_packet.data_block.trans2_parameters.information_level).to eq passthrough_info_level
384
+ end
385
+
386
+ it 'sets the File Information #delete_pending field of the packet' do
387
+ expect(file.delete_packet.data_block.trans2_data.info_level_struct.delete_pending).to eq 1
388
+ end
389
+
390
+ it 'sets the Trans2 ParameterBlock fields' do
391
+ parameter_block = file.delete_packet.parameter_block
392
+ expect(parameter_block.total_parameter_count).to eq request.parameter_block.parameter_count
393
+ expect(parameter_block.total_data_count).to eq request.parameter_block.data_count
394
+ expect(parameter_block.max_parameter_count).to eq request.parameter_block.parameter_count
395
+ expect(parameter_block.max_data_count).to eq 16_384
396
+ end
397
+ end
398
+
399
+ describe '#rename' do
400
+ let(:set_info_res) { RubySMB::SMB1::Packet::Trans2::SetFileInformationResponse.new }
401
+ let(:filename) { 'file.txt' }
402
+
403
+ before :example do
404
+ allow(client).to receive(:send_recv)
405
+ allow(RubySMB::SMB1::Packet::Trans2::SetFileInformationResponse).to receive(:read).and_return(set_info_res)
406
+ end
407
+
408
+ it 'calls #rename_packet' do
409
+ expect(file).to receive(:rename_packet)
410
+ file.rename(filename)
411
+ end
412
+
413
+ it 'returns the response NTStatus code' do
414
+ status = WindowsError::NTStatus::STATUS_SUCCESS
415
+ set_info_res.smb_header.nt_status = status.value
416
+ expect(file.rename(filename)).to eq status
417
+ end
418
+ end
419
+
420
+ describe '#rename_packet' do
421
+ let(:request) do
422
+ request = RubySMB::SMB1::Packet::Trans2::SetFileInformationRequest.new
423
+ passthrough_info_level = RubySMB::Fscc::FileInformation::FILE_RENAME_INFORMATION +
424
+ RubySMB::Fscc::FileInformation::SMB_INFO_PASSTHROUGH
425
+ request.data_block.trans2_parameters.information_level = passthrough_info_level
426
+ request
427
+ end
428
+
429
+ before :example do
430
+ allow(RubySMB::SMB1::Packet::Trans2::SetFileInformationRequest).to receive(:new).and_return(request)
431
+ end
432
+
433
+ it 'creates a new Trans2 SetFileInformationRequest packet' do
434
+ expect(RubySMB::SMB1::Packet::Trans2::SetFileInformationRequest).to receive(:new).and_return(request)
435
+ file.rename_packet(filename)
436
+ end
437
+
438
+ it 'calls Tree #set_header_fields to set SetFileInformationRequest headers' do
439
+ expect(tree).to receive(:set_header_fields).with(request).and_call_original
440
+ file.rename_packet(filename)
441
+ end
442
+
443
+ it 'sets the Trans2 parameter #fid of the packet with the File FID' do
444
+ fid = 0x1000
445
+ file.fid = fid
446
+ expect(file.rename_packet(filename).data_block.trans2_parameters.fid).to eq fid
447
+ end
448
+
449
+ it 'sets the Trans2 parameter #information_level of the packet' do
450
+ request.data_block.trans2_parameters.information_level = 0
451
+ passthrough_info_level = RubySMB::Fscc::FileInformation::FILE_RENAME_INFORMATION +
452
+ RubySMB::Fscc::FileInformation::SMB_INFO_PASSTHROUGH
453
+ expect(file.rename_packet(filename).data_block.trans2_parameters.information_level).to eq passthrough_info_level
454
+ end
455
+
456
+ it 'sets the File Information #rename_pending field of the packet' do
457
+ expect(file.rename_packet(filename).data_block.trans2_data.info_level_struct.file_name).to eq filename.encode('utf-16le').force_encoding('ASCII-8BIT')
458
+ end
459
+
460
+ it 'sets the Trans2 ParameterBlock fields' do
461
+ parameter_block = file.rename_packet(filename).parameter_block
462
+ expect(parameter_block.total_parameter_count).to eq request.parameter_block.parameter_count
463
+ expect(parameter_block.total_data_count).to eq request.parameter_block.data_count
464
+ expect(parameter_block.max_parameter_count).to eq request.parameter_block.parameter_count
465
+ expect(parameter_block.max_data_count).to eq 16_384
466
+ end
467
+ end
468
+ end
469
+