Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'release/20111213184834' into stable

  • Loading branch information...
commit 5249e74ca789e602edc8900cd8e8dbadc640f43c 2 parents cf0cbff + 3a20d53
Jenkins authored
Showing with 4,178 additions and 315 deletions.
  1. +1 −0  README
  2. BIN  data/armitage/armitage.jar
  3. +25 −0 data/armitage/whatsnew.txt
  4. BIN  data/gui/msfgui.jar
  5. +9 −0 data/sql/migrate/20111210000000_add_scope_to_hosts.rb
  6. +3 −9 external/source/gui/msfguijava/src/msfgui/MsfTable.java
  7. +14 −47 external/source/gui/msfguijava/src/msfgui/OpenConnectionDialog.form
  8. +15 −45 external/source/gui/msfguijava/src/msfgui/OpenConnectionDialog.java
  9. +7 −8 external/source/gui/msfguijava/src/msfgui/PayloadPopup.form
  10. +3 −15 external/source/gui/msfguijava/src/msfgui/PayloadPopup.java
  11. +5 −14 external/source/gui/msfguijava/src/msfgui/RpcConnection.java
  12. +0 −1  external/source/gui/msfguijava/src/msfgui/XmlRpc.java
  13. +0 −2  external/source/gui/msfguijava/src/msfgui/resources/OpenConnectionDialog.properties
  14. +14 −0 lib/bit-struct.rb
  15. +187 −0 lib/bit-struct/README
  16. +574 −0 lib/bit-struct/bit-struct.rb
  17. +48 −0 lib/bit-struct/char-field.rb
  18. +300 −0 lib/bit-struct/fields.rb
  19. +61 −0 lib/bit-struct/float-field.rb
  20. +20 −0 lib/bit-struct/hex-octet-field.rb
  21. +76 −0 lib/bit-struct/nested-field.rb
  22. +45 −0 lib/bit-struct/octet-field.rb
  23. +15 −0 lib/bit-struct/pad-field.rb
  24. +258 −0 lib/bit-struct/signed-field.rb
  25. +44 −0 lib/bit-struct/text-field.rb
  26. +248 −0 lib/bit-struct/unsigned-field.rb
  27. +77 −0 lib/bit-struct/vector-field.rb
  28. +173 −0 lib/bit-struct/vector.rb
  29. +69 −0 lib/bit-struct/yaml.rb
  30. +3 −0  lib/msf/core/auxiliary/crawler.rb
  31. +13 −4 lib/msf/core/db.rb
  32. +10 −1 lib/msf/core/handler/reverse_http.rb
  33. +16 −4 lib/msf/core/handler/reverse_https.rb
  34. +33 −0 lib/msf/core/handler/reverse_ipv6_http.rb
  35. +33 −0 lib/msf/core/handler/reverse_ipv6_https.rb
  36. +5 −0 lib/msf/core/model/web_site.rb
  37. +3 −3 lib/msf/core/module_manager.rb
  38. +2 −3 lib/msf/core/option_container.rb
  39. +1 −1  lib/msf/core/post/osx/system.rb
  40. +4 −4 lib/msf/core/rpc/v10/service.rb
  41. +22 −20 lib/msf/ui/console/command_dispatcher/db.rb
  42. +12 −1 lib/rex/proto/http/client.rb
  43. +9 −13 lib/rex/socket.rb
  44. +59 −13 lib/rex/socket/comm/local.rb
  45. +44 −19 lib/rex/socket/range_walker.rb
  46. +1 −1  lib/rex/socket/udp.rb
  47. +1 −1  modules/auxiliary/admin/http/typo3_sa_2009_002.rb
  48. +500 −0 modules/auxiliary/fuzzers/dns/dns_fuzzer.rb
  49. +5 −5 modules/auxiliary/gather/shodan_search.rb
  50. +0 −4 modules/auxiliary/scanner/h323/h323_version.rb
  51. +1 −1  modules/auxiliary/scanner/http/crawler.rb
  52. +79 −0 modules/auxiliary/scanner/http/yaws_traversal.rb
  53. +35 −6 modules/auxiliary/scanner/tftp/ipswitch_whatsupgold_tftp.rb
  54. +10 −4 modules/auxiliary/server/capture/http.rb
  55. +2 −0  modules/auxiliary/server/pxexploit.rb
  56. +79 −0 modules/auxiliary/sqli/oracle/dbms_cdc_subscribe_activate_subscription.rb
  57. +99 −0 modules/exploits/multi/http/familycms_less_exec.rb
  58. +2 −2 modules/exploits/multi/http/phpscheduleit_start_date.rb
  59. +105 −0 modules/exploits/multi/http/pmwiki_pagelist.rb
  60. +3 −0  modules/exploits/multi/http/tomcat_mgr_deploy.rb
  61. +108 −0 modules/exploits/multi/http/traq_plugin_exec.rb
  62. +1 −1  modules/exploits/unix/webapp/awstats_migrate_exec.rb
  63. +1 −1  modules/exploits/unix/webapp/joomla_tinybrowser.rb
  64. +1 −1  modules/exploits/unix/webapp/sphpblog_file_upload.rb
  65. +1 −1  modules/exploits/windows/browser/greendam_url.rb
  66. +112 −0 modules/exploits/windows/ftp/ability_server_stor.rb
  67. +1 −1  modules/exploits/windows/ftp/ms09_053_ftpd_nlst.rb
  68. +1 −1  modules/exploits/windows/misc/avidphoneticindexer.rb
  69. +107 −0 modules/exploits/windows/scada/codesys_web_server.rb
  70. +1 −1  modules/exploits/windows/tftp/attftp_long_filename.rb
  71. +1 −1  modules/payloads/stagers/linux/x86/reverse_ipv6_tcp.rb
  72. +98 −0 modules/payloads/stagers/windows/reverse_ipv6_http.rb
  73. +100 −0 modules/payloads/stagers/windows/reverse_ipv6_https.rb
  74. +4 −9 modules/post/windows/escalate/service_permissions.rb
  75. +5 −4 modules/post/windows/gather/win_privs.rb
  76. +5 −2 modules/post/windows/manage/enable_rdp.rb
  77. +96 −41 modules/post/windows/manage/persistence.rb
  78. +31 −0 scripts/resource/run_all_post.rc
  79. +22 −0 tools/msftidy.rb
