1 /** 2 * Some common stuff for replaying PortForward replay logs. 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <vladimir@thecybershadow.net> 12 */ 13 14 module ae.demo.portforward.replay; 15 16 import std.stdio; 17 import std.conv; 18 import std.string; 19 import std.datetime; 20 import std.exception; 21 22 import ae.utils.text; 23 24 class Replayer 25 { 26 private: 27 File f; 28 string line; 29 30 /// Consume and return next space-delimited word from line. 31 string getWord() 32 { 33 auto p = line.indexOf(' '); 34 string word; 35 if (p<0) 36 word = line, 37 line = null; 38 else 39 word = line[0..p], 40 line = line[p+1..$]; 41 return word; 42 } 43 44 /// Consume and return the rest of the line. 45 string getData() 46 { 47 string data = line; 48 line = null; 49 return data; 50 } 51 52 public: 53 this(string fn) 54 { 55 f.open(fn, "rb"); 56 57 nextLine(); 58 } 59 60 protected: 61 void nextLine() 62 { 63 line = f.readln().chomp(); 64 if (line=="" && f.eof()) 65 return; 66 67 auto recordTime = SysTime(to!long(getWord())); 68 string commandStr = getWord(); 69 enforce(commandStr.length==1, "Invalid command"); 70 char command = commandStr[0]; 71 bool proceed; 72 switch (command) 73 { 74 case 'S': 75 proceed = handleStart(recordTime); 76 break; 77 case 'L': 78 proceed = handleListen(recordTime, to!ushort(getWord())); 79 break; 80 case 'A': 81 proceed = handleAccept(recordTime, to!uint(getWord()), to!ushort(getWord())); 82 break; 83 case 'C': 84 { 85 uint index = to!uint(getWord()); 86 string addr = getWord(); 87 auto p = addr.lastIndexOf(':'); 88 proceed = handleConnect(recordTime, index, addr[0..p], to!ushort(addr[p+1..$])); 89 break; 90 } 91 case '<': 92 proceed = handleIncomingData(recordTime, to!uint(getWord()), stringUnescape(getData())); 93 break; 94 case '>': 95 proceed = handleOutgoingData(recordTime, to!uint(getWord()), stringUnescape(getData())); 96 break; 97 case '[': 98 proceed = handleIncomingDisconnect(recordTime, to!uint(getWord()), getData()); 99 break; 100 case ']': 101 proceed = handleOutgoingDisconnect(recordTime, to!uint(getWord()), getData()); 102 break; 103 default: 104 throw new Exception("Unknown command: " ~ command); 105 } 106 107 enforce(line.length==0, "Unexpected data at the end of line: " ~ line); 108 109 if (proceed) 110 nextLine(); 111 } 112 113 bool handleStart (SysTime time) { return true; } 114 bool handleListen (SysTime time, ushort port) { return true; } 115 bool handleAccept (SysTime time, uint index, ushort port) { return true; } 116 bool handleConnect (SysTime time, uint index, string host, ushort port) { return true; } 117 bool handleIncomingData (SysTime time, uint index, void[] data) { return true; } 118 bool handleOutgoingData (SysTime time, uint index, void[] data) { return true; } 119 bool handleIncomingDisconnect(SysTime time, uint index, string reason) { return true; } 120 bool handleOutgoingDisconnect(SysTime time, uint index, string reason) { return true; } 121 } 122 123 ubyte[] stringUnescape(string s) 124 { 125 ubyte[] r; 126 for (int i=0; i<s.length; i++) 127 if (s[i]=='\\') 128 r ~= fromHex!ubyte(s[i+1..i+3]), 129 i+=2; 130 else 131 r ~= s[i]; 132 return r; 133 }