forked from jochenwierum/openvpn-manager
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathManagementParser.cs
More file actions
218 lines (193 loc) · 8.76 KB
/
ManagementParser.cs
File metadata and controls
218 lines (193 loc) · 8.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
using System;
using System.Globalization;
using System.Text;
namespace OpenVPNUtils
{
// http://openvpn.net/index.php/documentation/miscellaneous/management-interface.html
/// <summary>
/// Class which parses received data from the management interface.
/// </summary>
internal class ManagementParser
{
#region variables
/// <summary>
/// Management logic which answers the requests.
/// </summary>
private ManagementLogic m_logic;
/// <summary>
/// Buffer which holds received lines.
/// </summary>
private StringBuilder m_received = new StringBuilder();
#endregion
/// <summary>
/// Creates a new management parser.
/// </summary>
/// <param name="oc">reference to the network communicator</param>
/// <param name="ol">reference to the management logic</param>
internal ManagementParser(Communicator oc, ManagementLogic ol)
{
m_logic = ol;
oc.gotLine += new UtilsHelper.Action<object,GotLineEventArgs>(oc_gotLine);
}
/// <summary>
/// a line was received, this method parses it and calls methods
/// in the management logic.
/// </summary>
/// <param name="sender">ignored</param>
/// <param name="e">information about the received line</param>
/// <remarks>
/// This method is not thread save!
/// But there is no need to call it from more than one thread.
/// Only the thread which holds the management connection calls this event.
/// </remarks>
private void oc_gotLine(object sender, GotLineEventArgs e)
{
string s = e.line;
// some lines start with a ">" (see link above)
// they come asynchron and are parsed imediately
if (s.StartsWith(">", StringComparison.OrdinalIgnoreCase))
{
string type = s.Substring(1, s.IndexOf(":",
StringComparison.OrdinalIgnoreCase) - 1);
string msg = s.Substring(type.Length + 2);
string[] infos = null;
AsyncEventDetail.EventType et = AsyncEventDetail.EventType.UNKNOWN;
switch (type)
{
case "ECHO": et = AsyncEventDetail.EventType.ECHO; break;
case "FATAL": et = AsyncEventDetail.EventType.FATAL; break;
case "HOLD": et = AsyncEventDetail.EventType.HOLD; break;
case "INFO": et = AsyncEventDetail.EventType.INFO; break;
case "LOG": et = AsyncEventDetail.EventType.LOG; break;
case "NEED-STR":
et = AsyncEventDetail.EventType.NEEDSTR;
string tmp = msg.Substring(msg.IndexOf('\'') + 1);
infos = new string[] {tmp.Substring(0,tmp.IndexOf('\''))};
break;
case "STATE":
et = AsyncEventDetail.EventType.STATE;
infos = msg.Split(new char[] { ',' });
break;
case "PASSWORD":
et = AsyncEventDetail.EventType.PASSWORD;
// Several messages format are possible
// * first is a request for a passwd
// >PASSWORD:Need 'Auth' username/password
// or
// >PASSWORD:Need 'Private Key' password
//
// * second is a notification
// >PASSWORD:Verification Failed: 'Auth'
// or
// >PASSWORD:Verification Failed: 'Private Key'
// Let's first determine the PASSWORD message type and thus format
// "Need" or "Verification"
if(msg.StartsWith("Need",
StringComparison.OrdinalIgnoreCase))
{
string tmp2 = msg.Substring(msg.IndexOf('\'') + 1);
string loginProfile = tmp2.Substring(0, tmp2.IndexOf('\'')); // 'Auth' or 'Private Key' or ...
string loginInfo = tmp2.Substring(tmp2.IndexOf('\'') + 2); // "password" or "username/password"
infos = new string[] { loginProfile, loginInfo, "Need" };
}
// "Verification Failed"
else if(msg.StartsWith("Verification Failed:",
StringComparison.OrdinalIgnoreCase))
{
string tmp2 = msg.Substring(msg.IndexOf('\'') + 1);
string loginProfile = tmp2.Substring(0, tmp2.IndexOf('\'')); // 'Auth' or 'Private Key' or ...
infos = new string[] { loginProfile, null, "Verification" };
}
break;
}
if (et != AsyncEventDetail.EventType.UNKNOWN)
{
m_logic.got_asyncEvent(new AsyncEventDetail(et, msg, infos));
return;
}
}
m_received.Append(s + Environment.NewLine);
s = m_received.ToString();
if (s.StartsWith("SUCCESS: ", StringComparison.OrdinalIgnoreCase)
|| s.StartsWith("ERROR: ", StringComparison.OrdinalIgnoreCase)
|| s.StartsWith(">", StringComparison.OrdinalIgnoreCase) ||
s.EndsWith("END" + Environment.NewLine, StringComparison.OrdinalIgnoreCase))
{
m_received.Remove(0, m_received.Length);
m_logic.cb_syncEvent(s);
}
}
/// <summary>
/// Clear the safed lines.
/// </summary>
public void reset()
{
m_received.Remove(0, m_received.Length);
}
/// <summary>
/// Parse an PKCS11ID-COUNT answer.
/// </summary>
/// <param name="s">the received line</param>
/// <returns>number of keys present, -1 on errors</returns>
public static int getPKCS11IDCount(string s) {
if (s.StartsWith(">PKCS11ID-COUNT:", StringComparison.OrdinalIgnoreCase))
{
s = s.Substring(16);
return int.Parse(s, CultureInfo.InvariantCulture.NumberFormat);
}
else
{
return -1;
}
}
/// <summary>
/// Extract a PKCS11Detail from a given answer.
/// </summary>
/// <param name="s">string which the server sent</param>
/// <returns>a PKCS11Detail structure which holds the extracted information</returns>
public static PKCS11Detail getPKCS11ID(string s) {
// get the essential parts
string[] parts = s.Split(new Char[] { ',' });
int nr;
string id;
string blob;
// try to get the number, id and blob
try
{
if (parts[0].StartsWith(">PKCS11ID-ENTRY:'",
StringComparison.OrdinalIgnoreCase))
nr = int.Parse(parts[0].Substring(17, parts[0].Length - 18),
CultureInfo.InvariantCulture.NumberFormat);
else
return null;
if (parts[1].StartsWith(" ID:'",
StringComparison.OrdinalIgnoreCase))
id = parts[1].Substring(5, parts[1].Length - 6);
else
return null;
if (parts[2].StartsWith(" BLOB:'",
StringComparison.OrdinalIgnoreCase))
blob = parts[2].Substring(7, parts[2].Length - 10);
else
return null;
// return the extracted data
return new PKCS11Detail(nr, id, blob);
}
catch (FormatException)
{
// the format had a problem;
return null;
}
}
/// <summary>
/// encode an parameter for an answer
/// (this is needed if it countains spaces)
/// </summary>
/// <param name="s">the parameter</param>
/// <returns>the encoded parameter</returns>
public static string encodeMsg(string s)
{
return "\"" + s.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"";
}
}
}