View
1  README
@@ -34,6 +34,7 @@ The copyright on this package is held by Rapid7 LLC.
This license does not apply to the following components:
+ - The Bit-Struct library located under lib/bit-struct
- The OpenSSL library embedded into the Meterpreter payload binaries and the
corresponding header files in the source tree
- The Packet Sniffer SDK (MicroOLAP) library embedded into the Meterpreter
View
BIN  data/armitage/armitage.jar
Binary file not shown
View
25 data/armitage/whatsnew.txt
@@ -1,6 +1,31 @@
Armitage Changelog
==================
+12 Dec 11
+---------
+- Armitage teaming mode now downloads the resulting file for any fileformat
+ exploit.
+- Armitage -> Set Exploit Rank and Set Target View now show a * next to an
+ item to indicate the current setting.
+- Shift+click on Launch in a module launch dialog will not close the module
+ launch dialog. One use case for this: set up a payload multi/handler,
+ shift+click Launch to do it, then change output type to exe, click Launch
+ and you're all set.
+- Dynamic Workspace editor now trims whitespace from your entries. Errant
+ whitespace causes Armitage to reject the entry and your workspace never
+ acivates.
+- Updated the "msfrpcd died" troubleshooting dialog. The new one takes folks
+ to a website with detailed information.
+- Armitage now uses "load" to load a meterpreter module instead of "use"
+- Key logger event log announcement now notes the session ID. This is so
+ your teammates will know not to migrate that session since it's recording
+ key strokes.
+- Right-click X in tab -> Save Screenshot now displays filename without the
+ path.
+- Deconfliction server now detects when database is not available and offers
+ troubleshooting steps.
+- Loot/Downloads viewer now has a right-click menu to Copy selected text.
+
22 Nov 11 - A big improvement...
---------
- Services refresh is now set to 30s (vs. 60s before)
View
BIN  data/gui/msfgui.jar
Binary file not shown
View
9 data/sql/migrate/20111210000000_add_scope_to_hosts.rb
@@ -0,0 +1,9 @@
+class AddScopeToHosts < ActiveRecord::Migration
+ def self.up
+ add_column :hosts, :scope, :text
+ end
+
+ def self.down
+ remove_column :hosts, :scope
+ end
+end
View
12 external/source/gui/msfguijava/src/msfgui/MsfTable.java
@@ -97,15 +97,9 @@ public void reAddQuery(boolean force, int offset) {
if(!force && !DraggableTabbedPane.isVisible(this))
return; //Don't re-add if not visible
try {
- Object arg;
- if(rpcConn.type.equals("XML") && (dbTable.equals("events") || dbTable.equals("loots"))){
- arg = MsfguiApp.workspace;
- } else {
- HashMap argM = new HashMap(10);
- argM.put("workspace", MsfguiApp.workspace);
- argM.put("offset", offset);
- arg = argM;
- }
+ HashMap arg = new HashMap(10);
+ arg.put("workspace", MsfguiApp.workspace);
+ arg.put("offset", offset);
List data = (List) ((Map)rpcConn.execute("db."+dbTable, arg)).get(dbTable);
if(data == null)
return;
View
61 external/source/gui/msfguijava/src/msfgui/OpenConnectionDialog.form
@@ -44,24 +44,18 @@
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="disableDbButton" min="-2" max="-2" attributes="0"/>
- <Component id="usernameField" alignment="0" pref="427" max="32767" attributes="0"/>
- <Component id="hostField" alignment="0" pref="427" max="32767" attributes="0"/>
- <Component id="passwordField" alignment="0" pref="427" max="32767" attributes="0"/>
- <Component id="portField" alignment="0" pref="427" max="32767" attributes="0"/>
- <Group type="102" alignment="0" attributes="0">
- <Component id="sslBox" min="-2" max="-2" attributes="0"/>
- <EmptySpace pref="250" max="32767" attributes="0"/>
- <Component id="xmlButton" min="-2" max="-2" attributes="0"/>
- <EmptySpace type="separate" max="-2" attributes="0"/>
- <Component id="msgpackButton" min="-2" max="-2" attributes="0"/>
- </Group>
+ <Component id="usernameField" alignment="0" pref="433" max="32767" attributes="0"/>
+ <Component id="hostField" alignment="0" pref="433" max="32767" attributes="0"/>
+ <Component id="passwordField" alignment="0" pref="433" max="32767" attributes="0"/>
+ <Component id="portField" alignment="0" pref="433" max="32767" attributes="0"/>
+ <Component id="sslBox" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="startNewButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="pathButton" min="-2" max="-2" attributes="0"/>
- <EmptySpace pref="126" max="32767" attributes="0"/>
+ <EmptySpace pref="132" max="32767" attributes="0"/>
<Component id="cancelButton" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="connectButton" min="-2" max="-2" attributes="0"/>
@@ -97,22 +91,14 @@
<Component id="portLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
- <Group type="103" groupAlignment="0" attributes="0">
- <Group type="102" attributes="0">
- <Group type="103" groupAlignment="1" max="-2" attributes="0">
- <Component id="sslBox" alignment="0" min="0" pref="0" max="32767" attributes="1"/>
- <Component id="sslLabel" alignment="0" max="32767" attributes="1"/>
- </Group>
- <EmptySpace max="-2" attributes="0"/>
- <Group type="103" groupAlignment="3" attributes="0">
- <Component id="disableDbLabel" alignment="3" min="-2" max="-2" attributes="0"/>
- <Component id="disableDbButton" alignment="3" min="-2" pref="18" max="-2" attributes="0"/>
- </Group>
- </Group>
- <Group type="103" alignment="0" groupAlignment="3" attributes="0">
- <Component id="msgpackButton" alignment="3" min="-2" max="-2" attributes="0"/>
- <Component id="xmlButton" alignment="3" min="-2" max="-2" attributes="0"/>
- </Group>
+ <Group type="103" groupAlignment="1" max="-2" attributes="0">
+ <Component id="sslBox" alignment="0" min="0" pref="0" max="32767" attributes="1"/>
+ <Component id="sslLabel" alignment="0" max="32767" attributes="1"/>
+ </Group>
+ <EmptySpace max="-2" attributes="0"/>
+ <Group type="103" groupAlignment="3" attributes="0">
+ <Component id="disableDbLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+ <Component id="disableDbButton" alignment="3" min="-2" pref="18" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
@@ -268,24 +254,5 @@
<Property name="name" type="java.lang.String" value="disableDbButton" noResource="true"/>
</Properties>
</Component>
- <Component class="javax.swing.JRadioButton" name="xmlButton">
- <Properties>
- <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
- <ComponentRef name="buttonGroup1"/>
- </Property>
- <Property name="selected" type="boolean" value="true"/>
- <Property name="text" type="java.lang.String" resourceKey="xmlButton.text"/>
- <Property name="name" type="java.lang.String" value="xmlButton" noResource="true"/>
- </Properties>
- </Component>
- <Component class="javax.swing.JRadioButton" name="msgpackButton">
- <Properties>
- <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
- <ComponentRef name="buttonGroup1"/>
- </Property>
- <Property name="text" type="java.lang.String" resourceKey="msgpackButton.text"/>
- <Property name="name" type="java.lang.String" value="msgpackButton" noResource="true"/>
- </Properties>
- </Component>
</SubComponents>
</Form>
View
60 external/source/gui/msfguijava/src/msfgui/OpenConnectionDialog.java
@@ -104,7 +104,7 @@ public static RpcConnection getConnection(MainFrame mainframe) {
int port = Integer.parseInt(info.get("port").toString());
boolean ssl = Boolean.parseBoolean(info.get("ssl").toString());
String type = info.get("type").toString();
- RpcConnection rpc = RpcConnection.getConn(type, username, password.toCharArray(), host, port, ssl);
+ RpcConnection rpc = RpcConnection.getConn(username, password.toCharArray(), host, port, ssl);
if(javax.swing.JOptionPane.showConfirmDialog(null, "Connect to last remembered rpcd?") == javax.swing.JOptionPane.YES_OPTION)
return rpc;
rpc.execute("auth.logout");
@@ -154,8 +154,6 @@ private void initComponents() {
sslLabel = new javax.swing.JLabel();
disableDbLabel = new javax.swing.JLabel();
disableDbButton = new javax.swing.JCheckBox();
- xmlButton = new javax.swing.JRadioButton();
- msgpackButton = new javax.swing.JRadioButton();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(msfgui.MsfguiApp.class).getContext().getResourceMap(OpenConnectionDialog.class);
@@ -255,15 +253,6 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
disableDbButton.setText(resourceMap.getString("disableDbButton.text")); // NOI18N
disableDbButton.setName("disableDbButton"); // NOI18N
- buttonGroup1.add(xmlButton);
- xmlButton.setSelected(true);
- xmlButton.setText(resourceMap.getString("xmlButton.text")); // NOI18N
- xmlButton.setName("xmlButton"); // NOI18N
-
- buttonGroup1.add(msgpackButton);
- msgpackButton.setText(resourceMap.getString("msgpackButton.text")); // NOI18N
- msgpackButton.setName("msgpackButton"); // NOI18N
-
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
@@ -284,21 +273,16 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(disableDbButton)
- .addComponent(usernameField, javax.swing.GroupLayout.DEFAULT_SIZE, 427, Short.MAX_VALUE)
- .addComponent(hostField, javax.swing.GroupLayout.DEFAULT_SIZE, 427, Short.MAX_VALUE)
- .addComponent(passwordField, javax.swing.GroupLayout.DEFAULT_SIZE, 427, Short.MAX_VALUE)
- .addComponent(portField, javax.swing.GroupLayout.DEFAULT_SIZE, 427, Short.MAX_VALUE)
- .addGroup(layout.createSequentialGroup()
- .addComponent(sslBox)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 250, Short.MAX_VALUE)
- .addComponent(xmlButton)
- .addGap(18, 18, 18)
- .addComponent(msgpackButton))))
+ .addComponent(usernameField, javax.swing.GroupLayout.DEFAULT_SIZE, 433, Short.MAX_VALUE)
+ .addComponent(hostField, javax.swing.GroupLayout.DEFAULT_SIZE, 433, Short.MAX_VALUE)
+ .addComponent(passwordField, javax.swing.GroupLayout.DEFAULT_SIZE, 433, Short.MAX_VALUE)
+ .addComponent(portField, javax.swing.GroupLayout.DEFAULT_SIZE, 433, Short.MAX_VALUE)
+ .addComponent(sslBox)))
.addGroup(layout.createSequentialGroup()
.addComponent(startNewButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(pathButton)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 126, Short.MAX_VALUE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 132, Short.MAX_VALUE)
.addComponent(cancelButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(connectButton)))
@@ -326,18 +310,13 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
.addComponent(portField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(portLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(layout.createSequentialGroup()
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
- .addComponent(sslBox, javax.swing.GroupLayout.Alignment.LEADING, 0, 0, Short.MAX_VALUE)
- .addComponent(sslLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
- .addComponent(disableDbLabel)
- .addComponent(disableDbButton, javax.swing.GroupLayout.PREFERRED_SIZE, 18, javax.swing.GroupLayout.PREFERRED_SIZE)))
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
- .addComponent(msgpackButton)
- .addComponent(xmlButton)))
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
+ .addComponent(sslBox, javax.swing.GroupLayout.Alignment.LEADING, 0, 0, Short.MAX_VALUE)
+ .addComponent(sslLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(disableDbLabel)
+ .addComponent(disableDbButton, javax.swing.GroupLayout.PREFERRED_SIZE, 18, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(connectButton, javax.swing.GroupLayout.DEFAULT_SIZE, 37, Short.MAX_VALUE)
@@ -356,11 +335,8 @@ private void connectButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN
String host = hostField.getText();
int port = Integer.parseInt(portField.getText());
boolean ssl = checkCrypto(sslBox.isSelected());
- String type = "xml";
- if(msgpackButton.isSelected())
- type = "msg";
try {
- rpcConn = RpcConnection.getConn(type, username, password, host, port, ssl);
+ rpcConn = RpcConnection.getConn(username, password, host, port, ssl);
} catch (MsfException mex) {
rpcConn = null;
}
@@ -390,10 +366,6 @@ private void portFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIR
private void startNewButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_startNewButtonActionPerformed
//Setup defaults
- if(msgpackButton.isSelected())
- RpcConnection.defaultType = "msg";
- else
- RpcConnection.defaultType = "xml";
RpcConnection.defaultUser = usernameField.getText();
if(passwordField.getPassword().length > 0)
RpcConnection.defaultPass = new String(passwordField.getPassword());
@@ -434,7 +406,6 @@ private void pathButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FI
private javax.swing.JLabel disableDbLabel;
private javax.swing.JTextField hostField;
private javax.swing.JLabel hostLabel;
- private javax.swing.JRadioButton msgpackButton;
private javax.swing.JPasswordField passwordField;
private javax.swing.JLabel passwordLabel;
private javax.swing.JButton pathButton;
@@ -446,6 +417,5 @@ private void pathButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FI
private javax.swing.JLabel titleLabel;
private javax.swing.JTextField usernameField;
private javax.swing.JLabel usernameLabel;
- private javax.swing.JRadioButton xmlButton;
// End of variables declaration//GEN-END:variables
}
View
15 external/source/gui/msfguijava/src/msfgui/PayloadPopup.form
@@ -73,14 +73,14 @@
<EmptySpace type="separate" max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Group type="102" alignment="1" attributes="0">
- <Component id="outputPathField" pref="453" max="32767" attributes="1"/>
+ <Component id="outputPathField" pref="853" max="32767" attributes="1"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="choosePathButton" min="-2" pref="91" max="-2" attributes="0"/>
</Group>
- <Component id="archField" alignment="0" pref="550" max="32767" attributes="1"/>
- <Component id="badcharsField" alignment="0" pref="550" max="32767" attributes="1"/>
- <Component id="timesField" alignment="0" pref="550" max="32767" attributes="1"/>
- <Component id="outputCombo" alignment="0" pref="550" max="32767" attributes="1"/>
+ <Component id="archField" alignment="0" pref="950" max="32767" attributes="1"/>
+ <Component id="badcharsField" alignment="0" pref="950" max="32767" attributes="1"/>
+ <Component id="timesField" alignment="0" pref="950" max="32767" attributes="1"/>
+ <Component id="outputCombo" alignment="0" pref="950" max="32767" attributes="1"/>
<Component id="encoderCombo" alignment="0" min="-2" pref="540" max="-2" attributes="1"/>
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" max="-2" attributes="0">
@@ -89,8 +89,8 @@
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
- <Component id="addCodeButton" pref="97" max="32767" attributes="1"/>
- <Component id="templateButton" alignment="0" pref="97" max="32767" attributes="1"/>
+ <Component id="addCodeButton" pref="497" max="32767" attributes="1"/>
+ <Component id="templateButton" alignment="0" pref="497" max="32767" attributes="1"/>
</Group>
</Group>
</Group>
@@ -112,7 +112,6 @@
<Component id="handleButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="handleConsoleButton" min="-2" max="-2" attributes="0"/>
- <EmptySpace min="-2" pref="121" max="-2" attributes="0"/>
</Group>
<Component id="outputScrollPane" alignment="0" min="-2" pref="691" max="-2" attributes="0"/>
</Group>
View
18 external/source/gui/msfguijava/src/msfgui/PayloadPopup.java
@@ -540,23 +540,14 @@ private void generateButtonActionPerformed(java.awt.event.ActionEvent evt) {//GE
hash.put("Encoder", "generic/none");
Map data = (Map) rpcConn.execute("module.execute", "payload", fullName,hash);
//Basic info
- byte[] buffer;
- String rawHex;
- if(rpcConn.type.equals("msg")){
- buffer = (byte[])data.get("payload");
- }else{
- rawHex = data.get("payload").toString();
- buffer = new byte[rawHex.length() / 2];
- for (int i = 0; i < rawHex.length(); i += 2)
- buffer[i/2] = (byte)Integer.parseInt(rawHex.substring(i, i + 2),16);
- }
+ byte[] buffer = (byte[])data.get("payload");
if(saveButton.isSelected()){ //Encode and output
hash.put("format", outputCombo.getSelectedItem().toString());
if(timesField.getText().length() > 0)
hash.put("ecount", timesField.getText());
if(badcharsField.getText().length() > 0){
- StringBuffer badbinary = new StringBuffer();
+ StringBuilder badbinary = new StringBuilder();
for(String s : badcharsField.getText().split("\\\\x"))
if(s.length() > 0)
badbinary.append((char)Integer.parseInt(s, 16));
@@ -574,10 +565,7 @@ private void generateButtonActionPerformed(java.awt.event.ActionEvent evt) {//GE
if(!outputCombo.getSelectedItem().toString().equals("jar")){ //jars don't get encoded
Map encoded = (Map) rpcConn.execute("module.encode", buffer,
encoderCombo.getSelectedItem().toString(),hash);
- if(rpcConn.type.equals("msg"))
- buffer = (byte[])encoded.get("encoded");
- else
- buffer = Base64.decode(encoded.get("encoded").toString());
+ buffer = (byte[])encoded.get("encoded");
}
FileOutputStream fout = new FileOutputStream(outputPathField.getText());
fout.write(buffer);
View
19 external/source/gui/msfguijava/src/msfgui/RpcConnection.java
@@ -27,10 +27,9 @@
* @author scriptjunkie
*/
public abstract class RpcConnection {
- public String type = "msg";
protected String rpcToken;
protected Map callCache = new HashMap();
- public static String defaultUser = "msf",defaultPass = null, defaultHost = "127.0.0.1", defaultType = "msg";
+ public static String defaultUser = "msf",defaultPass = null, defaultHost = "127.0.0.1";
public static int defaultPort = 55553;
public static boolean defaultSsl = false;
public static boolean disableDb = false;
@@ -56,12 +55,8 @@
* @return A new RPC connection
* @throws MsfException
*/
- public static RpcConnection getConn(String type, String username, char[] password, String host, int port, boolean ssl) throws MsfException{
- RpcConnection conn;
- if(type.toLowerCase().equals("xml"))
- conn = new XmlRpc();
- else
- conn = new MsgRpc();
+ public static RpcConnection getConn(String username, char[] password, String host, int port, boolean ssl) throws MsfException{
+ RpcConnection conn = new MsgRpc();
conn.setup(username, password, host, port, ssl);
return conn;
}
@@ -109,7 +104,6 @@ public void setup(String username, char[] password, String host, int port, boole
root.put("port", port);
root.put("ssl", ssl);
root.put("disableDb", disableDb);
- root.put("type", type);
MsfguiApp.savePreferences();
}
@@ -169,7 +163,6 @@ public void checkServerTrusted(java.security.cert.X509Certificate[] certs, Strin
public String toString(){
return "RPC connection "
- + "\ntype: "+type
+ "\nusername: "+username
+ "\npassword: " + password
+ "\nhost: " + host
@@ -265,10 +258,8 @@ protected RpcConnection doInBackground() throws Exception {
// Don't fork cause we'll check if it dies
String rpcType = "Basic";
- if(defaultType.toLowerCase().equals("msg"))
- rpcType = "Msg";
java.util.List args = new java.util.ArrayList(java.util.Arrays.asList(new String[]{
- "msfrpcd","-f","-P",defaultPass,"-t",rpcType,"-U",defaultUser,"-a","127.0.0.1"}));
+ "msfrpcd","-f","-P",defaultPass,"-t","Msg","-U",defaultUser,"-a","127.0.0.1"}));
if(!defaultSsl)
args.add("-S");
if(disableDb)
@@ -296,7 +287,7 @@ protected RpcConnection doInBackground() throws Exception {
} //Nope. We're good.
try {
- myRpcConn = RpcConnection.getConn(defaultType, defaultUser, defaultPass.toCharArray(), "127.0.0.1", defaultPort, defaultSsl);
+ myRpcConn = RpcConnection.getConn(defaultUser, defaultPass.toCharArray(), "127.0.0.1", defaultPort, defaultSsl);
connected = true;
break;
} catch (MsfException mex) {
View
1  external/source/gui/msfguijava/src/msfgui/XmlRpc.java
@@ -33,7 +33,6 @@
*/
public XmlRpc(){
super();
- type = "XML";
}
/** Creates an XMLRPC call from the given method name and parameters and sends it */
protected void writeCall(String methname, Object[] params) throws Exception{
View
2  external/source/gui/msfguijava/src/msfgui/resources/OpenConnectionDialog.properties
@@ -17,5 +17,3 @@ sslBox.text=
sslLabel.text=SSL
disableDbButton.text=
disableDbLabel.text=Disable DB
-xmlButton.text=XML
-msgpackButton.text=MsgPack
View
14 lib/bit-struct.rb
@@ -0,0 +1,14 @@
+# A Convenience to load all field classes and yaml handling.
+# XXX: Pretty certian this monkeypatch isn't required in Metasploit.
+
+if "a"[0].kind_of? Fixnum
+ unless Fixnum.methods.include? :ord
+ class Fixnum
+ def ord; self; end
+ end
+ end
+end
+
+require 'bit-struct/bit-struct'
+require 'bit-struct/fields'
+require 'bit-struct/yaml'
View
187 lib/bit-struct/README
@@ -0,0 +1,187 @@
+= BitStruct
+
+Class for packed binary data stored in ruby Strings. BitStruct accessors, generated from user declared fields, use pack/unpack to treat substrings as fields with a specified portable format.
+
+Field types include:
+
+* signed and unsigned integer (1..16 bits, or 24, 32, 40, 48... bits)
+
+* numeric fields (signed, unsigned, float) can be designated as any of the following endians: little, big, native, network (default)
+
+* fixed point, with arbitrary scale factor
+
+* fixed length character array
+
+* null-terminated character array for printable text
+
+* octets (hex and decimal representation options; useful for IP and MAC addrs)
+
+* float
+
+* nested BitStruct
+
+* vectors of embedded BitStructs
+
+* free-form "rest" field (e.g., for the variable-size payload of a packet)
+
+Field options (specifiable as :foo => val or "foo" => val) include:
+
+* *display_name*: used in BitStruct#inspect_detailed and BitStruct#describe outputs.
+
+* *default*: default field value
+
+* *format*: alternate format string for inspect
+
+* *endian*: for byte ordering of numeric fields (unsigned, signed, float): little, big, native, network (default)
+
+* *fixed*: float stored as fixed-point integer, with specified scale factor
+
+
+== Installation
+
+For .gem:
+
+ gem install bit-struct
+
+For .tgz, unpack and then:
+
+ ruby install.rb config
+ ruby install.rb setup
+ ruby install.rb install
+
+== Uses
+
+BitStruct is useful for defining packets used in network protocols. This is especially useful for raw IP--see examples/ping-recv.rb. All multibyte numeric fields are stored by default in network order.
+
+BitStruct is most efficient when your data is primarily treated as a binary string, and only secondarily treated as a data structure. (For instance, you are routing packets from one socket to another, possibly looking at one or two fields as it passes through or munging some headers.) If accessor operations are a bottleneck, a better approach is to define a class that wraps an array and uses pack/unpack when the object needs to behave like a binary string.
+
+== Features
+
+* Extensible with user-defined field classes.
+
+* Fields are fully introspectable and can be defined programmatically.
+
+* BitStruct.describe prints out documentation of all the fields of a BitStruct subclass, based on declarations. This is useful for communicating with developers who are not using ruby, but need to talk the same protocols. See Example, below.
+
+* Fields are inherited by subclasses. (The free-form "rest" field does not inherit, because it usually represents a payload whose structure is defined in subclasses using the fixed-size fields.)
+
+* BitStruct#inspect and BitStruct#inspect_detailed can be used for prettified display of contents. (More generally, BitStruct#inspect takes some options that control formatting and detail level.) See Example, below.
+
+* BitStruct inherits from String, so all the usual methods are available, and string-sharing (copy-on-write) is in effect.
+
+* Easy access to a "prototype" instance of each BitStruct subclass, from which all instances of that subclass are initialized as a copy (in the absence of other initialization parameters, such as a hash, a string, or a block). See BitStruct.initial_value, and BitStruct#initialize. See Example, below.
+
+* Easy conversion to and from hashes, using BitStruct#to_h and BitStruct.new.
+
+* BitStructs can persist using Marshal (a BitStruct is after all just a string) or using YAML (with human readable representation of the fields).
+
+* Includes tests, examples, and rdoc API documentation.
+
+== Limitations
+
+* Fields that are not aligned on byte boundaries may cross no more than two bytes boundaries. (See examples/byte-bdy.rb.)
+
+* No variable length fields (except the #rest field).
+
+== Future plans
+
+* Currently, the library is written in pure ruby. The implementation uses Array#pack and String#unpack calls, as well as shifting and masking in pure ruby. Future versions will optionally generate a customized C extension for better efficiency.
+
+* A debug mode in which a class identifier is prepended to every BitStruct, so that protocol errors can be detected. (This feature has been implemented in an app that uses BitStruct, but needs to be refactored into the BitStruct library itself.)
+
+* Remove field size and alignment limitations.
+
+== Example
+
+An IP packet can be defined and used like this:
+
+ require 'bit-struct'
+
+ class IP < BitStruct
+ unsigned :ip_v, 4, "Version"
+ unsigned :ip_hl, 4, "Header length"
+ unsigned :ip_tos, 8, "TOS"
+ unsigned :ip_len, 16, "Length"
+ unsigned :ip_id, 16, "ID"
+ unsigned :ip_off, 16, "Frag offset"
+ unsigned :ip_ttl, 8, "TTL"
+ unsigned :ip_p, 8, "Protocol"
+ unsigned :ip_sum, 16, "Checksum"
+ octets :ip_src, 32, "Source addr"
+ octets :ip_dst, 32, "Dest addr"
+ rest :body, "Body of message"
+
+ note " rest is application defined message body"
+
+ initial_value.ip_v = 4
+ initial_value.ip_hl = 5
+ end
+
+ ip = IP.new
+ ip.ip_tos = 0
+ ip.ip_len = 0
+ ip.ip_id = 0
+ ip.ip_off = 0
+ ip.ip_ttl = 255
+ ip.ip_p = 255
+ ip.ip_sum = 0
+ ip.ip_src = "192.168.1.4"
+ ip.ip_dst = "192.168.1.255"
+ ip.body = "This is the payload text."
+ ip.ip_len = ip.length
+
+ puts ip.inspect
+ puts "-"*50
+ puts ip.inspect_detailed
+ puts "-"*50
+ puts IP.describe
+
+(Note that you can also construct an IP packet by passing a string to new, or by passing a hash of <tt>field,value</tt> pairs, or by providing a block that is yielded the new BitStruct.)
+
+The output of this fragment is:
+
+ #<IP ip_v=4, ip_hl=5, ip_tos=0, ip_len=45, ip_id=0, ip_off=0, ip_ttl=255, ip_p=255, ip_sum=0, ip_src="192.168.1.4", ip_dst="192.168.1.255", body="This is the payload text.">
+ --------------------------------------------------
+ IP:
+ Version = 4
+ Header length = 5
+ TOS = 0
+ Length = 45
+ ID = 0
+ Frag offset = 0
+ TTL = 255
+ Protocol = 255
+ Checksum = 0
+ Source addr = "192.168.1.4"
+ Dest addr = "192.168.1.255"
+ Body of message = "This is the payload text."
+ --------------------------------------------------
+
+ Description of IP Packet:
+ byte: type name [size] description
+ ----------------------------------------------------------------------
+ @0: unsigned ip_v [ 4b] Version
+ @0: unsigned ip_hl [ 4b] Header length
+ @1: unsigned ip_tos [ 8b] TOS
+ @2: unsigned ip_len [ 16b] Length
+ @4: unsigned ip_id [ 16b] ID
+ @6: unsigned ip_off [ 16b] Frag offset
+ @8: unsigned ip_ttl [ 8b] TTL
+ @9: unsigned ip_p [ 8b] Protocol
+ @10: unsigned ip_sum [ 16b] Checksum
+ @12: octets ip_src [ 32b] Source addr
+ @16: octets ip_dst [ 32b] Dest addr
+ rest is application defined message body
+
+== Web site
+
+The current version of this software can be found at http://redshift.sourceforge.net/bit-struct.
+
+== License
+
+This software is distributed under the Ruby license. See http://www.ruby-lang.org.
+
+== Author
+
+Joel VanderWerf, mailto:vjoel@users.sourceforge.net
+Copyright (c) 2005-2009, Joel VanderWerf.
View
574 lib/bit-struct/bit-struct.rb
@@ -0,0 +1,574 @@
+# Class for packed binary data, with defined bitfields and accessors for them.
+# See {intro.txt}[link:../doc/files/intro_txt.html] for an overview.
+#
+# Data after the end of the defined fields is accessible using the +rest+
+# declaration. See examples/ip.rb. Nested fields can be declared using +nest+.
+# See examples/nest.rb.
+#
+# Note that all string methods are still available: length, grep, etc.
+# The String#replace method is useful.
+#
+class BitStruct < String
+ VERSION = "0.13.6"
+
+ class Field
+ # Offset of field in bits.
+ attr_reader :offset
+
+ # Length of field in bits.
+ attr_reader :length
+ alias size length
+
+ # Name of field (used for its accessors).
+ attr_reader :name
+
+ # Options, such as :default (varies for each field subclass).
+ # In general, options can be provided as strings or as symbols.
+ attr_reader :options
+
+ # Display name of field (used for printing).
+ attr_reader :display_name
+
+ # Default value.
+ attr_reader :default
+
+ # Format for printed value of field.
+ attr_reader :format
+
+ # Subclasses can override this to define a default for all fields of this
+ # class, not just the one currently being added to a BitStruct class, a
+ # "default default" if you will. The global default, if #default returns
+ # nil, is to fill the field with zero. Most field classes just let this
+ # default stand. The default can be overridden per-field when a BitStruct
+ # class is defined.
+ def self.default; nil; end
+
+ # Used in describe.
+ def self.class_name
+ @class_name ||= name[/\w+$/]
+ end
+
+ # Used in describe. Can be overridden per-subclass, as in NestedField.
+ def class_name
+ self.class.class_name
+ end
+
+ # Yield the description of this field, as an array of 5 strings: byte
+ # offset, type, name, size, and description. The opts hash may have:
+ #
+ # :expand :: if the value is true, expand complex fields
+ #
+ # (Subclass implementations may yield more than once for complex fields.)
+ #
+ def describe opts
+ bits = size
+ if bits > 32 and bits % 8 == 0
+ len_str = "%dB" % (bits/8)
+ else
+ len_str = "%db" % bits
+ end
+
+ byte_offset = offset / 8 + (opts[:byte_offset] || 0)
+
+ yield ["@%d" % byte_offset, class_name, name, len_str, display_name]
+ end
+
+ # Options are _display_name_, _default_, and _format_ (subclasses of Field
+ # may add other options).
+ def initialize(offset, length, name, opts = {})
+ @offset, @length, @name, @options =
+ offset, length, name, opts
+
+ @display_name = opts[:display_name] || opts["display_name"]
+ @default = opts[:default] || opts["default"] || self.class.default
+ @format = opts[:format] || opts["format"]
+ end
+
+ # Inspect the value of this field in the specified _obj_.
+ def inspect_in_object(obj, opts)
+ val = obj.send(name)
+ str =
+ begin
+ val.inspect(opts)
+ rescue ArgumentError # assume: "wrong number of arguments (1 for 0)"
+ val.inspect
+ end
+ (f=@format) ? (f % str) : str
+ end
+
+ # Normally, all fields show up in inspect, but some, such as padding,
+ # should not.
+ def inspectable?; true; end
+ end
+
+ NULL_FIELD = Field.new(0, 0, :null, :display_name => "null field")
+
+ # Raised when a field is added after an instance has been created. Fields
+ # cannot be added after this point.
+ class ClosedClassError < StandardError; end
+
+ # Raised if the chosen field name is not allowed, either because another
+ # field by that name exists, or because a method by that name exists.
+ class FieldNameError < StandardError; end
+
+ @default_options = {}
+
+ @initial_value = nil
+ @closed = nil
+ @rest_field = nil
+ @note = nil
+
+ class << self
+ def inherited cl
+ cl.instance_eval do
+ @initial_value = nil
+ @closed = nil
+ @rest_field = nil
+ @note = nil
+ end
+ end
+
+ # ------------------------
+ # :section: field access methods
+ #
+ # For introspection and metaprogramming.
+ #
+ # ------------------------
+
+ # Return the list of fields for this class.
+ def fields
+ @fields ||= self == BitStruct ? [] : superclass.fields.dup
+ end
+
+ # Return the list of fields defined by this class, not inherited
+ # from the superclass.
+ def own_fields
+ @own_fields ||= []
+ end
+
+ # Add a field to the BitStruct (usually, this is only used internally).
+ def add_field(name, length, opts = {})
+ round_byte_length ## just to make sure this has been calculated
+ ## before adding anything
+
+ name = name.to_sym
+
+ if @closed
+ raise ClosedClassError, "Cannot add field #{name}: " +
+ "The definition of the #{self.inspect} BitStruct class is closed."
+ end
+
+ if fields.find {|f|f.name == name}
+ raise FieldNameError, "Field #{name} is already defined as a field."
+ end
+
+ if instance_methods(true).find {|m| m == name}
+ if opts[:allow_method_conflict] || opts["allow_method_conflict"]
+ warn "Field #{name} is already defined as a method."
+ else
+ raise FieldNameError,"Field #{name} is already defined as a method."
+ end
+ end
+
+ field_class = opts[:field_class]
+
+ prev = fields[-1] || NULL_FIELD
+ offset = prev.offset + prev.length
+ field = field_class.new(offset, length, name, opts)
+ field.add_accessors_to(self)
+ fields << field
+ own_fields << field
+ @bit_length += field.length
+ @round_byte_length = (bit_length/8.0).ceil
+
+ if @initial_value
+ diff = @round_byte_length - @initial_value.length
+ if diff > 0
+ @initial_value << "\0" * diff
+ end
+ end
+
+ field
+ end
+
+ def parse_options(ary, default_name, default_field_class) # :nodoc:
+ opts = ary.grep(Hash).first || {}
+ opts = default_options.merge(opts)
+
+ opts[:display_name] = ary.grep(String).first || default_name
+ opts[:field_class] = ary.grep(Class).first || default_field_class
+
+ opts
+ end
+
+ # Get or set the hash of default options for the class, which apply to all
+ # fields. Changes take effect immediately, so can be used alternatingly with
+ # blocks of field declarations. If +h+ is provided, update the default
+ # options with that hash. Default options are inherited.
+ #
+ # This is especially useful with the <tt>:endian => val</tt> option.
+ def default_options h = nil
+ @default_options ||= superclass.default_options.dup
+ if h
+ @default_options.merge! h
+ end
+ @default_options
+ end
+
+ # Length, in bits, of this object.
+ def bit_length
+ @bit_length ||= fields.inject(0) {|a, f| a + f.length}
+ end
+
+ # Length, in bytes (rounded up), of this object.
+ def round_byte_length
+ @round_byte_length ||= (bit_length/8.0).ceil
+ end
+
+ def closed! # :nodoc:
+ @closed = true
+ end
+
+ def field_by_name name
+ @field_by_name ||= {}
+ field = @field_by_name[name]
+ unless field
+ field = fields.find {|f| f.name == name}
+ @field_by_name[name] = field if field
+ end
+ field
+ end
+ end
+
+ # Return the list of fields for this class.
+ def fields
+ self.class.fields
+ end
+
+ # Return the rest field for this class.
+ def rest_field
+ self.class.rest_field
+ end
+
+ # Return the field with the given name.
+ def field_by_name name
+ self.class.field_by_name name
+ end
+
+ # ------------------------
+ # :section: metadata inspection methods
+ #
+ # Methods to textually describe the format of a BitStruct subclass.
+ #
+ # ------------------------
+
+ class << self
+ # Default format for describe. Fields are byte, type, name, size,
+ # and description.
+ DESCRIBE_FORMAT = "%8s: %-12s %-14s[%4s] %s"
+
+ # Can be overridden to use a different format.
+ def describe_format
+ DESCRIBE_FORMAT
+ end
+
+ # Textually describe the fields of this class of BitStructs.
+ # Returns a printable table (array of line strings), based on +fmt+,
+ # which defaults to #describe_format, which defaults to +DESCRIBE_FORMAT+.
+ def describe(fmt = nil, opts = {})
+ if fmt.kind_of? Hash
+ opts = fmt; fmt = nil
+ end
+
+ if block_given?
+ fields.each do |field|
+ field.describe(opts) do |desc|
+ yield desc
+ end
+ end
+ nil
+
+ else
+ fmt ||= describe_format
+
+ result = []
+
+ unless opts[:omit_header]
+ result << fmt % ["byte", "type", "name", "size", "description"]
+ result << "-"*70
+ end
+
+ fields.each do |field|
+ field.describe(opts) do |desc|
+ result << fmt % desc
+ end
+ end
+
+ unless opts[:omit_footer]
+ result << @note if @note
+ end
+
+ result
+ end
+ end
+
+ # Subclasses can use this to append a string (or several) to the #describe
+ # output. Notes are not cumulative with inheritance. When used with no
+ # arguments simply returns the note string
+ def note(*str)
+ @note = str unless str.empty?
+ @note
+ end
+ end
+
+ # ------------------------
+ # :section: initialization and conversion methods
+ #
+ # ------------------------
+
+ # Initialize the string with the given string or bitstruct, or with a hash of
+ # field=>value pairs, or with the defaults for the BitStruct subclass, or
+ # with an IO or other object with a #read method. Fields can be strings or
+ # symbols. Finally, if a block is given, yield the instance for modification
+ # using accessors.
+ def initialize(value = nil) # :yields: instance
+ self << self.class.initial_value
+
+ case value
+ when Hash
+ value.each do |k, v|
+ send "#{k}=", v
+ end
+
+ when nil
+
+ else
+ if value.respond_to?(:read)
+ value = value.read(self.class.round_byte_length)
+ end
+
+ self[0, value.length] = value
+ end
+
+ self.class.closed!
+ yield self if block_given?
+ end
+
+ DEFAULT_TO_H_OPTS = {
+ :convert_keys => :to_sym,
+ :include_rest => true
+ }
+
+ # Returns a hash of {name=>value,...} for each field. By default, include
+ # the rest field.
+ # Keys are symbols derived from field names using +to_sym+, unless
+ # <tt>opts[:convert_keys]<\tt> is set to some other method name.
+ def to_h(opts = DEFAULT_TO_H_OPTS)
+ converter = opts[:convert_keys] || :to_sym
+
+ fields_for_to_h = fields
+ if opts[:include_rest] and (rest_field = self.class.rest_field)
+ fields_for_to_h += [rest_field]
+ end
+
+ fields_for_to_h.inject({}) do |h,f|
+ h[f.name.send(converter)] = send(f.name)
+ h
+ end
+ end
+
+ # Returns an array of values of the fields of the BitStruct. By default,
+ # include the rest field.
+ def to_a(include_rest = true)
+ ary =
+ fields.map do |f|
+ send(f.name)
+ end
+
+ if include_rest and (rest_field = self.class.rest_field)
+ ary << send(rest_field.name)
+ end
+ ary
+ end
+
+ ## temporary hack for 1.9
+ if "a"[0].kind_of? String
+ def [](*args)
+ if args.size == 1 and args[0].kind_of?(Fixnum)
+ super.ord
+ else
+ super
+ end
+ end
+
+ def []=(*args)
+ if args.size == 2 and (i=args[0]).kind_of?(Fixnum)
+ super(i, args[1].chr)
+ else
+ super
+ end
+ end
+ end
+
+ class << self
+ # The unique "prototype" object from which new instances are copied.
+ # The fields of this instance can be modified in the class definition
+ # to set default values for the fields in that class. (Otherwise, defaults
+ # defined by the fields themselves are used.) A copy of this object is
+ # inherited in subclasses, which they may override using defaults and
+ # by writing to the initial_value object itself.
+ #
+ # If called with a block, yield the initial value object before returning
+ # it. Useful for customization within a class definition.
+ #
+ def initial_value # :yields: the initial value
+ unless @initial_value
+ iv = defined?(superclass.initial_value) ?
+ superclass.initial_value.dup : ""
+ if iv.length < round_byte_length
+ iv << "\0" * (round_byte_length - iv.length)
+ end
+
+ @initial_value = "" # Serves as initval while the real initval is inited
+ @initial_value = new(iv)
+ @closed = false # only creating the first _real_ instance closes.
+
+ fields.each do |field|
+ @initial_value.send("#{field.name}=", field.default) if field.default
+ end
+ end
+ yield @initial_value if block_given?
+ @initial_value
+ end
+
+ # Take +data+ (a string or BitStruct) and parse it into instances of
+ # the +classes+, returning them in an array. The classes can be given
+ # as an array or a separate arguments. (For parsing a string into a _single_
+ # BitStruct instance, just use the #new method with the string as an arg.)
+ def parse(data, *classes)
+ classes.flatten.map do |c|
+ c.new(data.slice!(0...c.round_byte_length))
+ end
+ end
+
+ # Join the given structs (array or multiple args) as a string.
+ # Actually, the inherited String#+ instance method is the same, as is using
+ # Array#join.
+ def join(*structs)
+ structs.flatten.map {|struct| struct.to_s}.join("")
+ end
+ end
+
+ # ------------------------
+ # :section: inspection methods
+ #
+ # ------------------------
+
+ DEFAULT_INSPECT_OPTS = {
+ :format => "#<%s %s>",
+ :field_format => "%s=%s",
+ :separator => ", ",
+ :field_name_meth => :name,
+ :include_rest => true,
+ :brackets => ["[", "]"],
+ :include_class => true,
+ :simple_format => "<%s>"
+ }
+
+ DETAILED_INSPECT_OPTS = {
+ :format => "%s:\n%s",
+ :field_format => "%30s = %s",
+ :separator => "\n",
+ :field_name_meth => :display_name,
+ :include_rest => true,
+ :brackets => [nil, "\n"],
+ :include_class => true,
+ :simple_format => "\n%s"
+ }
+
+ # A standard inspect method which does not add newlines.
+ def inspect(opts = DEFAULT_INSPECT_OPTS)
+ field_format = opts[:field_format]
+ field_name_meth = opts[:field_name_meth]
+
+ fields_for_inspect = fields.select {|field| field.inspectable?}
+ if opts[:include_rest] and (rest_field = self.class.rest_field)
+ fields_for_inspect << rest_field
+ end
+
+ ary = fields_for_inspect.map do |field|
+ field_format %
+ [field.send(field_name_meth),
+ field.inspect_in_object(self, opts)]
+ end
+
+ body = ary.join(opts[:separator])
+
+ if opts[:include_class]
+ opts[:format] % [self.class, body]
+ else
+ opts[:simple_format] % body
+ end
+ end
+
+ # A more visually appealing inspect method that puts each field/value on
+ # a separate line. Very useful when output is scrolling by on a screen.
+ #
+ # (This is actually a convenience method to call #inspect with the
+ # DETAILED_INSPECT_OPTS opts.)
+ def inspect_detailed
+ inspect(DETAILED_INSPECT_OPTS)
+ end
+
+ # ------------------------
+ # :section: field declaration methods
+ #
+ # ------------------------
+
+ # Define accessors for a variable length substring from the end of
+ # the defined fields to the end of the BitStruct. The _rest_ may behave as
+ # a String or as some other String or BitStruct subclass.
+ #
+ # This does not add a field, which is useful because a superclass can have
+ # a rest method which accesses subclass data. In particular, #rest does
+ # not affect the #round_byte_length class method. Of course, any data
+ # in rest does add to the #length of the BitStruct, calculated as a string.
+ # Also, _rest_ is not inherited.
+ #
+ # The +ary+ argument(s) work as follows:
+ #
+ # If a class is provided, use it for the Field class (String by default).
+ # If a string is provided, use it for the display_name (+name+ by default).
+ # If a hash is provided, use it for options.
+ #
+ # *Warning*: the rest reader method returns a copy of the field, so
+ # accessors on that returned value do not affect the original rest field.
+ #
+ def self.rest(name, *ary)
+ if @rest_field
+ raise ArgumentError, "Duplicate rest field: #{name.inspect}."
+ end
+
+ opts = parse_options(ary, name, String)
+ offset = round_byte_length
+ byte_range = offset..-1
+ class_eval do
+ field_class = opts[:field_class]
+ define_method name do ||
+ field_class.new(self[byte_range])
+ end
+
+ define_method "#{name}=" do |val|
+ self[byte_range] = val
+ end
+
+ @rest_field = Field.new(offset, -1, name, {
+ :display_name => opts[:display_name],
+ :rest_class => field_class
+ })
+ end
+ end
+
+ # Not included with the other fields, but accessible separately.
+ def self.rest_field; @rest_field; end
+end
View
48 lib/bit-struct/char-field.rb
@@ -0,0 +1,48 @@
+class BitStruct
+ # Class for fixed length binary strings of characters.
+ # Declared with BitStruct.char.
+ class CharField < Field
+ #def self.default
+ # don't define this, since it must specify N nulls and we don't know N
+ #end
+
+ # Used in describe.
+ def self.class_name
+ @class_name ||= "char"
+ end
+
+ def add_accessors_to(cl, attr = name) # :nodoc:
+ unless offset % 8 == 0
+ raise ArgumentError,
+ "Bad offset, #{offset}, for #{self.class} #{name}." +
+ " Must be multiple of 8."
+ end
+
+ unless length % 8 == 0
+ raise ArgumentError,
+ "Bad length, #{length}, for #{self.class} #{name}." +
+ " Must be multiple of 8."
+ end
+
+ offset_byte = offset / 8
+ length_byte = length / 8
+ last_byte = offset_byte + length_byte - 1
+ byte_range = offset_byte..last_byte
+ val_byte_range = 0..length_byte-1
+
+ cl.class_eval do
+ define_method attr do ||
+ self[byte_range].to_s
+ end
+
+ define_method "#{attr}=" do |val|
+ val = val.to_s
+ if val.length < length_byte
+ val += "\0" * (length_byte - val.length)
+ end
+ self[byte_range] = val[val_byte_range]
+ end
+ end
+ end
+ end
+end
View
300 lib/bit-struct/fields.rb
@@ -0,0 +1,300 @@
+class BitStruct
+ class << self
+ # Define a char string field in the current subclass of BitStruct,
+ # with the given _name_ and _length_ (in bits). Trailing nulls _are_
+ # considered part of the string.
+ #
+ # If a class is provided, use it for the Field class.
+ # If a string is provided, use it for the display_name.
+ # If a hash is provided, use it for options.
+ #
+ # Note that the accessors have COPY semantics, not reference.
+ #
+ def char(name, length, *rest)
+ opts = parse_options(rest, name, CharField)
+ add_field(name, length, opts)
+ end
+ alias string char
+ BitStruct.autoload :CharField, "bit-struct/char-field"
+
+ # Define a floating point field in the current subclass of BitStruct,
+ # with the given _name_ and _length_ (in bits).
+ #
+ # If a class is provided, use it for the Field class.
+ # If a string is provided, use it for the display_name.
+ # If a hash is provided, use it for options.
+ #
+ # The <tt>:endian => :native</tt> option overrides the default of
+ # <tt>:network</tt> byte ordering, in favor of native byte ordering. Also
+ # permitted are <tt>:big</tt> (same as <tt>:network</tt>) and
+ # <tt>:little</tt>.
+ #
+ def float name, length, *rest
+ opts = parse_options(rest, name, FloatField)
+ add_field(name, length, opts)
+ end
+ BitStruct.autoload :FloatField, "bit-struct/float-field"
+
+ # Define an octet string field in the current subclass of BitStruct,
+ # with the given _name_ and _length_ (in bits). Trailing nulls are
+ # not considered part of the string. The field is accessed using
+ # period-separated hex digits.
+ #
+ # If a class is provided, use it for the Field class.
+ # If a string is provided, use it for the display_name.
+ # If a hash is provided, use it for options.
+ #
+ def hex_octets(name, length, *rest)
+ opts = parse_options(rest, name, HexOctetField)
+ add_field(name, length, opts)
+ end
+ BitStruct.autoload :HexOctetField, "bit-struct/hex-octet-field"
+
+ # Define a nested field in the current subclass of BitStruct,
+ # with the given _name_ and _nested_class_. Length is determined from
+ # _nested_class_.
+ #
+ # If a class is provided, use it for the Field class (i.e. <=NestedField).
+ # If a string is provided, use it for the display_name.
+ # If a hash is provided, use it for options.
+ #
+ # For example:
+ #
+ # class Sub < BitStruct
+ # unsigned :x, 8
+ # end
+ #
+ # class A < BitStruct
+ # nest :n, Sub
+ # end
+ #
+ # a = A.new
+ #
+ # p a # ==> #<A n=#<Sub x=0>>
+ #
+ # If a block is given, use it to define the nested fields. For example, the
+ # following is equivalent to the above example:
+ #
+ # class A < BitStruct
+ # nest :n do
+ # unsigned :x, 8
+ # end
+ # end
+ #
+ # WARNING: the accessors have COPY semantics, not reference. When you call a
+ # reader method to get the nested structure, you get a *copy* of that data.
+ # Expressed in terms of the examples above:
+ #
+ # # This fails to set x in a.
+ # a.n.x = 3
+ # p a # ==> #<A n=#<Sub x=0>>
+ #
+ # # This works
+ # n = a.n
+ # n.x = 3
+ # a.n = n
+ # p a # ==> #<A n=#<Sub x=3>>
+ #
+ def nest(name, *rest, &block)
+ nested_class = rest.grep(Class).find {|cl| cl <= BitStruct}
+ rest.delete nested_class
+ opts = parse_options(rest, name, NestedField)
+ nested_class = opts[:nested_class] ||= nested_class
+
+ unless (block and not nested_class) or (nested_class and not block)
+ raise ArgumentError,
+ "nested field must have either a nested_class option or a block," +
+ " but not both"
+ end
+
+ unless nested_class
+ nested_class = Class.new(BitStruct)
+ nested_class.class_eval(&block)
+ end
+
+ opts[:default] ||= nested_class.initial_value.dup
+ opts[:nested_class] = nested_class
+ field = add_field(name, nested_class.bit_length, opts)
+ field
+ end
+ alias struct nest
+ BitStruct.autoload :NestedField, "bit-struct/nested-field"
+
+ # Define an octet string field in the current subclass of BitStruct,
+ # with the given _name_ and _length_ (in bits). Trailing nulls are
+ # not considered part of the string. The field is accessed using
+ # period-separated decimal digits.
+ #
+ # If a class is provided, use it for the Field class.
+ # If a string is provided, use it for the display_name.
+ # If a hash is provided, use it for options.
+ #
+ def octets(name, length, *rest)
+ opts = parse_options(rest, name, OctetField)
+ add_field(name, length, opts)
+ end
+ BitStruct.autoload :OctetField, "bit-struct/octet-field"
+
+ # Define a padding field in the current subclass of BitStruct,
+ # with the given _name_ and _length_ (in bits).
+ #
+ # If a class is provided, use it for the Field class.
+ # If a string is provided, use it for the display_name.
+ # If a hash is provided, use it for options.
+ #
+ def pad(name, length, *rest)
+ opts = parse_options(rest, name, PadField)
+ add_field(name, length, opts)
+ end
+ alias padding pad
+ BitStruct.autoload :PadField, "bit-struct/pad-field"
+
+ # Define a signed integer field in the current subclass of BitStruct,
+ # with the given _name_ and _length_ (in bits).
+ #
+ # If a class is provided, use it for the Field class.
+ # If a string is provided, use it for the display_name.
+ # If a hash is provided, use it for options.
+ #
+ # SignedField adds the <tt>:fixed => divisor</tt> option, which specifies
+ # that the internally stored value is interpreted as a fixed point real
+ # number with the specified +divisor+.
+ #
+ # The <tt>:endian => :native</tt> option overrides the default of
+ # <tt>:network</tt> byte ordering, in favor of native byte ordering. Also
+ # permitted are <tt>:big</tt> (same as <tt>:network</tt>) and
+ # <tt>:little</tt>.
+ #
+ def signed name, length, *rest
+ opts = parse_options(rest, name, SignedField)
+ add_field(name, length, opts)
+ end
+ BitStruct.autoload :SignedField, "bit-struct/signed-field"
+
+ # Define a printable text string field in the current subclass of BitStruct,
+ # with the given _name_ and _length_ (in bits). Trailing nulls are
+ # _not_ considered part of the string.
+ #
+ # If a class is provided, use it for the Field class.
+ # If a string is provided, use it for the display_name.
+ # If a hash is provided, use it for options.
+ #
+ # Note that the accessors have COPY semantics, not reference.
+ #
+ def text(name, length, *rest)
+ opts = parse_options(rest, name, TextField)
+ add_field(name, length, opts)
+ end
+ BitStruct.autoload :TextField, "bit-struct/text-field"
+
+ # Define a unsigned integer field in the current subclass of BitStruct,
+ # with the given _name_ and _length_ (in bits).
+ #
+ # If a class is provided, use it for the Field class.
+ # If a string is provided, use it for the display_name.
+ # If a hash is provided, use it for options.
+ #
+ # UnsignedField adds the <tt>:fixed => divisor</tt> option, which specifies
+ # that the internally stored value is interpreted as a fixed point real
+ # number with the specified +divisor+.
+ #
+ # The <tt>:endian => :native</tt> option overrides the default of
+ # <tt>:network</tt> byte ordering, in favor of native byte ordering. Also
+ # permitted are <tt>:big</tt> (same as <tt>:network</tt>) and
+ # <tt>:little</tt>.
+ #
+ def unsigned name, length, *rest
+ opts = parse_options(rest, name, UnsignedField)
+ add_field(name, length, opts)
+ end
+ BitStruct.autoload :UnsignedField, "bit-struct/unsigned-field"
+
+ # Define a vector field in the current subclass of BitStruct,
+ # with the given _name_.
+ #
+ # If a class is provided, use it for the Vector class, otherwise
+ # the block must define the entry fields. The two forms looks like
+ # this:
+ #
+ # class Vec < BitStruct::Vector
+ # # these declarations apply to *each* entry in the vector:
+ # unsigned :x, 16
+ # signed :y, 32
+ # end
+ #
+ # class Packet < BitStruct
+ # # Using the Vec class defined above
+ # vector :v, Vec, "a vector", :length => 5
+ #
+ # # equivalently, using an anonymous subclass of BitStruct::Vector
+ # vector :v2, "a vector", :length => 5 do
+ # unsigned :x, 16
+ # signed :y, 32
+ # end
+ # end
+ #
+ # If a string is provided, use it for the display_name.
+ # If a hash is provided, use it for options.
+ # If a number is provided, use it for length (equivalent to using the
+ # :length option).
+ #
+ # WARNING: the accessors have COPY semantics, not reference. When you call a
+ # reader method to get the vector structure, you get a *copy* of that data.
+ #
+ # For example, to modify the numeric fields in a Packet as defined above:
+ #
+ # pkt = Packet.new
+ # vec = pkt.v
+ # entry = vec[2]
+ # entry.x = 123
+ # entry.y = -456
+ # vec[2] = entry
+ # pkt.v = vec
+ #
+ def vector(name, *rest, &block)
+ opts = parse_options(rest, name, nil)
+ cl = opts[:field_class]
+ opts[:field_class] = VectorField
+
+ unless (block and not cl) or (cl and not block)
+ raise ArgumentError,
+ "vector must have either a class or a block, but not both"
+ end
+
+ case
+ when cl == nil
+ vector_class = Class.new(BitStruct::Vector)
+ vector_class.class_eval(&block)
+
+ when cl < BitStruct
+ vector_class = Class.new(BitStruct::Vector)
+ vector_class.struct_class cl
+
+ when cl < BitStruct::Vector
+ vector_class = cl
+
+ else raise ArgumentError, "Bad vector class: #{cl.inspect}"
+ end
+
+ vector_class.default_options default_options
+
+ length = opts[:length] || rest.grep(Integer).first
+ ## what about :length => :lenfield
+ unless length
+ raise ArgumentError,
+ "Must provide length as argument N or as option :length => N"
+ end
+
+ opts[:default] ||= vector_class.new(length) ## nil if variable length
+ opts[:vector_class] = vector_class
+
+ bit_length = vector_class.struct_class.round_byte_length * 8 * length
+
+ field = add_field(name, bit_length, opts)
+ field
+ end
+ BitStruct.autoload :VectorField, "bit-struct/vector-field"
+ end
+
+ autoload :Vector, "bit-struct/vector"
+end
View
61 lib/bit-struct/float-field.rb
@@ -0,0 +1,61 @@
+class BitStruct
+ # Class for floats (single and double precision) in network order.
+ # Declared with BitStruct.float.
+ class FloatField < Field
+ # Used in describe.
+ def self.class_name
+ @class_name ||= "float"
+ end
+
+ def add_accessors_to(cl, attr = name) # :nodoc:
+ unless offset % 8 == 0
+ raise ArgumentError,
+ "Bad offset, #{offset}, for #{self.class} #{name}." +
+ " Must be multiple of 8."
+ end
+
+ unless length == 32 or length == 64
+ raise ArgumentError,
+ "Bad length, #{length}, for #{self.class} #{name}." +
+ " Must be 32 or 64."
+ end
+
+ offset_byte = offset / 8
+ length_byte = length / 8
+ last_byte = offset_byte + length_byte - 1
+ byte_range = offset_byte..last_byte
+
+ endian = (options[:endian] || options["endian"]).to_s
+ case endian
+ when "native"
+ ctl = case length
+ when 32; "f"
+ when 64; "d"
+ end
+ when "little"
+ ctl = case length
+ when 32; "e"
+ when 64; "E"
+ end
+ when "network", "big", ""
+ ctl = case length
+ when 32; "g"
+ when 64; "G"
+ end
+ else
+ raise ArgumentError,
+ "Unrecognized endian option: #{endian.inspect}"
+ end
+
+ cl.class_eval do
+ define_method attr do ||
+ self[byte_range].unpack(ctl).first
+ end
+
+ define_method "#{attr}=" do |val|
+ self[byte_range] = [val].pack(ctl)
+ end
+ end
+ end
+ end
+end
View
20 lib/bit-struct/hex-octet-field.rb
@@ -0,0 +1,20 @@
+require 'bit-struct/char-field'
+
+class BitStruct
+ # Class for char fields that can be accessed with values like
+ # "xx:xx:xx:xx", where each xx is up to 2 hex digits representing a
+ # single octet. The original string-based accessors are still available with
+ # the <tt>_chars</tt> suffix.
+ #
+ # Declared with BitStruct.hex_octets.
+ class HexOctetField < BitStruct::OctetField
+ # Used in describe.
+ def self.class_name
+ @class_name ||= "hex_octets"
+ end
+
+ SEPARATOR = ":"
+ FORMAT = "%02x"
+ BASE = 16
+ end
+end
View
76 lib/bit-struct/nested-field.rb
@@ -0,0 +1,76 @@
+require 'bit-struct/bit-struct'
+
+class BitStruct
+ # Class for nesting a BitStruct as a field within another BitStruct.
+ # Declared with BitStruct.nest.
+ class NestedField < Field
+ def initialize(*args)
+ super
+ end
+
+ # Used in describe.
+ def self.class_name
+ @class_name ||= "nest"
+ end
+
+ def class_name
+ @class_name ||= nested_class.name[/\w+$/]
+ end
+
+ def nested_class
+ @nested_class ||= options[:nested_class] || options["nested_class"]
+ end
+
+ def describe opts
+ if opts[:expand]
+ opts = opts.dup
+ opts[:byte_offset] = offset / 8
+ opts[:omit_header] = opts[:omit_footer] = true
+ nested_class.describe(nil, opts) {|desc| yield desc}
+ else
+ super
+ end
+ end
+
+ def add_accessors_to(cl, attr = name) # :nodoc:
+ unless offset % 8 == 0
+ raise ArgumentError,
+ "Bad offset, #{offset}, for nested field #{name}." +
+ " Must be multiple of 8."
+ end
+
+ unless length % 8 == 0
+ raise ArgumentError,
+ "Bad length, #{length}, for nested field #{name}." +
+ " Must be multiple of 8."
+ end
+
+ offset_byte = offset / 8
+ length_byte = length / 8
+ last_byte = offset_byte + length_byte - 1
+ byte_range = offset_byte..last_byte
+
+ nc = nested_class
+
+ cl.class_eval do
+ define_method attr do ||
+ nc.new(self[byte_range])
+ end
+
+ define_method "#{attr}=" do |val|
+ if val.length != length_byte
+ raise ArgumentError, "Size mismatch in nested struct assignment " +
+ "to #{attr} with value #{val.inspect}"
+ end
+
+ if val.class != nc
+ warn "Type mismatch in nested struct assignment " +
+ "to #{attr} with value #{val.inspect}"
+ end
+
+ self[byte_range] = val
+ end
+ end
+ end
+ end
+end
View
45 lib/bit-struct/octet-field.rb
@@ -0,0 +1,45 @@
+require 'bit-struct/char-field'
+
+class BitStruct
+ # Class for char fields that can be accessed with values like
+ # "xxx.xxx.xxx.xxx", where each xxx is up to 3 decimal digits representing a
+ # single octet. The original string-based accessors are still available with
+ # the <tt>_chars</tt> suffix.
+ #
+ # Declared with BitStruct.octets.
+ class OctetField < BitStruct::CharField
+ # Used in describe.
+ def self.class_name
+ @class_name ||= "octets"
+ end
+
+ SEPARATOR = "."
+ FORMAT = "%d"
+ BASE = 10
+
+ def add_accessors_to(cl, attr = name) # :nodoc:
+ attr_chars = "#{attr}_chars"
+ super(cl, attr_chars)
+ sep = self.class::SEPARATOR
+ base = self.class::BASE
+ fmt = self.class::FORMAT
+
+ cl.class_eval do
+ define_method attr do ||
+ ary = []
+ send(attr_chars).each_byte do |c|
+ ary << fmt % c
+ end
+ ary.join(sep)
+ end
+
+ old_writer = "#{attr_chars}="
+
+ define_method "#{attr}=" do |val|
+ data = val.split(sep).map{|s|s.to_i(base)}.pack("c*")
+ send(old_writer, data)
+ end
+ end
+ end
+ end
+end
View
15 lib/bit-struct/pad-field.rb
@@ -0,0 +1,15 @@
+class BitStruct
+ # Class for fixed length padding.
+ class PadField < Field
+ # Used in describe.
+ def self.class_name
+ @class_name ||= "padding"
+ end
+
+ def add_accessors_to(cl, attr = name) # :nodoc:
+ # No accessors for padding.
+ end
+
+ def inspectable?; false; end
+ end
+end
View
258 lib/bit-struct/signed-field.rb
@@ -0,0 +1,258 @@
+class BitStruct
+ # Class for signed integers in network order, 1-16 bits, or 8n bits.
+ # Declared with BitStruct.signed.
+ class SignedField < Field
+ # Used in describe.
+ def self.class_name
+ @class_name ||= "signed"
+ end
+
+ def add_accessors_to(cl, attr = name) # :nodoc:
+ offset_byte = offset / 8
+ offset_bit = offset % 8
+
+ length_bit = offset_bit + length
+ length_byte = (length_bit/8.0).ceil
+ last_byte = offset_byte + length_byte - 1
+ max = 2**length-1
+ mid = 2**(length-1)
+ max_unsigned = 2**length
+ to_signed = proc {|n| (n>=mid) ? n - max_unsigned : n}
+# to_signed = proc {|n| (n>=mid) ? -((n ^ max) + 1) : n}
+
+ divisor = options[:fixed] || options["fixed"]
+ divisor_f = divisor && divisor.to_f
+# if divisor and not divisor.is_a? Fixnum
+# raise ArgumentError, "fixed-point divisor must be a fixnum"
+# end
+
+ endian = (options[:endian] || options["endian"]).to_s
+ case endian
+ when "native"
+ ctl = length_byte <= 2 ? "s" : "l"
+ if length == 16 or length == 32
+ to_signed = proc {|n| n}
+ # with pack support, to_signed can be replaced with no-op
+ end
+ when "little"
+ ctl = length_byte <= 2 ? "v" : "V"
+ when "network", "big", ""
+ ctl = length_byte <= 2 ? "n" : "N"
+ else
+ raise ArgumentError,
+ "Unrecognized endian option: #{endian.inspect}"
+ end
+
+ data_is_big_endian =
+ ([1234].pack(ctl) == [1234].pack(length_byte <= 2 ? "n" : "N"))
+
+ if length_byte == 1
+ rest = 8 - length_bit
+ mask = ["0"*offset_bit + "1"*length + "0"*rest].pack("B8")[0].ord
+ mask2 = ["1"*offset_bit + "0"*length + "1"*rest].pack("B8")[0].ord
+
+ cl.class_eval do
+ if divisor
+ define_method attr do ||
+ to_signed[(self[offset_byte] & mask) >> rest] / divisor_f
+ end
+
+ define_method "#{attr}=" do |val|
+ val = (val * divisor).round
+ self[offset_byte] =
+ (self[offset_byte] & mask2) | ((val<<rest) & mask)
+ end
+
+ else
+ define_method attr do ||
+ to_signed[(self[offset_byte] & mask) >> rest]
+ end
+
+ define_method "#{attr}=" do |val|
+ self[offset_byte] =
+ (self[offset_byte] & mask2) | ((val<<rest) & mask)
+ end
+ end
+ end
+
+ elsif offset_bit == 0 and length % 8 == 0
+ field_length = length
+ byte_range = offset_byte..last_byte
+
+ cl.class_eval do
+ case field_length
+ when 8
+ if divisor
+ define_method attr do ||
+ to_signed[self[offset_byte]] / divisor_f
+ end
+
+ define_method "#{attr}=" do |val|
+ val = (val * divisor).round
+ self[offset_byte] = val
+ end
+
+ else
+ define_method attr do ||
+ to_signed[self[offset_byte]]
+ end
+
+ define_method "#{attr}=" do |val|
+ self[offset_byte] = val
+ end
+ end
+
+ when 16, 32
+ if divisor
+ define_method attr do ||
+ to_signed[self[byte_range].unpack(ctl).first] / divisor_f
+ end
+
+ define_method "#{attr}=" do |val|
+ val = (val * divisor).round
+ self[byte_range] = [val].pack(ctl)
+ end
+
+ else
+ define_method attr do ||
+ to_signed[self[byte_range].unpack(ctl).first]
+ end
+
+ define_method "#{attr}=" do |val|
+ self[byte_range] = [val].pack(ctl)
+ end
+ end
+
+ else
+ reader_helper = proc do |substr|
+ bytes = substr.unpack("C*")
+ bytes.reverse! unless data_is_big_endian
+ bytes.inject do |sum, byte|
+ (sum << 8) + byte
+ end
+ end
+
+ writer_helper = proc do |val|
+ bytes = []
+ val += max_unsigned if val < 0
+ while val > 0
+ bytes.push val % 256
+ val = val >> 8
+ end
+ if bytes.length < length_byte
+ bytes.concat [0] * (length_byte - bytes.length)
+ end
+
+ bytes.reverse! if data_is_big_endian
+ bytes.pack("C*")
+ end
+
+ if divisor
+ define_method attr do ||
+ to_signed[reader_helper[self[byte_range]] / divisor_f]
+ end
+
+ define_method "#{attr}=" do |val|
+ self[byte_range] = writer_helper[(val * divisor).round]
+ end
+
+ else
+ define_method attr do ||
+ to_signed[reader_helper[self[byte_range]]]
+ end
+
+ define_method "#{attr}=" do |val|
+ self[byte_range] = writer_helper[val]
+ end
+ end
+ end
+ end
+
+ elsif length_byte == 2 # unaligned field that fits within two whole bytes
+ byte_range = offset_byte..last_byte
+ rest = 16 - length_bit
+
+ mask = ["0"*offset_bit + "1"*length + "0"*rest]
+ mask = mask.pack("B16").unpack(ctl).first
+
+ mask2 = ["1"*offset_bit + "0"*length + "1"*rest]
+ mask2 = mask2.pack("B16").unpack(ctl).first
+
+ cl.class_eval do
+ if divisor
+ define_method attr do ||
+ to_signed[(self[byte_range].unpack(ctl).first & mask) >> rest] /
+ divisor_f
+ end
+
+ define_method "#{attr}=" do |val|
+ val = (val * divisor).round
+ x = (self[byte_range].unpack(ctl).first & mask2) |
+ ((val<<rest) & mask)
+ self[byte_range] = [x].pack(ctl)
+ end
+
+ else
+ define_method attr do ||
+ to_signed[(self[byte_range].unpack(ctl).first & mask) >> rest]
+ end
+
+ define_method "#{attr}=" do |val|
+ x = (self[byte_range].unpack(ctl).first & mask2) |
+ ((val<<rest) & mask)
+ self[byte_range] = [x].pack(ctl)
+ end
+ end
+ end
+
+ elsif length_byte == 3 # unaligned field that fits within 3 whole bytes
+ byte_range = offset_byte..last_byte
+ rest = 32 - length_bit
+
+ mask = ["0"*offset_bit + "1"*length + "0"*rest]
+ mask = mask.pack("B32").unpack(ctl).first
+
+ mask2 = ["1"*offset_bit + "0"*length + "1"*rest]
+ mask2 = mask2.pack("B32").unpack(ctl).first
+
+ cl.class_eval do
+ if divisor
+ define_method attr do ||
+ bytes = self[byte_range]
+ bytes << 0
+ to_signed[((bytes.unpack(ctl).first & mask) >> rest)] /
+ divisor_f
+ end
+
+ define_method "#{attr}=" do |val|
+ val = (val * divisor).round
+ bytes = self[byte_range]
+ bytes << 0
+ x = (bytes.unpack(ctl).first & mask2) |
+ ((val<<rest) & mask)
+ self[byte_range] = [x].pack(ctl)[0..2]
+ end
+
+ else
+ define_method attr do ||
+ bytes = self[byte_range]
+ bytes << 0
+ to_signed[(bytes.unpack(ctl).first & mask) >> rest]
+ end
+
+ define_method "#{attr}=" do |val|
+ bytes = self[byte_range]
+ bytes << 0
+ x = (bytes.unpack(ctl).first & mask2) |
+ ((val<<rest) & mask)
+ self[byte_range] = [x].pack(ctl)[0..2]
+ end
+ end
+ end
+
+ else
+ raise "unsupported: #{inspect}"
+ end
+ end
+ end
+end
View
44 lib/bit-struct/text-field.rb
@@ -0,0 +1,44 @@
+class BitStruct
+ # Class for null-terminated printable text strings.
+ # Declared with BitStruct.text.
+ class TextField < Field
+ # Used in describe.
+ def self.class_name
+ @class_name ||= "text"
+ end
+
+ def add_accessors_to(cl, attr = name) # :nodoc:
+ unless offset % 8 == 0
+ raise ArgumentError,
+ "Bad offset, #{offset}, for #{self.class} #{name}." +
+ " Must be multiple of 8."
+ end
+
+ unless length % 8 == 0
+ raise ArgumentError,
+ "Bad length, #{length}, for #{self.class} #{name}." +
+ " Must be multiple of 8."
+ end
+
+ offset_byte = offset / 8
+ length_byte = length / 8
+ last_byte = offset_byte + length_byte - 1
+ byte_range = offset_byte..last_byte
+ val_byte_range = 0..length_byte-1
+
+ cl.class_eval do
+ define_method attr do ||
+ self[byte_range].sub(/\0*$/, "").to_s
+ end
+
+ define_method "#{attr}=" do |val|
+ val = val.to_s
+ if val.length < length_byte
+ val += "\0" * (length_byte - val.length)
+ end
+ self[byte_range] = val[val_byte_range]
+ end
+ end
+ end
+ end
+end
View
248 lib/bit-struct/unsigned-field.rb
@@ -0,0 +1,248 @@
+class BitStruct
+ # Class for unsigned integers in network order, 1-16 bits, or 8n bits.
+ # Declared with BitStruct.unsigned.
+ class UnsignedField < Field
+ # Used in describe.
+ def self.class_name
+ @class_name ||= "unsigned"
+ end
+
+ def add_accessors_to(cl, attr = name) # :nodoc:
+ offset_byte = offset / 8
+ offset_bit = offset % 8
+
+ length_bit = offset_bit + length
+ length_byte = (length_bit/8.0).ceil
+ last_byte = offset_byte + length_byte - 1
+
+ divisor = options[:fixed] || options["fixed"]
+ divisor_f = divisor && divisor.to_f
+# if divisor and not divisor.is_a? Fixnum
+# raise ArgumentError, "fixed-point divisor must be a fixnum"
+# end
+
+ endian = (options[:endian] || options["endian"]).to_s
+ case endian
+ when "native"
+ ctl = length_byte <= 2 ? "S" : "L"
+ when "little"
+ ctl = length_byte <= 2 ? "v" : "V"
+ when "network", "big", ""
+ ctl = length_byte <= 2 ? "n" : "N"