Task 628: .RUA File Format
Task 628: .RUA File Format
File Format Specifications for .RUA
The .RUA file format refers to the DMARC (Domain-based Message Authentication, Reporting, and Conformance) Aggregate Report format, as defined in RFC 7489. These reports are XML files that provide aggregated data on email authentication results for messages claiming to be from a particular domain. The format is structured according to a specific XML schema, which ensures consistency in reporting SPF, DKIM, and DMARC alignment results. The files are typically named with .xml extensions but can be referred to or saved as .rua in some contexts for DMARC RUA (Reporting URI for Aggregate) reports. The schema is versioned (currently 1.0) and is hosted at http://dmarc.org/dmarc-xml/0.1/rua.xsd.
- List of all properties of this file format intrinsic to its file system:
The properties are the XML elements and sub-elements defined in the schema. They represent the data fields inherent to the file format's structure, including metadata, policy details, and authentication results. Here is a comprehensive list, organized hierarchically with their types and meanings:
- version (decimal): The version of the report format (must be 1.0 for RFC 7489 compliance).
- report_metadata (complex):
- org_name (string): Name of the organization generating the report.
- email (string): Primary contact email for the reporting organization.
- extra_contact_info (string, optional): Additional contact information for the reporting organization.
- report_id (string): Unique identifier for this specific report.
- date_range (complex):
- begin (integer): Start of the reporting period in Unix timestamp (seconds since epoch, UTC).
- end (integer): End of the reporting period in Unix timestamp (seconds since epoch, UTC).
- error (string, optional, multiple): Any errors encountered during report generation.
- policy_published (complex):
- domain (string): The domain where the DMARC record was found.
- adkim (string, optional): DKIM alignment mode ("r" for relaxed, "s" for strict).
- aspf (string, optional): SPF alignment mode ("r" for relaxed, "s" for strict).
- p (string): Policy for the domain ("none", "quarantine", "reject").
- sp (string): Policy for subdomains ("none", "quarantine", "reject").
- pct (integer): Percentage of messages to which the policy applies (0-100).
- fo (string): Failure reporting options in effect.
- record (complex, multiple allowed):
- row (complex):
- source_ip (string): IP address (IPv4 or IPv6) of the message sender.
- count (integer): Number of messages from this IP during the reporting period.
- policy_evaluated (complex):
- disposition (string): Disposition applied to the messages ("none", "quarantine", "reject").
- dkim (string): DMARC-aligned DKIM result ("pass" or "fail").
- spf (string): DMARC-aligned SPF result ("pass" or "fail").
- reason (complex, optional, multiple):
- type (string): Reason for policy override ("forwarded", "sampled_out", "trusted_forwarder", "mailing_list", "local_policy", "other").
- comment (string, optional): Additional details on the override reason.
- identifiers (complex):
- envelope_to (string, optional): Envelope recipient domain.
- envelope_from (string): RFC 5321 MailFrom domain.
- header_from (string): RFC 5322 From domain.
- auth_results (complex):
- dkim (complex, optional, multiple):
- domain (string): DKIM "d=" parameter (signing domain).
- selector (string, optional): DKIM "s=" parameter (selector).
- result (string): DKIM verification result ("none", "pass", "fail", "policy", "neutral", "temperror", "permerror").
- human_result (string, optional): Additional human-readable DKIM info.
- spf (complex, multiple, at least one required):
- domain (string): Domain checked for SPF.
- scope (string): SPF scope ("helo" or "mfrom").
- result (string): SPF verification result ("none", "neutral", "pass", "fail", "softfail", "temperror", "permerror").
These properties are intrinsic to the format's structure and are not file system metadata like size or creation date; they define the content and organization of the data within the XML file.
- Two direct download links for files of format .RUA:
Note: These are sample DMARC aggregate reports in XML format (the core of .RUA). Direct raw links from GitHub repositories:
- https://raw.githubusercontent.com/domainaware/parsedmarc/master/tests/data/aggregate.xml
- https://raw.githubusercontent.com/domainaware/parsedmarc/master/tests/data/aggregate2.xml
- Ghost blog embedded HTML JavaScript for drag and drop .RUA file dump:
- Python class for .RUA:
import xml.etree.ElementTree as ET
import os
class RuaFileHandler:
def __init__(self, filepath):
self.filepath = filepath
self.tree = None
self.root = None
def read(self):
if not os.path.exists(self.filepath) or not self.filepath.endswith('.rua'):
raise ValueError("Invalid .RUA file path.")
self.tree = ET.parse(self.filepath)
self.root = self.tree.getroot()
if self.root.tag != 'feedback':
raise ValueError("Not a valid DMARC .RUA file.")
def decode_and_print(self):
if not self.root:
self.read()
def traverse(node, path=''):
if len(node) == 0:
print(f"{path}.{node.tag}: {node.text.strip() if node.text else ''}")
else:
for child in node:
traverse(child, path + ('.' if path else '') + node.tag)
traverse(self.root)
def write(self, data):
# data is a dict mirroring the structure, e.g., {'version': '1.0', 'report_metadata': {'org_name': 'Example Org', ...}}
root = ET.Element('feedback')
def build_element(parent, key, value):
if isinstance(value, dict):
sub = ET.SubElement(parent, key)
for k, v in value.items():
build_element(sub, k, v)
else:
sub = ET.SubElement(parent, key)
sub.text = str(value)
for k, v in data.items():
build_element(root, k, v)
tree = ET.ElementTree(root)
tree.write(self.filepath, encoding='utf-8', xml_declaration=True)
# Example usage:
# handler = RuaFileHandler('example.rua')
# handler.read()
# handler.decode_and_print()
# data = {'version': '1.0', 'report_metadata': {'org_name': 'Test'}} # etc.
# handler.write(data)
- Java class for .RUA:
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
public class RuaFileHandler {
private String filepath;
private Document doc;
public RuaFileHandler(String filepath) {
this.filepath = filepath;
}
public void read() throws Exception {
if (!filepath.endsWith(".rua")) {
throw new IllegalArgumentException("Invalid .RUA file.");
}
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
doc = dBuilder.parse(new File(filepath));
doc.getDocumentElement().normalize();
if (!doc.getDocumentElement().getNodeName().equals("feedback")) {
throw new IllegalArgumentException("Not a valid DMARC .RUA file.");
}
}
public void decodeAndPrint() throws Exception {
if (doc == null) {
read();
}
traverseAndPrint(doc.getDocumentElement(), "");
}
private void traverseAndPrint(Node node, String path) {
if (node.getNodeType() == Node.ELEMENT_NODE) {
String currentPath = path.isEmpty() ? node.getNodeName() : path + "." + node.getNodeName();
NodeList children = node.getChildNodes();
boolean hasText = false;
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
if (child.getNodeType() == Node.TEXT_NODE && child.getTextContent().trim().length() > 0) {
System.out.println(currentPath + ": " + child.getTextContent().trim());
hasText = true;
}
}
if (!hasText) {
for (int i = 0; i < children.getLength(); i++) {
traverseAndPrint(children.item(i), currentPath);
}
}
}
}
public void write() throws Exception {
// For simplicity, create a sample document; in real use, build from data map
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document newDoc = dBuilder.newDocument();
org.w3c.dom.Element root = newDoc.createElement("feedback");
newDoc.appendChild(root);
// Add elements example
org.w3c.dom.Element version = newDoc.createElement("version");
version.appendChild(newDoc.createTextNode("1.0"));
root.appendChild(version);
// ... add more as needed
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(newDoc);
StreamResult result = new StreamResult(new File(filepath));
transformer.transform(source, result);
}
// Example usage:
// public static void main(String[] args) throws Exception {
// RuaFileHandler handler = new RuaFileHandler("example.rua");
// handler.read();
// handler.decodeAndPrint();
// handler.write();
// }
}
- JavaScript class for .RUA:
class RuaFileHandler {
constructor(filepath) {
this.filepath = filepath;
}
async read() {
// For browser, assume fetch; for Node, use fs
const response = await fetch(this.filepath);
const xmlText = await response.text();
const parser = new DOMParser();
this.doc = parser.parseFromString(xmlText, 'text/xml');
if (this.doc.documentElement.nodeName !== 'feedback') {
throw new Error('Not a valid DMARC .RUA file.');
}
}
decodeAndPrint() {
if (!this.doc) {
throw new Error('File not read.');
}
let result = 'Dumped .RUA Properties:\n';
const traverse = (node, path = '') => {
if (node.nodeType === Node.ELEMENT_NODE) {
const currentPath = path ? `${path}.${node.nodeName}` : node.nodeName;
if (node.childNodes.length === 1 && node.firstChild.nodeType === Node.TEXT_NODE) {
result += `${currentPath}: ${node.textContent.trim()}\n`;
} else {
node.childNodes.forEach(child => traverse(child, currentPath));
}
}
};
traverse(this.doc.documentElement);
console.log(result);
}
write(data) {
// data is object like {version: '1.0', report_metadata: {org_name: 'Test'}}
const xmlDoc = document.implementation.createDocument(null, 'feedback');
const build = (parent, key, value) => {
if (typeof value === 'object') {
const elem = xmlDoc.createElement(key);
parent.appendChild(elem);
Object.entries(value).forEach(([k, v]) => build(elem, k, v));
} else {
const elem = xmlDoc.createElement(key);
elem.textContent = value;
parent.appendChild(elem);
}
};
Object.entries(data).forEach(([k, v]) => build(xmlDoc.documentElement, k, v));
const serializer = new XMLSerializer();
const xmlStr = serializer.serializeToString(xmlDoc);
console.log(xmlStr); // In real use, write to file via Blob or Node fs
}
}
// Example usage:
// const handler = new RuaFileHandler('example.rua');
// await handler.read();
// handler.decodeAndPrint();
// handler.write({version: '1.0'});
- C class for .RUA:
Note: C does not have native "classes" like C++; this is implemented as a struct with functions (using libxml2 for XML parsing, assuming it's available as it's common for C XML handling). For write, use simple string building for simplicity.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
typedef struct {
char *filepath;
xmlDocPtr doc;
} RuaFileHandler;
RuaFileHandler* rua_create(const char *filepath) {
RuaFileHandler *handler = malloc(sizeof(RuaFileHandler));
handler->filepath = strdup(filepath);
handler->doc = NULL;
return handler;
}
void rua_read(RuaFileHandler *handler) {
if (strstr(handler->filepath, ".rua") == NULL) {
fprintf(stderr, "Invalid .RUA file.\n");
return;
}
handler->doc = xmlReadFile(handler->filepath, NULL, 0);
if (handler->doc == NULL || strcmp((const char*)handler->doc->children->name, "feedback") != 0) {
fprintf(stderr, "Not a valid DMARC .RUA file.\n");
}
}
void traverse_and_print(xmlNode *node, char *path) {
if (node == NULL) return;
char new_path[1024];
snprintf(new_path, sizeof(new_path), "%s%s%s", path, strlen(path) > 0 ? "." : "", node->name);
if (node->type == XML_ELEMENT_NODE) {
if (node->children && node->children->type == XML_TEXT_NODE && node->children->content) {
printf("%s: %s\n", new_path, node->children->content);
} else {
xmlNode *child = node->children;
while (child) {
traverse_and_print(child, new_path);
child = child->next;
}
}
}
}
void rua_decode_and_print(RuaFileHandler *handler) {
if (handler->doc == NULL) rua_read(handler);
traverse_and_print(handler->doc->children, "");
}
void rua_write(RuaFileHandler *handler) {
// Simple example write; in real use, build from data
xmlDocPtr new_doc = xmlNewDoc(BAD_CAST "1.0");
xmlNodePtr root = xmlNewNode(NULL, BAD_CAST "feedback");
xmlDocSetRootElement(new_doc, root);
xmlNewChild(root, NULL, BAD_CAST "version", BAD_CAST "1.0");
// Add more nodes as needed...
xmlSaveFormatFileEnc(handler->filepath, new_doc, "UTF-8", 1);
xmlFreeDoc(new_doc);
}
void rua_destroy(RuaFileHandler *handler) {
if (handler->doc) xmlFreeDoc(handler->doc);
free(handler->filepath);
free(handler);
}
// Example usage:
// int main() {
// RuaFileHandler *handler = rua_create("example.rua");
// rua_read(handler);
// rua_decode_and_print(handler);
// rua_write(handler);
// rua_destroy(handler);
// return 0;
// }