corrade-vassal – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | vero | 1 | /* |
2 | * GridProxy.cs: implementation of OpenMetaverse proxy library |
||
3 | * |
||
4 | * Copyright (c) 2006 Austin Jennings |
||
5 | * Pregen modifications made by Andrew Ortman on Dec 10, 2006 -> Dec 20, 2006 |
||
6 | * |
||
7 | * |
||
8 | * All rights reserved. |
||
9 | * |
||
10 | * - Redistribution and use in source and binary forms, with or without |
||
11 | * modification, are permitted provided that the following conditions are met: |
||
12 | * |
||
13 | * - Redistributions of source code must retain the above copyright notice, this |
||
14 | * list of conditions and the following disclaimer. |
||
15 | * - Neither the name of the openmetaverse.org nor the names |
||
16 | * of its contributors may be used to endorse or promote products derived from |
||
17 | * this software without specific prior written permission. |
||
18 | * |
||
19 | * |
||
20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||
21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||
22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||
23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
||
24 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
||
25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
||
26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||
27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||
28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||
29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||
30 | * POSSIBILITY OF SUCH DAMAGE. |
||
31 | */ |
||
32 | |||
33 | using System; |
||
34 | using System.IO; |
||
35 | using System.Net; |
||
36 | using System.Xml; |
||
37 | using System.Text; |
||
38 | using System.Threading; |
||
39 | using System.Net.Sockets; |
||
40 | using System.Collections.Generic; |
||
41 | using System.Text.RegularExpressions; |
||
42 | using OpenMetaverse; |
||
43 | using OpenMetaverse.Http; |
||
44 | using OpenMetaverse.Packets; |
||
45 | using OpenMetaverse.StructuredData; |
||
46 | using log4net; |
||
47 | using Nwc.XmlRpc; |
||
48 | using Logger = Nwc.XmlRpc.Logger; |
||
49 | |||
50 | namespace GridProxy |
||
51 | { |
||
52 | /// <summary> |
||
53 | /// Proxy Configuration Class |
||
54 | /// </summary> |
||
55 | public class ProxyConfig |
||
56 | { |
||
57 | /// <summary> |
||
58 | /// The user agent reported to the remote server |
||
59 | /// </summary> |
||
60 | public string userAgent; |
||
61 | /// <summary> |
||
62 | /// Email address of the proxy application's author |
||
63 | /// </summary> |
||
64 | public string author; |
||
65 | /// <summary> |
||
66 | /// The port the proxy server will listen on |
||
67 | /// </summary> |
||
68 | public ushort loginPort = 8080; |
||
69 | /// <summary> |
||
70 | /// The IP Address the proxy server will communication with the client on |
||
71 | /// </summary> |
||
72 | public IPAddress clientFacingAddress = IPAddress.Loopback; |
||
73 | /// <summary> |
||
74 | /// The IP Address the proxy server will communicate with the server on |
||
75 | /// </summary> |
||
76 | public IPAddress remoteFacingAddress = IPAddress.Any; |
||
77 | /// <summary> |
||
78 | /// The URI of the login server |
||
79 | /// </summary> |
||
80 | public Uri remoteLoginUri = new Uri("https://login.agni.lindenlab.com/cgi-bin/login.cgi"); |
||
81 | |||
82 | /// <summary> |
||
83 | /// construct a default proxy configuration with the specified userAgent and author |
||
84 | /// </summary> |
||
85 | /// <param name="userAgent">The user agent reported to the remote server</param> |
||
86 | /// <param name="author">Email address of the proxy application's author</param> |
||
87 | public ProxyConfig(string userAgent, string author) |
||
88 | { |
||
89 | this.userAgent = userAgent; |
||
90 | this.author = author; |
||
91 | } |
||
92 | |||
93 | /// <summary> |
||
94 | /// construct a default proxy configuration, parsing command line arguments (try --help) |
||
95 | /// </summary> |
||
96 | /// <param name="userAgent">The user agent reported to the remote server</param> |
||
97 | /// <param name="author">Email address of the proxy application's author</param> |
||
98 | /// <param name="args">An array containing the parameters to use to override the proxy |
||
99 | /// servers default settings</param> |
||
100 | public ProxyConfig(string userAgent, string author, string[] args, bool exitOnError) |
||
101 | : this(userAgent, author) |
||
102 | { |
||
103 | Dictionary<string, ArgumentParser> argumentParsers = new Dictionary<string, ArgumentParser>(); |
||
104 | argumentParsers["help"] = new ArgumentParser(ParseHelp); |
||
105 | argumentParsers["proxy-help"] = new ArgumentParser(ParseHelp); |
||
106 | argumentParsers["proxy-login-port"] = new ArgumentParser(ParseLoginPort); |
||
107 | argumentParsers["proxy-client-facing-address"] = new ArgumentParser(ParseClientFacingAddress); |
||
108 | argumentParsers["proxy-remote-facing-address"] = new ArgumentParser(ParseRemoteFacingAddress); |
||
109 | argumentParsers["proxy-remote-login-uri"] = new ArgumentParser(ParseRemoteLoginUri); |
||
110 | |||
111 | foreach (string arg in args) |
||
112 | { |
||
113 | foreach (string argument in argumentParsers.Keys) |
||
114 | { |
||
115 | Match match = (new Regex("^--" + argument + "(?:=(.*))?$")).Match(arg); |
||
116 | if (match.Success) |
||
117 | { |
||
118 | string value; |
||
119 | if (match.Groups[1].Captures.Count == 1) |
||
120 | value = match.Groups[1].Captures[0].ToString(); |
||
121 | else |
||
122 | value = null; |
||
123 | try |
||
124 | { |
||
125 | ((ArgumentParser)argumentParsers[argument])(value); |
||
126 | } |
||
127 | catch |
||
128 | { |
||
129 | Console.WriteLine("invalid value for --" + argument); |
||
130 | if (exitOnError) |
||
131 | { |
||
132 | ParseHelp(null); |
||
133 | } |
||
134 | else |
||
135 | { |
||
136 | throw; |
||
137 | } |
||
138 | } |
||
139 | } |
||
140 | } |
||
141 | } |
||
142 | } |
||
143 | |||
144 | private delegate void ArgumentParser(string value); |
||
145 | |||
146 | private void ParseHelp(string value) |
||
147 | { |
||
148 | Console.WriteLine("Proxy command-line arguments:"); |
||
149 | Console.WriteLine(" --help display this help"); |
||
150 | Console.WriteLine(" --proxy-login-port=<port> listen for logins on <port>"); |
||
151 | Console.WriteLine(" --proxy-client-facing-address=<IP> communicate with client via <IP>"); |
||
152 | Console.WriteLine(" --proxy-remote-facing-address=<IP> communicate with server via <IP>"); |
||
153 | Console.WriteLine(" --proxy-remote-login-uri=<URI> use SL login server at <URI>"); |
||
154 | Console.WriteLine(" --log-all log all packets by default in Analyst"); |
||
155 | Console.WriteLine(" --log-whitelist=<file> log packets listed in file, one name per line"); |
||
156 | Console.WriteLine(" --no-log-blacklist=<file> don't log packets in file, one name per line"); |
||
157 | Console.WriteLine(" --output=<logfile> log Analyst output to a file"); |
||
158 | |||
159 | Environment.Exit(1); |
||
160 | } |
||
161 | |||
162 | private void ParseLoginPort(string value) |
||
163 | { |
||
164 | loginPort = Convert.ToUInt16(value); |
||
165 | } |
||
166 | |||
167 | private void ParseClientFacingAddress(string value) |
||
168 | { |
||
169 | clientFacingAddress = IPAddress.Parse(value); |
||
170 | } |
||
171 | |||
172 | private void ParseRemoteFacingAddress(string value) |
||
173 | { |
||
174 | remoteFacingAddress = IPAddress.Parse(value); |
||
175 | } |
||
176 | |||
177 | private void ParseRemoteLoginUri(string value) |
||
178 | { |
||
179 | remoteLoginUri = new Uri(value); |
||
180 | } |
||
181 | } |
||
182 | |||
183 | // Proxy: OpenMetaverse proxy server |
||
184 | // A Proxy instance is only prepared to deal with one client at a time. |
||
185 | public class Proxy |
||
186 | { |
||
187 | public ProxyConfig proxyConfig; |
||
188 | private string loginURI; |
||
189 | |||
190 | static List<string> BinaryResponseCaps = new List<string>() |
||
191 | { |
||
192 | "GetTexture", |
||
193 | "GetMesh", |
||
194 | "GetMesh2" |
||
195 | }; |
||
196 | |||
197 | /* |
||
198 | * Proxy Management |
||
199 | */ |
||
200 | |||
201 | // Proxy: construct a proxy server with the given configuration |
||
202 | public Proxy(ProxyConfig proxyConfig) |
||
203 | { |
||
204 | this.proxyConfig = proxyConfig; |
||
205 | |||
206 | ServicePointManager.CertificatePolicy = new TrustAllCertificatePolicy(); |
||
207 | ServicePointManager.Expect100Continue = false; |
||
208 | // Even though this will compile on Mono 2.4, it throws a runtime exception |
||
209 | //ServicePointManager.ServerCertificateValidationCallback = TrustAllCertificatePolicy.TrustAllCertificateHandler; |
||
210 | |||
211 | InitializeLoginProxy(); |
||
212 | InitializeSimProxy(); |
||
213 | InitializeCaps(); |
||
214 | } |
||
215 | |||
216 | object keepAliveLock = new object(); |
||
217 | |||
218 | // Start: begin accepting clients |
||
219 | public void Start() |
||
220 | { |
||
221 | lock (this) |
||
222 | { |
||
223 | System.Threading.Monitor.Enter(keepAliveLock); |
||
224 | (new Thread(new ThreadStart(KeepAlive))).Start(); |
||
225 | |||
226 | RunSimProxy(); |
||
227 | |||
228 | Thread runLoginProxy = new Thread(new ThreadStart(RunLoginProxy)); |
||
229 | runLoginProxy.IsBackground = true; |
||
230 | runLoginProxy.Name = "Login Proxy"; |
||
231 | runLoginProxy.Start(); |
||
232 | |||
233 | IPEndPoint endPoint = (IPEndPoint)loginServer.LocalEndPoint; |
||
234 | IPAddress displayAddress; |
||
235 | if (endPoint.Address == IPAddress.Any) |
||
236 | displayAddress = IPAddress.Loopback; |
||
237 | else |
||
238 | displayAddress = endPoint.Address; |
||
239 | loginURI = "http://" + displayAddress + ":" + endPoint.Port + "/"; |
||
240 | |||
241 | OpenMetaverse.Logger.Log("Proxy ready at " + loginURI, Helpers.LogLevel.Info); |
||
242 | } |
||
243 | } |
||
244 | |||
245 | // Stop: allow foreground threads to die |
||
246 | public void Stop() |
||
247 | { |
||
248 | lock (this) |
||
249 | { |
||
250 | System.Threading.Monitor.Exit(keepAliveLock); |
||
251 | } |
||
252 | } |
||
253 | |||
254 | // KeepAlive: blocks until the proxy is free to shut down |
||
255 | public void KeepAlive() |
||
256 | { |
||
257 | |||
258 | OpenMetaverse.Logger.Log(">T> KeepAlive", Helpers.LogLevel.Debug); |
||
259 | |||
260 | lock (keepAliveLock) { }; |
||
261 | |||
262 | if (loginServer.Connected) |
||
263 | { |
||
264 | loginServer.Disconnect(false); |
||
265 | loginServer.Shutdown(SocketShutdown.Both); |
||
266 | } |
||
267 | |||
268 | loginServer.Close(); |
||
269 | |||
270 | OpenMetaverse.Logger.Log("<T< KeepAlive", Helpers.LogLevel.Debug); |
||
271 | } |
||
272 | |||
273 | // AddDelegate: add callback packetDelegate for packets of type packetName going direction |
||
274 | public void AddDelegate(PacketType packetType, Direction direction, PacketDelegate packetDelegate) |
||
275 | { |
||
276 | lock (this) |
||
277 | { |
||
278 | Dictionary<PacketType, List<PacketDelegate>> delegates = (direction == Direction.Incoming ? incomingDelegates : outgoingDelegates); |
||
279 | if (!delegates.ContainsKey(packetType)) |
||
280 | { |
||
281 | delegates[packetType] = new List<PacketDelegate>(); |
||
282 | } |
||
283 | List<PacketDelegate> delegateArray = delegates[packetType]; |
||
284 | if (!delegateArray.Contains(packetDelegate)) |
||
285 | { |
||
286 | delegateArray.Add(packetDelegate); |
||
287 | } |
||
288 | } |
||
289 | } |
||
290 | |||
291 | // RemoveDelegate: remove callback for packets of type packetName going direction |
||
292 | public void RemoveDelegate(PacketType packetType, Direction direction, PacketDelegate packetDelegate) |
||
293 | { |
||
294 | lock (this) |
||
295 | { |
||
296 | Dictionary<PacketType, List<PacketDelegate>> delegates = (direction == Direction.Incoming ? incomingDelegates : outgoingDelegates); |
||
297 | if (!delegates.ContainsKey(packetType)) |
||
298 | { |
||
299 | return; |
||
300 | } |
||
301 | List<PacketDelegate> delegateArray = delegates[packetType]; |
||
302 | if (delegateArray.Contains(packetDelegate)) |
||
303 | { |
||
304 | delegateArray.Remove(packetDelegate); |
||
305 | } |
||
306 | } |
||
307 | } |
||
308 | |||
309 | private Packet callDelegates(Dictionary<PacketType, List<PacketDelegate>> delegates, Packet packet, IPEndPoint remoteEndPoint) |
||
310 | { |
||
311 | PacketType origType = packet.Type; |
||
312 | foreach (PacketDelegate del in delegates[origType]) |
||
313 | { |
||
314 | try { packet = del(packet, remoteEndPoint); } |
||
315 | catch (Exception ex) { OpenMetaverse.Logger.Log("Error in packet delegate", Helpers.LogLevel.Warning, ex); } |
||
316 | |||
317 | // FIXME: how should we handle the packet type changing? |
||
318 | if (packet == null || packet.Type != origType) break; |
||
319 | } |
||
320 | return packet; |
||
321 | } |
||
322 | |||
323 | // InjectPacket: send packet to the client or server when direction is Incoming or Outgoing, respectively |
||
324 | public void InjectPacket(Packet packet, Direction direction) |
||
325 | { |
||
326 | lock (this) |
||
327 | { |
||
328 | if (activeCircuit == null) |
||
329 | { |
||
330 | // no active circuit; queue the packet for injection once we have one |
||
331 | List<Packet> queue = direction == Direction.Incoming ? queuedIncomingInjections : queuedOutgoingInjections; |
||
332 | queue.Add(packet); |
||
333 | } |
||
334 | else |
||
335 | // tell the active sim proxy to inject the packet |
||
336 | ((SimProxy)simProxies[activeCircuit]).Inject(packet, direction); |
||
337 | } |
||
338 | } |
||
339 | |||
340 | /* |
||
341 | * Login Proxy |
||
342 | */ |
||
343 | |||
344 | private Socket loginServer; |
||
345 | private int capsReqCount = 0; |
||
346 | |||
347 | // InitializeLoginProxy: initialize the login proxy |
||
348 | private void InitializeLoginProxy() |
||
349 | { |
||
350 | try |
||
351 | { |
||
352 | loginServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); |
||
353 | loginServer.Bind(new IPEndPoint(proxyConfig.clientFacingAddress, proxyConfig.loginPort)); |
||
354 | loginServer.Listen(1); |
||
355 | } |
||
356 | catch (SocketException e) |
||
357 | { |
||
358 | OpenMetaverse.Logger.Log("Socket Exception", Helpers.LogLevel.Error, e); |
||
359 | } |
||
360 | catch (ObjectDisposedException e) |
||
361 | { |
||
362 | OpenMetaverse.Logger.Log("Socket Object is disposed Exception", Helpers.LogLevel.Error, e); |
||
363 | } |
||
364 | } |
||
365 | |||
366 | // RunLoginProxy: process login requests from clients |
||
367 | private void RunLoginProxy() |
||
368 | { |
||
369 | OpenMetaverse.Logger.Log(">T> RunLoginProxy", Helpers.LogLevel.Debug); |
||
370 | |||
371 | try |
||
372 | { |
||
373 | for (; ; ) |
||
374 | { |
||
375 | try |
||
376 | { |
||
377 | Socket client = loginServer.Accept(); |
||
378 | |||
379 | Thread connThread = new Thread((ThreadStart)delegate |
||
380 | { |
||
381 | OpenMetaverse.Logger.Log(">T> LoginProxy", Helpers.LogLevel.Debug); |
||
382 | ProxyHTTP(client); |
||
383 | OpenMetaverse.Logger.Log("<T< LoginProxy", Helpers.LogLevel.Debug); |
||
384 | }); |
||
385 | |||
386 | connThread.IsBackground = true; |
||
387 | connThread.Name = "LoginProxy"; |
||
388 | connThread.Start(); |
||
389 | } |
||
390 | catch (SocketException e) |
||
391 | { |
||
392 | // indicates we've told the listener to shutdown |
||
393 | if (e.SocketErrorCode == SocketError.Interrupted) |
||
394 | break; |
||
395 | |||
396 | OpenMetaverse.Logger.Log("Login Failed", Helpers.LogLevel.Error, e); |
||
397 | break; |
||
398 | } |
||
399 | catch (ObjectDisposedException) |
||
400 | { |
||
401 | break; |
||
402 | } |
||
403 | // send any packets queued for injection |
||
404 | if (activeCircuit != null) |
||
405 | { |
||
406 | lock (this) |
||
407 | { |
||
408 | SimProxy activeProxy = (SimProxy)simProxies[activeCircuit]; |
||
409 | foreach (Packet packet in queuedOutgoingInjections) |
||
410 | activeProxy.Inject(packet, Direction.Outgoing); |
||
411 | queuedOutgoingInjections = new List<Packet>(); |
||
412 | } |
||
413 | } |
||
414 | } |
||
415 | } |
||
416 | catch (Exception e) |
||
417 | { |
||
418 | OpenMetaverse.Logger.Log("Exception in RunLoginProxy", Helpers.LogLevel.Error, e); |
||
419 | } |
||
420 | |||
421 | OpenMetaverse.Logger.Log("<T< RunLoginProxy", Helpers.LogLevel.Debug); |
||
422 | } |
||
423 | |||
424 | private class HandyNetReader |
||
425 | { |
||
426 | private NetworkStream netStream; |
||
427 | private const int BUF_SIZE = 8192; |
||
428 | private byte[] buf = new byte[BUF_SIZE]; |
||
429 | private int bufFill = 0; |
||
430 | |||
431 | public HandyNetReader(NetworkStream s) |
||
432 | { |
||
433 | netStream = s; |
||
434 | } |
||
435 | |||
436 | public byte[] ReadLine() |
||
437 | { |
||
438 | int i = -1; |
||
439 | while (true) |
||
440 | { |
||
441 | i = Array.IndexOf(buf, (byte)'\n', 0, bufFill); |
||
442 | if (i >= 0) break; |
||
443 | if (bufFill >= BUF_SIZE) return null; |
||
444 | if (!ReadMore()) return null; |
||
445 | } |
||
446 | if (bufFill < (i + 1)) return null; |
||
447 | byte[] ret = new byte[i]; |
||
448 | Array.Copy(buf, ret, i); |
||
449 | Array.Copy(buf, i + 1, buf, 0, bufFill - (i + 1)); |
||
450 | bufFill -= i + 1; |
||
451 | return ret; |
||
452 | } |
||
453 | |||
454 | private bool ReadMore() |
||
455 | { |
||
456 | try |
||
457 | { |
||
458 | int n = netStream.Read(buf, bufFill, BUF_SIZE - bufFill); |
||
459 | bufFill += n; |
||
460 | return n > 0; |
||
461 | } |
||
462 | catch |
||
463 | { |
||
464 | return false; |
||
465 | } |
||
466 | } |
||
467 | |||
468 | public int Read(byte[] rbuf, int start, int len) |
||
469 | { |
||
470 | int read = 0; |
||
471 | while (len > bufFill) |
||
472 | { |
||
473 | Array.Copy(buf, 0, rbuf, start, bufFill); |
||
474 | start += bufFill; len -= bufFill; |
||
475 | read += bufFill; bufFill = 0; |
||
476 | if (!ReadMore()) break; |
||
477 | } |
||
478 | if (bufFill < len) return 0; |
||
479 | Array.Copy(buf, 0, rbuf, start, len); |
||
480 | Array.Copy(buf, len, buf, 0, bufFill - len); |
||
481 | bufFill -= len; read += len; |
||
482 | return read; |
||
483 | } |
||
484 | } |
||
485 | |||
486 | // ProxyHTTP: proxy a HTTP request |
||
487 | private void ProxyHTTP(Socket client) |
||
488 | { |
||
489 | NetworkStream netStream = new NetworkStream(client); |
||
490 | HandyNetReader reader = new HandyNetReader(netStream); |
||
491 | |||
492 | string line = null; |
||
493 | int reqNo; |
||
494 | int contentLength = 0; |
||
495 | string contentType = ""; |
||
496 | Match match; |
||
497 | string uri; |
||
498 | string meth; |
||
499 | Dictionary<string, string> headers = new Dictionary<string, string>(); |
||
500 | |||
501 | lock (this) |
||
502 | { |
||
503 | capsReqCount++; reqNo = capsReqCount; |
||
504 | } |
||
505 | |||
506 | byte[] byteLine = reader.ReadLine(); |
||
507 | if (byteLine == null) |
||
508 | { |
||
509 | //This dirty hack is part of the LIBOMV-457 workaround |
||
510 | //The connecting libomv client being proxied can manage to trigger a null from the ReadLine() |
||
511 | //The happens just after the seed request and is not seen again. TODO find this bug in the library. |
||
512 | netStream.Close(); client.Close(); |
||
513 | return; |
||
514 | } |
||
515 | |||
516 | if (byteLine != null) line = Encoding.UTF8.GetString(byteLine).Replace("\r", ""); |
||
517 | |||
518 | if (line == null) |
||
519 | throw new Exception("EOF in client HTTP header"); |
||
520 | |||
521 | match = new Regex(@"^(\S+)\s+(\S+)\s+(HTTP/\d\.\d)$").Match(line); |
||
522 | |||
523 | if (!match.Success) |
||
524 | { |
||
525 | OpenMetaverse.Logger.Log("[" + reqNo + "] Bad request!", Helpers.LogLevel.Warning); |
||
526 | byte[] wr = Encoding.ASCII.GetBytes("HTTP/1.0 400 Bad Request\r\nContent-Length: 0\r\n\r\n"); |
||
527 | netStream.Write(wr, 0, wr.Length); |
||
528 | netStream.Close(); client.Close(); |
||
529 | return; |
||
530 | } |
||
531 | |||
532 | meth = match.Groups[1].Captures[0].ToString(); |
||
533 | uri = match.Groups[2].Captures[0].ToString(); |
||
534 | |||
535 | OpenMetaverse.Logger.Log(String.Format("[{0}] {1}:{2}", reqNo, meth, uri), Helpers.LogLevel.Debug); |
||
536 | |||
537 | // read HTTP header |
||
538 | do |
||
539 | { |
||
540 | // read one line of the header |
||
541 | line = Encoding.UTF8.GetString(reader.ReadLine()).Replace("\r", ""); |
||
542 | |||
543 | // check for premature EOF |
||
544 | if (line == null) |
||
545 | throw new Exception("EOF in client HTTP header"); |
||
546 | |||
547 | if (line == "") break; |
||
548 | |||
549 | match = new Regex(@"^([^:]+):\s*(.*)$").Match(line); |
||
550 | |||
551 | if (!match.Success) |
||
552 | { |
||
553 | OpenMetaverse.Logger.Log(String.Format("[{0}] Bad Header: '{1}'", reqNo, line), Helpers.LogLevel.Warning); |
||
554 | byte[] wr = Encoding.ASCII.GetBytes("HTTP/1.0 400 Bad Request\r\nContent-Length: 0\r\n\r\n"); |
||
555 | netStream.Write(wr, 0, wr.Length); |
||
556 | netStream.Close(); client.Close(); |
||
557 | return; |
||
558 | } |
||
559 | |||
560 | string key = match.Groups[1].Captures[0].ToString(); |
||
561 | string val = match.Groups[2].Captures[0].ToString(); |
||
562 | headers[key.ToLower()] = val; |
||
563 | } while (line != ""); |
||
564 | |||
565 | if (headers.ContainsKey("content-length")) |
||
566 | { |
||
567 | contentLength = Convert.ToInt32(headers["content-length"]); |
||
568 | } |
||
569 | |||
570 | if (headers.ContainsKey("content-type")) |
||
571 | { |
||
572 | contentType = headers["content-type"]; |
||
573 | } |
||
574 | |||
575 | // read the HTTP body into a buffer |
||
576 | byte[] content = new byte[contentLength]; |
||
577 | reader.Read(content, 0, contentLength); |
||
578 | |||
579 | if (contentLength < 8192) |
||
580 | OpenMetaverse.Logger.Log(String.Format("[{0}] request length={1}:\n{2}", reqNo, contentLength, Utils.BytesToString(content)), Helpers.LogLevel.Debug); |
||
581 | |||
582 | if (uri == "/") |
||
583 | { |
||
584 | if (contentType == "application/xml+llsd" || contentType == "application/xml") |
||
585 | { |
||
586 | ProxyLoginSD(netStream, content); |
||
587 | } |
||
588 | else |
||
589 | { |
||
590 | ProxyLogin(netStream, content); |
||
591 | } |
||
592 | } |
||
593 | else if (new Regex(@"^/https?://.*$").Match(uri).Success) |
||
594 | { |
||
595 | ProxyCaps(netStream, meth, uri.Substring(1), headers, content, reqNo); |
||
596 | } |
||
597 | else if (new Regex(@"^/https?:/.*$").Match(uri).Success) |
||
598 | { |
||
599 | //This is a libomv client and the proxy CAPS URI has been munged by the C# URI class |
||
600 | //Part of the LIBOMV-457 work around, TODO make this much nicer. |
||
601 | uri = uri.Replace(":/", "://"); |
||
602 | ProxyCaps(netStream, meth, uri.Substring(1), headers, content, reqNo); |
||
603 | } |
||
604 | else |
||
605 | { |
||
606 | OpenMetaverse.Logger.Log("404 not found: " + uri, Helpers.LogLevel.Error); |
||
607 | byte[] wr = Encoding.ASCII.GetBytes("HTTP/1.0 404 Not Found\r\nContent-Length: 0\r\n\r\n"); |
||
608 | netStream.Write(wr, 0, wr.Length); |
||
609 | netStream.Close(); client.Close(); |
||
610 | return; |
||
611 | } |
||
612 | |||
613 | netStream.Close(); |
||
614 | client.Close(); |
||
615 | |||
616 | } |
||
617 | |||
618 | public ObservableDictionary<string, CapInfo> KnownCaps = new ObservableDictionary<string, CapInfo>(); |
||
619 | //private Dictionary<string, bool> SubHack = new Dictionary<string, bool>(); |
||
620 | |||
621 | private void ProxyCaps(NetworkStream netStream, string meth, string uri, Dictionary<string, string> headers, byte[] content, int reqNo) |
||
622 | { |
||
623 | Match match = new Regex(@"^(https?)://([^:/]+)(:\d+)?(/.*)$").Match(uri); |
||
624 | if (!match.Success) |
||
625 | { |
||
626 | OpenMetaverse.Logger.Log("[" + reqNo + "] Malformed proxy URI: " + uri, Helpers.LogLevel.Error); |
||
627 | byte[] wr = Encoding.ASCII.GetBytes("HTTP/1.0 404 Not Found\r\nContent-Length: 0\r\n\r\n"); |
||
628 | netStream.Write(wr, 0, wr.Length); |
||
629 | return; |
||
630 | } |
||
631 | |||
632 | CapInfo cap = null; |
||
633 | lock (this) |
||
634 | { |
||
635 | string capuri = Regex.Replace(uri, @"/?\?.*$", string.Empty); |
||
636 | |||
637 | if (KnownCaps.ContainsKey(capuri)) |
||
638 | { |
||
639 | cap = KnownCaps[capuri]; |
||
640 | } |
||
641 | } |
||
642 | |||
643 | CapsRequest capReq = null; bool shortCircuit = false; bool requestFailed = false; |
||
644 | if (cap != null) |
||
645 | { |
||
646 | capReq = new CapsRequest(cap); |
||
647 | |||
648 | if (cap.ReqFmt == CapsDataFormat.OSD) |
||
649 | { |
||
650 | capReq.Request = OSDParser.DeserializeLLSDXml(content); |
||
651 | } |
||
652 | else |
||
653 | { |
||
654 | capReq.Request = OSDParser.DeserializeLLSDXml(content); |
||
655 | } |
||
656 | |||
657 | capReq.RawRequest = content; |
||
658 | capReq.FullUri = uri; |
||
659 | |||
660 | foreach (CapsDelegate d in cap.GetDelegates()) |
||
661 | { |
||
662 | if (d(capReq, CapsStage.Request)) { shortCircuit = true; break; } |
||
663 | } |
||
664 | } |
||
665 | |||
666 | byte[] respBuf = null; |
||
667 | string consoleMsg = String.Empty; |
||
668 | |||
669 | if (shortCircuit) |
||
670 | { |
||
671 | byte[] wr = Encoding.UTF8.GetBytes("HTTP/1.0 200 OK\r\n"); |
||
672 | netStream.Write(wr, 0, wr.Length); |
||
673 | } |
||
674 | else |
||
675 | { |
||
676 | HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(uri); |
||
677 | req.KeepAlive = false; |
||
678 | |||
679 | foreach (string header in headers.Keys) |
||
680 | { |
||
681 | if (header == "connection" || |
||
682 | header == "content-length" || header == "date" || header == "expect" || |
||
683 | header == "host" || header == "if-modified-since" || header == "referer" || |
||
684 | header == "transfer-encoding" || header == "user-agent" || |
||
685 | header == "proxy-connection" || header == "accept-encoding") |
||
686 | { |
||
687 | // can't touch these! |
||
688 | } |
||
689 | else if (header == "accept") |
||
690 | { |
||
691 | req.Accept = headers["accept"]; |
||
692 | } |
||
693 | else if (header == "content-type") |
||
694 | { |
||
695 | req.ContentType = headers["content-type"]; |
||
696 | } |
||
697 | else if (header == "range") |
||
698 | { |
||
699 | string rangeHeader = headers[header]; |
||
700 | string[] parts = rangeHeader.Split('='); |
||
701 | |||
702 | if (parts.Length == 2) |
||
703 | { |
||
704 | string[] range = parts[1].Split('-'); |
||
705 | int from; |
||
706 | int to; |
||
707 | |||
708 | if (range.Length == 2) |
||
709 | { |
||
710 | if (int.TryParse(range[0], out from) |
||
711 | && int.TryParse(range[1], out to)) |
||
712 | { |
||
713 | req.AddRange(parts[0], from, to); |
||
714 | } |
||
715 | } |
||
716 | else if (range.Length == 1 && int.TryParse(range[0], out to)) |
||
717 | { |
||
718 | req.AddRange(parts[0], to); |
||
719 | } |
||
720 | } |
||
721 | } |
||
722 | else |
||
723 | { |
||
724 | req.Headers[header] = headers[header]; |
||
725 | } |
||
726 | } |
||
727 | if (capReq != null) |
||
728 | { |
||
729 | capReq.RequestHeaders = req.Headers; |
||
730 | } |
||
731 | |||
732 | req.Method = meth; |
||
733 | |||
734 | // can't do gets on requests with a content body |
||
735 | // without throwing a protocol exception. So force it to post |
||
736 | // incase our parser stupidly set it to GET due to the viewer |
||
737 | // doing something stupid like sending an empty request |
||
738 | if (content.Length > 0) |
||
739 | req.Method = "POST"; |
||
740 | |||
741 | req.ContentLength = content.Length; |
||
742 | |||
743 | HttpWebResponse resp; |
||
744 | try |
||
745 | { |
||
746 | if (content.Length > 0) |
||
747 | { |
||
748 | Stream reqStream = req.GetRequestStream(); |
||
749 | reqStream.Write(content, 0, content.Length); |
||
750 | reqStream.Close(); |
||
751 | } |
||
752 | else if (cap == null) |
||
753 | { |
||
754 | OpenMetaverse.Logger.Log(string.Format("{0} {1}", req.Method, req.Address.ToString()), Helpers.LogLevel.Info); |
||
755 | } |
||
756 | resp = (HttpWebResponse)req.GetResponse(); |
||
757 | } |
||
758 | |||
759 | catch (WebException e) |
||
760 | { |
||
761 | if (e.Status == WebExceptionStatus.Timeout || e.Status == WebExceptionStatus.SendFailure) |
||
762 | { |
||
763 | OpenMetaverse.Logger.Log("Request timeout", Helpers.LogLevel.Warning, e); |
||
764 | byte[] wr = Encoding.ASCII.GetBytes("HTTP/1.0 504 Proxy Request Timeout\r\nContent-Length: 0\r\n\r\n"); |
||
765 | netStream.Write(wr, 0, wr.Length); |
||
766 | return; |
||
767 | } |
||
768 | else if (e.Status == WebExceptionStatus.ProtocolError && e.Response != null) |
||
769 | { |
||
770 | resp = (HttpWebResponse)e.Response; requestFailed = true; |
||
771 | } |
||
772 | else |
||
773 | { |
||
774 | OpenMetaverse.Logger.Log("Request error", Helpers.LogLevel.Error, e); |
||
775 | byte[] wr = Encoding.ASCII.GetBytes("HTTP/1.0 502 Gateway Error\r\nContent-Length: 0\r\n\r\n"); // FIXME |
||
776 | netStream.Write(wr, 0, wr.Length); |
||
777 | return; |
||
778 | } |
||
779 | } |
||
780 | |||
781 | try |
||
782 | { |
||
783 | Stream respStream = resp.GetResponseStream(); |
||
784 | int read; |
||
785 | int length = 0; |
||
786 | respBuf = new byte[256]; |
||
787 | |||
788 | do |
||
789 | { |
||
790 | read = respStream.Read(respBuf, length, 256); |
||
791 | if (read > 0) |
||
792 | { |
||
793 | length += read; |
||
794 | Array.Resize(ref respBuf, length + 256); |
||
795 | } |
||
796 | } while (read > 0); |
||
797 | |||
798 | Array.Resize(ref respBuf, length); |
||
799 | |||
800 | if (capReq != null && !requestFailed) |
||
801 | { |
||
802 | if (cap.RespFmt == CapsDataFormat.OSD) |
||
803 | { |
||
804 | capReq.Response = OSDParser.DeserializeLLSDXml(respBuf); |
||
805 | } |
||
806 | else |
||
807 | { |
||
808 | capReq.Response = OSDParser.DeserializeLLSDXml(respBuf); |
||
809 | } |
||
810 | capReq.RawResponse = respBuf; |
||
811 | |||
812 | } |
||
813 | |||
814 | consoleMsg += "[" + reqNo + "] Response from " + uri + "\nStatus: " + (int)resp.StatusCode + " " + resp.StatusDescription + "\n"; |
||
815 | |||
816 | { |
||
817 | byte[] wr = Encoding.UTF8.GetBytes("HTTP/1.0 " + (int)resp.StatusCode + " " + resp.StatusDescription + "\r\n"); |
||
818 | netStream.Write(wr, 0, wr.Length); |
||
819 | } |
||
820 | |||
821 | if (capReq != null) |
||
822 | capReq.ResponseHeaders = resp.Headers; |
||
823 | |||
824 | for (int i = 0; i < resp.Headers.Count; i++) |
||
825 | { |
||
826 | string key = resp.Headers.Keys[i]; |
||
827 | string val = resp.Headers[i]; |
||
828 | string lkey = key.ToLower(); |
||
829 | if (lkey != "content-length" && lkey != "transfer-encoding" && lkey != "connection") |
||
830 | { |
||
831 | consoleMsg += key + ": " + val + "\n"; |
||
832 | byte[] wr = Encoding.UTF8.GetBytes(key + ": " + val + "\r\n"); |
||
833 | netStream.Write(wr, 0, wr.Length); |
||
834 | } |
||
835 | } |
||
836 | } |
||
837 | catch (Exception ex) |
||
838 | { |
||
839 | // TODO: Should we handle this somehow? |
||
840 | OpenMetaverse.Logger.DebugLog("Failed writing output: " + ex.Message); |
||
841 | } |
||
842 | } |
||
843 | |||
844 | if (cap != null && !requestFailed && !capReq.Response.ToString().Equals("undef")) |
||
845 | { |
||
846 | foreach (CapsDelegate d in cap.GetDelegates()) |
||
847 | { |
||
848 | try |
||
849 | { |
||
850 | if (d(capReq, CapsStage.Response)) { break; } |
||
851 | } |
||
852 | catch (InvalidCastException ex) |
||
853 | { |
||
854 | OpenMetaverse.Logger.Log("Invalid Cast thrown trying to cast OSD to OSDMap: \n'" + capReq.Response.AsString() + "' Length=" + capReq.RawResponse.Length.ToString() + "\n", |
||
855 | Helpers.LogLevel.Error, ex); |
||
856 | } |
||
857 | catch (Exception ex) |
||
858 | { |
||
859 | OpenMetaverse.Logger.Log("Error firing delegate", Helpers.LogLevel.Error, ex); |
||
860 | } |
||
861 | } |
||
862 | |||
863 | if (cap.RespFmt == CapsDataFormat.OSD) |
||
864 | { |
||
865 | respBuf = OSDParser.SerializeLLSDXmlBytes((OSD)capReq.Response); |
||
866 | } |
||
867 | else |
||
868 | { |
||
869 | respBuf = OSDParser.SerializeLLSDXmlBytes(capReq.Response); |
||
870 | } |
||
871 | } |
||
872 | |||
873 | |||
874 | string respString; |
||
875 | if (cap == null || cap.RespFmt == CapsDataFormat.Binary) |
||
876 | { |
||
877 | respString = "<data>"; |
||
878 | } |
||
879 | else |
||
880 | { |
||
881 | respString = Encoding.UTF8.GetString(respBuf); |
||
882 | } |
||
883 | |||
884 | consoleMsg += "\n" + respString + "\n--------"; |
||
885 | OpenMetaverse.Logger.Log(consoleMsg, Helpers.LogLevel.Debug); |
||
886 | OpenMetaverse.Logger.Log("[" + reqNo + "] Fixed-up response:\n" + respString + "\n--------", Helpers.LogLevel.Debug); |
||
887 | |||
888 | try |
||
889 | { |
||
890 | byte[] wr2 = Encoding.UTF8.GetBytes("Content-Length: " + respBuf.Length + "\r\n\r\n"); |
||
891 | netStream.Write(wr2, 0, wr2.Length); |
||
892 | |||
893 | netStream.Write(respBuf, 0, respBuf.Length); |
||
894 | } |
||
895 | catch (SocketException) { } |
||
896 | catch (IOException) { } |
||
897 | catch (Exception e) |
||
898 | { |
||
899 | OpenMetaverse.Logger.Log("Exception: Error writing to stream " + e, Helpers.LogLevel.Error, e); |
||
900 | } |
||
901 | |||
902 | return; |
||
903 | } |
||
904 | |||
905 | private bool FixupSeedCapsResponse(CapsRequest capReq, CapsStage stage) |
||
906 | { |
||
907 | if (stage != CapsStage.Response) return false; |
||
908 | |||
909 | OSDMap nm = new OSDMap(); |
||
910 | |||
911 | if (capReq.Response.Type == OSDType.Map) |
||
912 | { |
||
913 | OSDMap m = (OSDMap)capReq.Response; |
||
914 | |||
915 | foreach (string key in m.Keys) |
||
916 | { |
||
917 | string val = m[key].AsString(); |
||
918 | |||
919 | if (!String.IsNullOrEmpty(val)) |
||
920 | { |
||
921 | if (!KnownCaps.ContainsKey(val)) |
||
922 | { |
||
923 | CapsDataFormat resFmt = BinaryResponseCaps.Contains(key) ? CapsDataFormat.Binary : CapsDataFormat.OSD; |
||
924 | CapsDataFormat reqFmt = CapsDataFormat.OSD; |
||
925 | CapInfo newCap = new CapInfo(val, capReq.Info.Sim, key, reqFmt, resFmt); |
||
926 | newCap.AddDelegate(new CapsDelegate(KnownCapDelegate)); |
||
927 | lock (this) { KnownCaps[val] = newCap; } |
||
928 | } |
||
929 | nm[key] = OSD.FromString(loginURI + val); |
||
930 | } |
||
931 | else |
||
932 | { |
||
933 | nm[key] = OSD.FromString(val); |
||
934 | } |
||
935 | } |
||
936 | } |
||
937 | |||
938 | capReq.Response = nm; |
||
939 | return false; |
||
940 | } |
||
941 | |||
942 | private Dictionary<string, List<CapsDelegate>> KnownCapsDelegates = new Dictionary<string, List<CapsDelegate>>(); |
||
943 | |||
944 | |||
945 | private void InitializeCaps() |
||
946 | { |
||
947 | AddCapsDelegate("EventQueueGet", new CapsDelegate(FixupEventQueueGet)); |
||
948 | } |
||
949 | |||
950 | public void AddCapsDelegate(string CapName, CapsDelegate capsDelegate) |
||
951 | { |
||
952 | lock (this) |
||
953 | { |
||
954 | |||
955 | if (!KnownCapsDelegates.ContainsKey(CapName)) |
||
956 | { |
||
957 | KnownCapsDelegates[CapName] = new List<CapsDelegate>(); |
||
958 | } |
||
959 | List<CapsDelegate> delegateArray = KnownCapsDelegates[CapName]; |
||
960 | if (!delegateArray.Contains(capsDelegate)) |
||
961 | { |
||
962 | delegateArray.Add(capsDelegate); |
||
963 | } |
||
964 | } |
||
965 | } |
||
966 | |||
967 | public void RemoveCapRequestDelegate(string CapName, CapsDelegate capsDelegate) |
||
968 | { |
||
969 | lock (this) |
||
970 | { |
||
971 | |||
972 | if (!KnownCapsDelegates.ContainsKey(CapName)) |
||
973 | { |
||
974 | return; |
||
975 | } |
||
976 | List<CapsDelegate> delegateArray = KnownCapsDelegates[CapName]; |
||
977 | if (delegateArray.Contains(capsDelegate)) |
||
978 | { |
||
979 | delegateArray.Remove(capsDelegate); |
||
980 | } |
||
981 | } |
||
982 | } |
||
983 | |||
984 | private bool KnownCapDelegate(CapsRequest capReq, CapsStage stage) |
||
985 | { |
||
986 | lock (this) |
||
987 | { |
||
988 | if (!KnownCapsDelegates.ContainsKey(capReq.Info.CapType)) |
||
989 | return false; |
||
990 | |||
991 | if (stage == CapsStage.Response) |
||
992 | { |
||
993 | if (capReq.Response != null && capReq.Response is OSDMap) |
||
994 | { |
||
995 | OSDMap map = (OSDMap)capReq.Response; |
||
996 | |||
997 | if (map.ContainsKey("uploader")) |
||
998 | { |
||
999 | string val = map["uploader"].AsString(); |
||
1000 | |||
1001 | if (!KnownCaps.ContainsKey(val)) |
||
1002 | { |
||
1003 | CapInfo newCap = new CapInfo(val, capReq.Info.Sim, capReq.Info.CapType, CapsDataFormat.Binary, CapsDataFormat.OSD); |
||
1004 | newCap.AddDelegate(new CapsDelegate(KnownCapDelegate)); |
||
1005 | lock (this) { KnownCaps[val] = newCap; } |
||
1006 | } |
||
1007 | |||
1008 | map["uploader"] = OSD.FromString(loginURI + val); |
||
1009 | } |
||
1010 | } |
||
1011 | } |
||
1012 | |||
1013 | List<CapsDelegate> delegates = KnownCapsDelegates[capReq.Info.CapType]; |
||
1014 | |||
1015 | foreach (CapsDelegate d in delegates) |
||
1016 | { |
||
1017 | if (d(capReq, stage)) { return true; } |
||
1018 | } |
||
1019 | } |
||
1020 | |||
1021 | return false; |
||
1022 | } |
||
1023 | |||
1024 | private bool FixupEventQueueGet(CapsRequest capReq, CapsStage stage) |
||
1025 | { |
||
1026 | if (stage != CapsStage.Response) return false; |
||
1027 | |||
1028 | OSDMap map = null; |
||
1029 | if (capReq.Response is OSDMap) |
||
1030 | map = (OSDMap)capReq.Response; |
||
1031 | else return false; |
||
1032 | |||
1033 | OSDArray array = null; |
||
1034 | if (map.ContainsKey("events") && map["events"] is OSDArray) |
||
1035 | array = (OSDArray)map["events"]; |
||
1036 | else |
||
1037 | return false; |
||
1038 | |||
1039 | for (int i = 0; i < array.Count; i++) |
||
1040 | { |
||
1041 | OSDMap evt = (OSDMap)array[i]; |
||
1042 | |||
1043 | string message = evt["message"].AsString(); |
||
1044 | OSDMap body = (OSDMap)evt["body"]; |
||
1045 | |||
1046 | if (message == "TeleportFinish" || message == "CrossedRegion") |
||
1047 | { |
||
1048 | OSDMap info = null; |
||
1049 | if (message == "TeleportFinish") |
||
1050 | info = (OSDMap)(((OSDArray)body["Info"])[0]); |
||
1051 | else |
||
1052 | info = (OSDMap)(((OSDArray)body["RegionData"])[0]); |
||
1053 | byte[] bytes = info["SimIP"].AsBinary(); |
||
1054 | uint simIP = Utils.BytesToUInt(bytes); |
||
1055 | ushort simPort = (ushort)info["SimPort"].AsInteger(); |
||
1056 | string capsURL = info["SeedCapability"].AsString(); |
||
1057 | |||
1058 | GenericCheck(ref simIP, ref simPort, ref capsURL, capReq.Info.Sim == activeCircuit); |
||
1059 | |||
1060 | info["SeedCapability"] = OSD.FromString(capsURL); |
||
1061 | bytes[0] = (byte)(simIP % 256); |
||
1062 | bytes[1] = (byte)((simIP >> 8) % 256); |
||
1063 | bytes[2] = (byte)((simIP >> 16) % 256); |
||
1064 | bytes[3] = (byte)((simIP >> 24) % 256); |
||
1065 | info["SimIP"] = OSD.FromBinary(bytes); |
||
1066 | info["SimPort"] = OSD.FromInteger(simPort); |
||
1067 | } |
||
1068 | else if (message == "EnableSimulator") |
||
1069 | { |
||
1070 | OSDMap info = null; |
||
1071 | info = (OSDMap)(((OSDArray)body["SimulatorInfo"])[0]); |
||
1072 | byte[] bytes = info["IP"].AsBinary(); |
||
1073 | uint IP = Utils.BytesToUInt(bytes); |
||
1074 | ushort Port = (ushort)info["Port"].AsInteger(); |
||
1075 | string capsURL = null; |
||
1076 | |||
1077 | GenericCheck(ref IP, ref Port, ref capsURL, capReq.Info.Sim == activeCircuit); |
||
1078 | |||
1079 | bytes[0] = (byte)(IP % 256); |
||
1080 | bytes[1] = (byte)((IP >> 8) % 256); |
||
1081 | bytes[2] = (byte)((IP >> 16) % 256); |
||
1082 | bytes[3] = (byte)((IP >> 24) % 256); |
||
1083 | info["IP"] = OSD.FromBinary(bytes); |
||
1084 | info["Port"] = OSD.FromInteger(Port); |
||
1085 | } |
||
1086 | else if (message == "EstablishAgentCommunication") |
||
1087 | { |
||
1088 | string ipAndPort = body["sim-ip-and-port"].AsString(); |
||
1089 | string[] pieces = ipAndPort.Split(':'); |
||
1090 | byte[] bytes = IPAddress.Parse(pieces[0]).GetAddressBytes(); |
||
1091 | uint simIP = Utils.BytesToUInt(bytes); |
||
1092 | ushort simPort = (ushort)Convert.ToInt32(pieces[1]); |
||
1093 | |||
1094 | string capsURL = body["seed-capability"].AsString(); |
||
1095 | |||
1096 | OpenMetaverse.Logger.Log("DEBUG: Got EstablishAgentCommunication for " + ipAndPort + " with seed cap " + capsURL, Helpers.LogLevel.Debug); |
||
1097 | |||
1098 | GenericCheck(ref simIP, ref simPort, ref capsURL, false); |
||
1099 | body["seed-capability"] = OSD.FromString(capsURL); |
||
1100 | string ipport = String.Format("{0}:{1}", new IPAddress(simIP), simPort); |
||
1101 | body["sim-ip-and-port"] = OSD.FromString(ipport); |
||
1102 | |||
1103 | OpenMetaverse.Logger.Log("DEBUG: Modified EstablishAgentCommunication to " + body["sim-ip-and-port"].AsString() + " with seed cap " + capsURL, Helpers.LogLevel.Debug); |
||
1104 | } |
||
1105 | } |
||
1106 | return false; |
||
1107 | } |
||
1108 | |||
1109 | private void ProxyLogin(NetworkStream netStream, byte[] content) |
||
1110 | { |
||
1111 | lock (this) |
||
1112 | { |
||
1113 | // incase some silly person tries to access with their web browser |
||
1114 | if (content.Length <= 0) |
||
1115 | return; |
||
1116 | |||
1117 | // convert the body into an XML-RPC request |
||
1118 | XmlRpcRequest request = (XmlRpcRequest)(new XmlRpcRequestDeserializer()).Deserialize(Encoding.UTF8.GetString(content)); |
||
1119 | |||
1120 | // call the loginRequestDelegate |
||
1121 | lock (loginRequestDelegates) |
||
1122 | { |
||
1123 | foreach (XmlRpcRequestDelegate d in loginRequestDelegates) |
||
1124 | { |
||
1125 | try { d(this, new XmlRpcRequestEventArgs(request)); } |
||
1126 | //try { d(request); } |
||
1127 | catch (Exception e) { OpenMetaverse.Logger.Log("Exception in login request delegate" + e, Helpers.LogLevel.Error, e); } |
||
1128 | } |
||
1129 | } |
||
1130 | XmlRpcResponse response; |
||
1131 | try |
||
1132 | { |
||
1133 | // forward the XML-RPC request to the server |
||
1134 | response = (XmlRpcResponse)request.Send(proxyConfig.remoteLoginUri.ToString(), |
||
1135 | 30 * 1000); // 30 second timeout |
||
1136 | } |
||
1137 | catch (Exception e) |
||
1138 | { |
||
1139 | OpenMetaverse.Logger.Log("Error during login response", Helpers.LogLevel.Error, e); |
||
1140 | return; |
||
1141 | } |
||
1142 | |||
1143 | System.Collections.Hashtable responseData; |
||
1144 | try |
||
1145 | { |
||
1146 | responseData = (System.Collections.Hashtable)response.Value; |
||
1147 | } |
||
1148 | catch (Exception e) |
||
1149 | { |
||
1150 | OpenMetaverse.Logger.Log(e.Message, Helpers.LogLevel.Error); |
||
1151 | return; |
||
1152 | } |
||
1153 | |||
1154 | // proxy any simulator address given in the XML-RPC response |
||
1155 | if (responseData.Contains("sim_ip") && responseData.Contains("sim_port")) |
||
1156 | { |
||
1157 | IPEndPoint realSim = new IPEndPoint(IPAddress.Parse((string)responseData["sim_ip"]), Convert.ToUInt16(responseData["sim_port"])); |
||
1158 | IPEndPoint fakeSim = ProxySim(realSim); |
||
1159 | responseData["sim_ip"] = fakeSim.Address.ToString(); |
||
1160 | responseData["sim_port"] = fakeSim.Port; |
||
1161 | activeCircuit = realSim; |
||
1162 | } |
||
1163 | |||
1164 | // start a new proxy session |
||
1165 | Reset(); |
||
1166 | |||
1167 | if (responseData.Contains("seed_capability")) |
||
1168 | { |
||
1169 | CapInfo info = new CapInfo((string)responseData["seed_capability"], activeCircuit, "SeedCapability"); |
||
1170 | info.AddDelegate(new CapsDelegate(FixupSeedCapsResponse)); |
||
1171 | |||
1172 | KnownCaps[(string)responseData["seed_capability"]] = info; |
||
1173 | responseData["seed_capability"] = loginURI + responseData["seed_capability"]; |
||
1174 | } |
||
1175 | |||
1176 | // forward the XML-RPC response to the client |
||
1177 | StreamWriter writer = new StreamWriter(netStream); |
||
1178 | writer.Write("HTTP/1.0 200 OK\r\n"); |
||
1179 | writer.Write("Content-type: text/xml\r\n"); |
||
1180 | writer.Write("\r\n"); |
||
1181 | |||
1182 | XmlTextWriter responseWriter = new XmlTextWriter(writer); |
||
1183 | XmlRpcResponseSerializer.Singleton.Serialize(responseWriter, response); |
||
1184 | responseWriter.Close(); writer.Close(); |
||
1185 | |||
1186 | lock (loginResponseDelegates) |
||
1187 | { |
||
1188 | foreach (XmlRpcResponseDelegate d in loginResponseDelegates) |
||
1189 | { |
||
1190 | try { d(response); } |
||
1191 | catch (Exception e) { OpenMetaverse.Logger.Log("Exception in login response delegate" + e, Helpers.LogLevel.Error, e); } |
||
1192 | } |
||
1193 | } |
||
1194 | |||
1195 | } |
||
1196 | } |
||
1197 | |||
1198 | private void ProxyLoginSD(NetworkStream netStream, byte[] content) |
||
1199 | { |
||
1200 | lock (this) |
||
1201 | { |
||
1202 | AutoResetEvent remoteComplete = new AutoResetEvent(false); |
||
1203 | CapsClient loginRequest = new CapsClient(proxyConfig.remoteLoginUri); |
||
1204 | OSD response = null; |
||
1205 | loginRequest.OnComplete += new CapsClient.CompleteCallback( |
||
1206 | delegate(CapsClient client, OSD result, Exception error) |
||
1207 | { |
||
1208 | if (error == null) |
||
1209 | { |
||
1210 | if (result != null && result.Type == OSDType.Map) |
||
1211 | { |
||
1212 | response = result; |
||
1213 | } |
||
1214 | } |
||
1215 | remoteComplete.Set(); |
||
1216 | } |
||
1217 | ); |
||
1218 | loginRequest.BeginGetResponse(content, "application/llsd+xml", 1000 * 100); |
||
1219 | remoteComplete.WaitOne(1000 * 100, false); |
||
1220 | |||
1221 | if (response == null) |
||
1222 | { |
||
1223 | byte[] wr = Encoding.ASCII.GetBytes("HTTP/1.0 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n"); |
||
1224 | netStream.Write(wr, 0, wr.Length); |
||
1225 | return; |
||
1226 | } |
||
1227 | |||
1228 | OSDMap map = (OSDMap)response; |
||
1229 | |||
1230 | OSD llsd; |
||
1231 | string sim_port = null, sim_ip = null, seed_capability = null; |
||
1232 | map.TryGetValue("sim_port", out llsd); |
||
1233 | if (llsd != null) sim_port = llsd.AsString(); |
||
1234 | map.TryGetValue("sim_ip", out llsd); |
||
1235 | if (llsd != null) sim_ip = llsd.AsString(); |
||
1236 | map.TryGetValue("seed_capability", out llsd); |
||
1237 | if (llsd != null) seed_capability = llsd.AsString(); |
||
1238 | |||
1239 | if (sim_port == null || sim_ip == null || seed_capability == null) |
||
1240 | { |
||
1241 | if (map != null) |
||
1242 | { |
||
1243 | OpenMetaverse.Logger.Log("Connection to server failed, returned LLSD error follows:\n" + map.ToString(), Helpers.LogLevel.Error); |
||
1244 | } |
||
1245 | byte[] wr = Encoding.ASCII.GetBytes("HTTP/1.0 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n"); |
||
1246 | netStream.Write(wr, 0, wr.Length); |
||
1247 | return; |
||
1248 | } |
||
1249 | |||
1250 | IPEndPoint realSim = new IPEndPoint(IPAddress.Parse(sim_ip), Convert.ToUInt16(sim_port)); |
||
1251 | IPEndPoint fakeSim = ProxySim(realSim); |
||
1252 | map["sim_ip"] = OSD.FromString(fakeSim.Address.ToString()); |
||
1253 | map["sim_port"] = OSD.FromInteger(fakeSim.Port); |
||
1254 | activeCircuit = realSim; |
||
1255 | |||
1256 | // start a new proxy session |
||
1257 | Reset(); |
||
1258 | |||
1259 | CapInfo info = new CapInfo(seed_capability, activeCircuit, "SeedCapability"); |
||
1260 | info.AddDelegate(new CapsDelegate(FixupSeedCapsResponse)); |
||
1261 | |||
1262 | KnownCaps[seed_capability] = info; |
||
1263 | map["seed_capability"] = OSD.FromString(loginURI + seed_capability); |
||
1264 | |||
1265 | StreamWriter writer = new StreamWriter(netStream); |
||
1266 | writer.Write("HTTP/1.0 200 OK\r\n"); |
||
1267 | writer.Write("Content-type: application/xml+llsd\r\n"); |
||
1268 | writer.Write("\r\n"); |
||
1269 | writer.Write(OSDParser.SerializeLLSDXmlString(response)); |
||
1270 | writer.Close(); |
||
1271 | } |
||
1272 | } |
||
1273 | |||
1274 | /* |
||
1275 | * Sim Proxy |
||
1276 | */ |
||
1277 | |||
1278 | private Socket simFacingSocket; |
||
1279 | public IPEndPoint activeCircuit = null; |
||
1280 | private Dictionary<IPEndPoint, IPEndPoint> proxyEndPoints = new Dictionary<IPEndPoint, IPEndPoint>(); |
||
1281 | private Dictionary<IPEndPoint, SimProxy> simProxies = new Dictionary<IPEndPoint, SimProxy>(); |
||
1282 | private Dictionary<EndPoint, SimProxy> proxyHandlers = new Dictionary<EndPoint, SimProxy>(); |
||
1283 | //private XmlRpcRequestDelegate loginRequestDelegate = null; |
||
1284 | //private XmlRpcResponseDelegate loginResponseDelegate = null; |
||
1285 | |||
1286 | public List<XmlRpcRequestDelegate> loginRequestDelegates = new List<XmlRpcRequestDelegate>(); |
||
1287 | public List<XmlRpcResponseDelegate> loginResponseDelegates = new List<XmlRpcResponseDelegate>(); |
||
1288 | |||
1289 | private Dictionary<PacketType, List<PacketDelegate>> incomingDelegates = new Dictionary<PacketType, List<PacketDelegate>>(); |
||
1290 | private Dictionary<PacketType, List<PacketDelegate>> outgoingDelegates = new Dictionary<PacketType, List<PacketDelegate>>(); |
||
1291 | private List<Packet> queuedIncomingInjections = new List<Packet>(); |
||
1292 | private List<Packet> queuedOutgoingInjections = new List<Packet>(); |
||
1293 | |||
1294 | // InitializeSimProxy: initialize the sim proxy |
||
1295 | private void InitializeSimProxy() |
||
1296 | { |
||
1297 | InitializeAddressCheckers(); |
||
1298 | |||
1299 | simFacingSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); |
||
1300 | simFacingSocket.Bind(new IPEndPoint(proxyConfig.remoteFacingAddress, 0)); |
||
1301 | Reset(); |
||
1302 | } |
||
1303 | |||
1304 | // Reset: start a new session |
||
1305 | private void Reset() |
||
1306 | { |
||
1307 | foreach (SimProxy simProxy in simProxies.Values) |
||
1308 | simProxy.Reset(); |
||
1309 | |||
1310 | KnownCaps.Clear(); |
||
1311 | } |
||
1312 | |||
1313 | private byte[] receiveBuffer = new byte[8192]; |
||
1314 | private byte[] zeroBuffer = new byte[8192]; |
||
1315 | private EndPoint remoteEndPoint = (EndPoint)new IPEndPoint(IPAddress.Any, 0); |
||
1316 | |||
1317 | // RunSimProxy: start listening for packets from remote sims |
||
1318 | private void RunSimProxy() |
||
1319 | { |
||
1320 | simFacingSocket.BeginReceiveFrom(receiveBuffer, 0, receiveBuffer.Length, SocketFlags.None, ref remoteEndPoint, new AsyncCallback(ReceiveFromSim), null); |
||
1321 | } |
||
1322 | |||
1323 | // ReceiveFromSim: packet received from a remote sim |
||
1324 | private void ReceiveFromSim(IAsyncResult ar) |
||
1325 | { |
||
1326 | lock (this) |
||
1327 | try |
||
1328 | { |
||
1329 | //if (!simFacingSocket.Connected) return; |
||
1330 | // pause listening and fetch the packet |
||
1331 | bool needsZero = false; |
||
1332 | bool needsCopy = true; |
||
1333 | int length; |
||
1334 | length = simFacingSocket.EndReceiveFrom(ar, ref remoteEndPoint); |
||
1335 | |||
1336 | if (proxyHandlers.ContainsKey(remoteEndPoint)) |
||
1337 | { |
||
1338 | // find the proxy responsible for forwarding this packet |
||
1339 | SimProxy simProxy = (SimProxy)proxyHandlers[remoteEndPoint]; |
||
1340 | |||
1341 | // interpret the packet according to the SL protocol |
||
1342 | Packet packet; |
||
1343 | int end = length - 1; |
||
1344 | |||
1345 | packet = Packet.BuildPacket(receiveBuffer, ref end, zeroBuffer); |
||
1346 | |||
1347 | // check for ACKs we're waiting for |
||
1348 | packet = simProxy.CheckAcks(packet, Direction.Incoming, ref length, ref needsCopy); |
||
1349 | |||
1350 | // modify sequence numbers to account for injections |
||
1351 | uint oldSequence = packet.Header.Sequence; |
||
1352 | packet = simProxy.ModifySequence(packet, Direction.Incoming, ref length, ref needsCopy); |
||
1353 | |||
1354 | // keep track of sequence numbers |
||
1355 | if (packet.Header.Sequence > simProxy.incomingSequence) |
||
1356 | simProxy.incomingSequence = packet.Header.Sequence; |
||
1357 | |||
1358 | // check the packet for addresses that need proxying |
||
1359 | if (incomingCheckers.ContainsKey(packet.Type)) |
||
1360 | { |
||
1361 | /* if (needsZero) { |
||
1362 | length = Helpers.ZeroDecode(packet.Header.Data, length, zeroBuffer); |
||
1363 | packet.Header.Data = zeroBuffer; |
||
1364 | needsZero = false; |
||
1365 | } */ |
||
1366 | |||
1367 | Packet newPacket = ((AddressChecker)incomingCheckers[packet.Type])(packet); |
||
1368 | SwapPacket(packet, newPacket); |
||
1369 | packet = newPacket; |
||
1370 | needsCopy = false; |
||
1371 | } |
||
1372 | |||
1373 | // pass the packet to any callback delegates |
||
1374 | if (incomingDelegates.ContainsKey(packet.Type)) |
||
1375 | { |
||
1376 | /* if (needsZero) { |
||
1377 | length = Helpers.ZeroDecode(packet.Header.Data, length, zeroBuffer); |
||
1378 | packet.Header.Data = zeroBuffer; |
||
1379 | needsCopy = true; |
||
1380 | } */ |
||
1381 | |||
1382 | if (packet.Header.AckList != null && needsCopy) |
||
1383 | { |
||
1384 | uint[] newAcks = new uint[packet.Header.AckList.Length]; |
||
1385 | Array.Copy(packet.Header.AckList, 0, newAcks, 0, newAcks.Length); |
||
1386 | packet.Header.AckList = newAcks; // FIXME |
||
1387 | } |
||
1388 | |||
1389 | try |
||
1390 | { |
||
1391 | Packet newPacket = callDelegates(incomingDelegates, packet, (IPEndPoint)remoteEndPoint); |
||
1392 | if (newPacket == null) |
||
1393 | { |
||
1394 | if (packet.Header.Reliable) |
||
1395 | simProxy.Inject(SpoofAck(oldSequence), Direction.Outgoing); |
||
1396 | |||
1397 | if (packet.Header.AppendedAcks) |
||
1398 | packet = SeparateAck(packet); |
||
1399 | else |
||
1400 | packet = null; |
||
1401 | } |
||
1402 | else |
||
1403 | { |
||
1404 | bool oldReliable = packet.Header.Reliable; |
||
1405 | bool newReliable = newPacket.Header.Reliable; |
||
1406 | if (oldReliable && !newReliable) |
||
1407 | simProxy.Inject(SpoofAck(oldSequence), Direction.Outgoing); |
||
1408 | else if (!oldReliable && newReliable) |
||
1409 | simProxy.WaitForAck(packet, Direction.Incoming); |
||
1410 | |||
1411 | SwapPacket(packet, newPacket); |
||
1412 | packet = newPacket; |
||
1413 | } |
||
1414 | } |
||
1415 | catch (Exception e) |
||
1416 | { |
||
1417 | OpenMetaverse.Logger.Log("Exception in incoming delegate", Helpers.LogLevel.Error, e); |
||
1418 | } |
||
1419 | |||
1420 | if (packet != null) |
||
1421 | simProxy.SendPacket(packet, false); |
||
1422 | } |
||
1423 | else |
||
1424 | simProxy.SendPacket(packet, needsZero); |
||
1425 | } |
||
1426 | else |
||
1427 | // ignore packets from unknown peers |
||
1428 | OpenMetaverse.Logger.Log("Dropping packet from unknown peer " + remoteEndPoint, Helpers.LogLevel.Warning); |
||
1429 | } |
||
1430 | catch (Exception e) |
||
1431 | { |
||
1432 | OpenMetaverse.Logger.Log("Error processing incoming packet from simulator", Helpers.LogLevel.Error, e); |
||
1433 | } |
||
1434 | finally |
||
1435 | { |
||
1436 | // resume listening |
||
1437 | try |
||
1438 | { |
||
1439 | simFacingSocket.BeginReceiveFrom(receiveBuffer, 0, receiveBuffer.Length, SocketFlags.None, |
||
1440 | ref remoteEndPoint, new AsyncCallback(ReceiveFromSim), null); |
||
1441 | } |
||
1442 | catch (Exception e) |
||
1443 | { |
||
1444 | OpenMetaverse.Logger.Log("Listener Socket Exception", Helpers.LogLevel.Error, e); |
||
1445 | } |
||
1446 | } |
||
1447 | } |
||
1448 | |||
1449 | // SendPacket: send a packet to a sim from our fake client endpoint |
||
1450 | public void SendPacket(Packet packet, IPEndPoint endPoint, bool skipZero) |
||
1451 | { |
||
1452 | |||
1453 | byte[] buffer = packet.ToBytes(); |
||
1454 | if (skipZero || !packet.Header.Zerocoded) |
||
1455 | simFacingSocket.SendTo(buffer, buffer.Length, SocketFlags.None, endPoint); |
||
1456 | else |
||
1457 | { |
||
1458 | int zeroLength = Helpers.ZeroEncode(buffer, buffer.Length, zeroBuffer); |
||
1459 | simFacingSocket.SendTo(zeroBuffer, zeroLength, SocketFlags.None, endPoint); |
||
1460 | } |
||
1461 | } |
||
1462 | |||
1463 | // SpoofAck: create an ACK for the given packet |
||
1464 | public Packet SpoofAck(uint sequence) |
||
1465 | { |
||
1466 | PacketAckPacket spoof = new PacketAckPacket(); |
||
1467 | spoof.Packets = new PacketAckPacket.PacketsBlock[1]; |
||
1468 | spoof.Packets[0] = new PacketAckPacket.PacketsBlock(); |
||
1469 | spoof.Packets[0].ID = sequence; |
||
1470 | return (Packet)spoof; |
||
1471 | } |
||
1472 | |||
1473 | // SeparateAck: create a standalone PacketAck for packet's appended ACKs |
||
1474 | public Packet SeparateAck(Packet packet) |
||
1475 | { |
||
1476 | PacketAckPacket seperate = new PacketAckPacket(); |
||
1477 | seperate.Packets = new PacketAckPacket.PacketsBlock[packet.Header.AckList.Length]; |
||
1478 | |||
1479 | for (int i = 0; i < packet.Header.AckList.Length; ++i) |
||
1480 | { |
||
1481 | seperate.Packets[i] = new PacketAckPacket.PacketsBlock(); |
||
1482 | seperate.Packets[i].ID = packet.Header.AckList[i]; |
||
1483 | } |
||
1484 | |||
1485 | Packet ack = seperate; |
||
1486 | ack.Header.Sequence = packet.Header.Sequence; |
||
1487 | return ack; |
||
1488 | } |
||
1489 | |||
1490 | // SwapPacket: copy the sequence number and appended ACKs from one packet to another |
||
1491 | public static void SwapPacket(Packet oldPacket, Packet newPacket) |
||
1492 | { |
||
1493 | newPacket.Header.Sequence = oldPacket.Header.Sequence; |
||
1494 | |||
1495 | int oldAcks = oldPacket.Header.AppendedAcks ? oldPacket.Header.AckList.Length : 0; |
||
1496 | int newAcks = newPacket.Header.AppendedAcks ? newPacket.Header.AckList.Length : 0; |
||
1497 | |||
1498 | if (oldAcks != 0 || newAcks != 0) |
||
1499 | { |
||
1500 | uint[] newAckList = new uint[oldAcks]; |
||
1501 | Array.Copy(oldPacket.Header.AckList, 0, newAckList, 0, oldAcks); |
||
1502 | |||
1503 | newPacket.Header.AckList = newAckList; |
||
1504 | newPacket.Header.AppendedAcks = oldPacket.Header.AppendedAcks; |
||
1505 | } |
||
1506 | } |
||
1507 | |||
1508 | // ProxySim: return the proxy for the specified sim, creating it if it doesn't exist |
||
1509 | private IPEndPoint ProxySim(IPEndPoint simEndPoint) |
||
1510 | { |
||
1511 | if (proxyEndPoints.ContainsKey(simEndPoint)) |
||
1512 | // return the existing proxy |
||
1513 | return (IPEndPoint)proxyEndPoints[simEndPoint]; |
||
1514 | else |
||
1515 | { |
||
1516 | // return a new proxy |
||
1517 | SimProxy simProxy = new SimProxy(proxyConfig, simEndPoint, this); |
||
1518 | IPEndPoint fakeSim = simProxy.LocalEndPoint(); |
||
1519 | OpenMetaverse.Logger.Log("Creating proxy for " + simEndPoint + " at " + fakeSim, Helpers.LogLevel.Info); |
||
1520 | simProxy.Run(); |
||
1521 | proxyEndPoints.Add(simEndPoint, fakeSim); |
||
1522 | simProxies.Add(simEndPoint, simProxy); |
||
1523 | return fakeSim; |
||
1524 | } |
||
1525 | } |
||
1526 | |||
1527 | // AddHandler: remember which sim proxy corresponds to a given sim |
||
1528 | private void AddHandler(EndPoint endPoint, SimProxy proxy) |
||
1529 | { |
||
1530 | proxyHandlers.Add(endPoint, proxy); |
||
1531 | } |
||
1532 | |||
1533 | // SimProxy: proxy for a single simulator |
||
1534 | private class SimProxy |
||
1535 | { |
||
1536 | //private ProxyConfig proxyConfig; |
||
1537 | private IPEndPoint remoteEndPoint; |
||
1538 | private Proxy proxy; |
||
1539 | private Socket socket; |
||
1540 | public uint incomingSequence; |
||
1541 | public uint outgoingSequence; |
||
1542 | private List<uint> incomingInjections; |
||
1543 | private List<uint> outgoingInjections; |
||
1544 | private uint incomingOffset = 0; |
||
1545 | private uint outgoingOffset = 0; |
||
1546 | private Dictionary<uint, Packet> incomingAcks; |
||
1547 | private Dictionary<uint, Packet> outgoingAcks; |
||
1548 | private List<uint> incomingSeenAcks; |
||
1549 | private List<uint> outgoingSeenAcks; |
||
1550 | |||
1551 | // SimProxy: construct a proxy for a single simulator |
||
1552 | public SimProxy(ProxyConfig proxyConfig, IPEndPoint simEndPoint, Proxy proxy) |
||
1553 | { |
||
1554 | //this.proxyConfig = proxyConfig; |
||
1555 | remoteEndPoint = new IPEndPoint(simEndPoint.Address, simEndPoint.Port); |
||
1556 | this.proxy = proxy; |
||
1557 | socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); |
||
1558 | socket.Bind(new IPEndPoint(proxyConfig.clientFacingAddress, 0)); |
||
1559 | proxy.AddHandler(remoteEndPoint, this); |
||
1560 | Reset(); |
||
1561 | } |
||
1562 | |||
1563 | // Reset: start a new session |
||
1564 | public void Reset() |
||
1565 | { |
||
1566 | incomingSequence = 0; |
||
1567 | outgoingSequence = 0; |
||
1568 | incomingInjections = new List<uint>(); |
||
1569 | outgoingInjections = new List<uint>(); |
||
1570 | incomingAcks = new Dictionary<uint, Packet>(); |
||
1571 | outgoingAcks = new Dictionary<uint, Packet>(); |
||
1572 | incomingSeenAcks = new List<uint>(); |
||
1573 | outgoingSeenAcks = new List<uint>(); |
||
1574 | } |
||
1575 | |||
1576 | // BackgroundTasks: resend unacknowledged packets and keep data structures clean |
||
1577 | private void BackgroundTasks() |
||
1578 | { |
||
1579 | try |
||
1580 | { |
||
1581 | int tick = 1; |
||
1582 | int incomingInjectionsPoint = 0; |
||
1583 | int outgoingInjectionsPoint = 0; |
||
1584 | int incomingSeenAcksPoint = 0; |
||
1585 | int outgoingSeenAcksPoint = 0; |
||
1586 | |||
1587 | for (; ; Thread.Sleep(1000)) lock (proxy) |
||
1588 | { |
||
1589 | if ((tick = (tick + 1) % 60) == 0) |
||
1590 | { |
||
1591 | for (int i = 0; i < incomingInjectionsPoint; ++i) |
||
1592 | { |
||
1593 | incomingInjections.RemoveAt(0); |
||
1594 | ++incomingOffset; |
||
1595 | } |
||
1596 | incomingInjectionsPoint = incomingInjections.Count; |
||
1597 | |||
1598 | for (int i = 0; i < outgoingInjectionsPoint; ++i) |
||
1599 | { |
||
1600 | outgoingInjections.RemoveAt(0); |
||
1601 | ++outgoingOffset; |
||
1602 | } |
||
1603 | outgoingInjectionsPoint = outgoingInjections.Count; |
||
1604 | |||
1605 | for (int i = 0; i < incomingSeenAcksPoint; ++i) |
||
1606 | { |
||
1607 | incomingAcks.Remove(incomingSeenAcks[0]); |
||
1608 | incomingSeenAcks.RemoveAt(0); |
||
1609 | } |
||
1610 | incomingSeenAcksPoint = incomingSeenAcks.Count; |
||
1611 | |||
1612 | for (int i = 0; i < outgoingSeenAcksPoint; ++i) |
||
1613 | { |
||
1614 | outgoingAcks.Remove(outgoingSeenAcks[0]); |
||
1615 | outgoingSeenAcks.RemoveAt(0); |
||
1616 | } |
||
1617 | outgoingSeenAcksPoint = outgoingSeenAcks.Count; |
||
1618 | } |
||
1619 | |||
1620 | foreach (uint id in incomingAcks.Keys) |
||
1621 | if (!incomingSeenAcks.Contains(id)) |
||
1622 | { |
||
1623 | Packet packet = (Packet)incomingAcks[id]; |
||
1624 | packet.Header.Resent = true; |
||
1625 | SendPacket(packet, false); |
||
1626 | } |
||
1627 | |||
1628 | foreach (uint id in outgoingAcks.Keys) |
||
1629 | if (!outgoingSeenAcks.Contains(id)) |
||
1630 | { |
||
1631 | Packet packet = (Packet)outgoingAcks[id]; |
||
1632 | packet.Header.Resent = true; |
||
1633 | proxy.SendPacket(packet, remoteEndPoint, false); |
||
1634 | } |
||
1635 | } |
||
1636 | } |
||
1637 | catch (Exception e) |
||
1638 | { |
||
1639 | OpenMetaverse.Logger.Log("Exception running BackgroundTasks", Helpers.LogLevel.Error, e); |
||
1640 | } |
||
1641 | } |
||
1642 | |||
1643 | // LocalEndPoint: return the endpoint that the client should communicate with |
||
1644 | public IPEndPoint LocalEndPoint() |
||
1645 | { |
||
1646 | return (IPEndPoint)socket.LocalEndPoint; |
||
1647 | } |
||
1648 | |||
1649 | private byte[] receiveBuffer = new byte[8192]; |
||
1650 | private byte[] zeroBuffer = new byte[8192]; |
||
1651 | private EndPoint clientEndPoint = new IPEndPoint(IPAddress.Any, 0); |
||
1652 | bool firstReceive = true; |
||
1653 | |||
1654 | // Run: forward packets from the client to the sim |
||
1655 | public void Run() |
||
1656 | { |
||
1657 | Thread backgroundTasks = new Thread(new ThreadStart(BackgroundTasks)); |
||
1658 | backgroundTasks.IsBackground = true; |
||
1659 | backgroundTasks.Start(); |
||
1660 | socket.BeginReceiveFrom(receiveBuffer, 0, receiveBuffer.Length, SocketFlags.None, ref clientEndPoint, new AsyncCallback(ReceiveFromClient), null); |
||
1661 | } |
||
1662 | |||
1663 | // ReceiveFromClient: packet received from the client |
||
1664 | private void ReceiveFromClient(IAsyncResult ar) |
||
1665 | { |
||
1666 | lock (proxy) |
||
1667 | { |
||
1668 | try |
||
1669 | { |
||
1670 | // pause listening and fetch the packet |
||
1671 | bool needsZero = false; |
||
1672 | bool needsCopy = true; |
||
1673 | int length = 0; |
||
1674 | |||
1675 | try { length = socket.EndReceiveFrom(ar, ref clientEndPoint); } |
||
1676 | catch (SocketException) { } |
||
1677 | |||
1678 | if (length != 0) |
||
1679 | { |
||
1680 | // interpret the packet according to the SL protocol |
||
1681 | int end = length - 1; |
||
1682 | Packet packet = OpenMetaverse.Packets.Packet.BuildPacket(receiveBuffer, ref end, zeroBuffer); |
||
1683 | |||
1684 | //OpenMetaverse.Logger.Log("-> " + packet.Type + " #" + packet.Header.Sequence, Helpers.LogLevel.Debug); |
||
1685 | |||
1686 | // check for ACKs we're waiting for |
||
1687 | packet = CheckAcks(packet, Direction.Outgoing, ref length, ref needsCopy); |
||
1688 | |||
1689 | // modify sequence numbers to account for injections |
||
1690 | uint oldSequence = packet.Header.Sequence; |
||
1691 | packet = ModifySequence(packet, Direction.Outgoing, ref length, ref needsCopy); |
||
1692 | |||
1693 | // keep track of sequence numbers |
||
1694 | if (packet.Header.Sequence > outgoingSequence) |
||
1695 | outgoingSequence = packet.Header.Sequence; |
||
1696 | |||
1697 | // check the packet for addresses that need proxying |
||
1698 | if (proxy.outgoingCheckers.ContainsKey(packet.Type)) |
||
1699 | { |
||
1700 | /* if (packet.Header.Zerocoded) { |
||
1701 | length = Helpers.ZeroDecode(packet.Header.Data, length, zeroBuffer); |
||
1702 | packet.Header.Data = zeroBuffer; |
||
1703 | needsZero = false; |
||
1704 | } */ |
||
1705 | |||
1706 | Packet newPacket = ((AddressChecker)proxy.outgoingCheckers[packet.Type])(packet); |
||
1707 | SwapPacket(packet, newPacket); |
||
1708 | packet = newPacket; |
||
1709 | needsCopy = false; |
||
1710 | } |
||
1711 | |||
1712 | // pass the packet to any callback delegates |
||
1713 | if (proxy.outgoingDelegates.ContainsKey(packet.Type)) |
||
1714 | { |
||
1715 | if (packet.Header.AckList != null && needsCopy) |
||
1716 | { |
||
1717 | uint[] newAcks = new uint[packet.Header.AckList.Length]; |
||
1718 | Array.Copy(packet.Header.AckList, 0, newAcks, 0, newAcks.Length); |
||
1719 | packet.Header.AckList = newAcks; // FIXME |
||
1720 | } |
||
1721 | |||
1722 | try |
||
1723 | { |
||
1724 | Packet newPacket = proxy.callDelegates(proxy.outgoingDelegates, packet, remoteEndPoint); |
||
1725 | if (newPacket == null) |
||
1726 | { |
||
1727 | if (packet.Header.Reliable) |
||
1728 | Inject(proxy.SpoofAck(oldSequence), Direction.Incoming); |
||
1729 | |||
1730 | if (packet.Header.AppendedAcks) |
||
1731 | packet = proxy.SeparateAck(packet); |
||
1732 | else |
||
1733 | packet = null; |
||
1734 | } |
||
1735 | else |
||
1736 | { |
||
1737 | bool oldReliable = packet.Header.Reliable; |
||
1738 | bool newReliable = newPacket.Header.Reliable; |
||
1739 | if (oldReliable && !newReliable) |
||
1740 | Inject(proxy.SpoofAck(oldSequence), Direction.Incoming); |
||
1741 | else if (!oldReliable && newReliable) |
||
1742 | WaitForAck(packet, Direction.Outgoing); |
||
1743 | |||
1744 | SwapPacket(packet, newPacket); |
||
1745 | packet = newPacket; |
||
1746 | } |
||
1747 | } |
||
1748 | catch (Exception e) |
||
1749 | { |
||
1750 | OpenMetaverse.Logger.Log("exception in outgoing delegate", Helpers.LogLevel.Error, e); |
||
1751 | } |
||
1752 | |||
1753 | if (packet != null) |
||
1754 | proxy.SendPacket(packet, remoteEndPoint, false); |
||
1755 | } |
||
1756 | else |
||
1757 | proxy.SendPacket(packet, remoteEndPoint, needsZero); |
||
1758 | |||
1759 | // send any packets queued for injection |
||
1760 | if (firstReceive) |
||
1761 | { |
||
1762 | firstReceive = false; |
||
1763 | foreach (Packet queuedPacket in proxy.queuedIncomingInjections) |
||
1764 | Inject(queuedPacket, Direction.Incoming); |
||
1765 | proxy.queuedIncomingInjections = new List<Packet>(); |
||
1766 | } |
||
1767 | } |
||
1768 | } |
||
1769 | catch (Exception e) |
||
1770 | { |
||
1771 | OpenMetaverse.Logger.Log("Proxy error sending packet", Helpers.LogLevel.Error, e); |
||
1772 | } |
||
1773 | finally |
||
1774 | { |
||
1775 | // resume listening |
||
1776 | try |
||
1777 | { |
||
1778 | socket.BeginReceiveFrom(receiveBuffer, 0, receiveBuffer.Length, SocketFlags.None, |
||
1779 | ref clientEndPoint, new AsyncCallback(ReceiveFromClient), null); |
||
1780 | } |
||
1781 | catch (SocketException e) |
||
1782 | { |
||
1783 | OpenMetaverse.Logger.Log("Socket Shutdown: " + e.SocketErrorCode, Helpers.LogLevel.Warning); |
||
1784 | } |
||
1785 | } |
||
1786 | } |
||
1787 | } |
||
1788 | |||
1789 | // SendPacket: send a packet from the sim to the client via our fake sim endpoint |
||
1790 | public void SendPacket(Packet packet, bool skipZero) |
||
1791 | { |
||
1792 | byte[] buffer = packet.ToBytes(); |
||
1793 | if (skipZero || !packet.Header.Zerocoded) |
||
1794 | socket.SendTo(buffer, buffer.Length, SocketFlags.None, clientEndPoint); |
||
1795 | else |
||
1796 | { |
||
1797 | int zeroLength = Helpers.ZeroEncode(buffer, buffer.Length, zeroBuffer); |
||
1798 | socket.SendTo(zeroBuffer, zeroLength, SocketFlags.None, clientEndPoint); |
||
1799 | } |
||
1800 | } |
||
1801 | |||
1802 | // Inject: inject a packet |
||
1803 | public void Inject(Packet packet, Direction direction) |
||
1804 | { |
||
1805 | if (direction == Direction.Incoming) |
||
1806 | { |
||
1807 | if (firstReceive) |
||
1808 | { |
||
1809 | proxy.queuedIncomingInjections.Add(packet); |
||
1810 | return; |
||
1811 | } |
||
1812 | |||
1813 | incomingInjections.Add(++incomingSequence); |
||
1814 | packet.Header.Sequence = incomingSequence; |
||
1815 | } |
||
1816 | else |
||
1817 | { |
||
1818 | outgoingInjections.Add(++outgoingSequence); |
||
1819 | packet.Header.Sequence = outgoingSequence; |
||
1820 | } |
||
1821 | |||
1822 | if (packet.Header.Reliable) |
||
1823 | WaitForAck(packet, direction); |
||
1824 | |||
1825 | if (direction == Direction.Incoming) |
||
1826 | { |
||
1827 | byte[] buffer = packet.ToBytes(); |
||
1828 | if (!packet.Header.Zerocoded) |
||
1829 | socket.SendTo(buffer, buffer.Length, SocketFlags.None, clientEndPoint); |
||
1830 | else |
||
1831 | { |
||
1832 | int zeroLength = Helpers.ZeroEncode(buffer, buffer.Length, zeroBuffer); |
||
1833 | socket.SendTo(zeroBuffer, zeroLength, SocketFlags.None, clientEndPoint); |
||
1834 | } |
||
1835 | } |
||
1836 | else |
||
1837 | proxy.SendPacket(packet, remoteEndPoint, false); |
||
1838 | } |
||
1839 | |||
1840 | // WaitForAck: take care of resending a packet until it's ACKed |
||
1841 | public void WaitForAck(Packet packet, Direction direction) |
||
1842 | { |
||
1843 | Dictionary<uint, Packet> table = direction == Direction.Incoming ? incomingAcks : outgoingAcks; |
||
1844 | table.Add(packet.Header.Sequence, packet); |
||
1845 | } |
||
1846 | |||
1847 | // CheckAcks: check for and remove ACKs of packets we've injected |
||
1848 | public Packet CheckAcks(Packet packet, Direction direction, ref int length, ref bool needsCopy) |
||
1849 | { |
||
1850 | Dictionary<uint, Packet> acks = direction == Direction.Incoming ? outgoingAcks : incomingAcks; |
||
1851 | List<uint> seenAcks = direction == Direction.Incoming ? outgoingSeenAcks : incomingSeenAcks; |
||
1852 | |||
1853 | if (acks.Count == 0) |
||
1854 | return packet; |
||
1855 | |||
1856 | // check for embedded ACKs |
||
1857 | if (packet.Type == PacketType.PacketAck) |
||
1858 | { |
||
1859 | bool changed = false; |
||
1860 | List<PacketAckPacket.PacketsBlock> newPacketBlocks = new List<PacketAckPacket.PacketsBlock>(); |
||
1861 | foreach (PacketAckPacket.PacketsBlock pb in ((PacketAckPacket)packet).Packets) |
||
1862 | { |
||
1863 | uint id = pb.ID; |
||
1864 | if (acks.ContainsKey(id)) |
||
1865 | { |
||
1866 | acks.Remove(id); |
||
1867 | seenAcks.Add(id); |
||
1868 | changed = true; |
||
1869 | } |
||
1870 | else |
||
1871 | { |
||
1872 | newPacketBlocks.Add(pb); |
||
1873 | } |
||
1874 | } |
||
1875 | if (changed) |
||
1876 | { |
||
1877 | PacketAckPacket newPacket = new PacketAckPacket(); |
||
1878 | newPacket.Packets = new PacketAckPacket.PacketsBlock[newPacketBlocks.Count]; |
||
1879 | |||
1880 | int a = 0; |
||
1881 | foreach (PacketAckPacket.PacketsBlock pb in newPacketBlocks) |
||
1882 | { |
||
1883 | newPacket.Packets[a++] = pb; |
||
1884 | } |
||
1885 | |||
1886 | SwapPacket(packet, (Packet)newPacket); |
||
1887 | packet = newPacket; |
||
1888 | needsCopy = false; |
||
1889 | } |
||
1890 | } |
||
1891 | |||
1892 | // check for appended ACKs |
||
1893 | if (packet.Header.AppendedAcks) |
||
1894 | { |
||
1895 | int ackCount = packet.Header.AckList.Length; |
||
1896 | for (int i = 0; i < ackCount; ) |
||
1897 | { |
||
1898 | uint ackID = packet.Header.AckList[i]; // FIXME FIXME FIXME |
||
1899 | |||
1900 | if (acks.ContainsKey(ackID)) |
||
1901 | { |
||
1902 | uint[] newAcks = new uint[ackCount - 1]; |
||
1903 | Array.Copy(packet.Header.AckList, 0, newAcks, 0, i); |
||
1904 | Array.Copy(packet.Header.AckList, i + 1, newAcks, i, ackCount - i - 1); |
||
1905 | packet.Header.AckList = newAcks; |
||
1906 | --ackCount; |
||
1907 | acks.Remove(ackID); |
||
1908 | seenAcks.Add(ackID); |
||
1909 | needsCopy = false; |
||
1910 | } |
||
1911 | else |
||
1912 | ++i; |
||
1913 | } |
||
1914 | if (ackCount == 0) |
||
1915 | { |
||
1916 | packet.Header.AppendedAcks = false; |
||
1917 | packet.Header.AckList = new uint[0]; |
||
1918 | } |
||
1919 | } |
||
1920 | |||
1921 | return packet; |
||
1922 | } |
||
1923 | |||
1924 | // ModifySequence: modify a packet's sequence number and ACK IDs to account for injections |
||
1925 | public Packet ModifySequence(Packet packet, Direction direction, ref int length, ref bool needsCopy) |
||
1926 | { |
||
1927 | List<uint> ourInjections = direction == Direction.Outgoing ? outgoingInjections : incomingInjections; |
||
1928 | List<uint> theirInjections = direction == Direction.Incoming ? outgoingInjections : incomingInjections; |
||
1929 | uint ourOffset = direction == Direction.Outgoing ? outgoingOffset : incomingOffset; |
||
1930 | uint theirOffset = direction == Direction.Incoming ? outgoingOffset : incomingOffset; |
||
1931 | |||
1932 | uint newSequence = (uint)(packet.Header.Sequence + ourOffset); |
||
1933 | foreach (uint injection in ourInjections) |
||
1934 | if (newSequence >= injection) |
||
1935 | ++newSequence; |
||
1936 | |||
1937 | packet.Header.Sequence = newSequence; |
||
1938 | |||
1939 | if (packet.Header.AppendedAcks) |
||
1940 | { |
||
1941 | int ackCount = packet.Header.AckList.Length; |
||
1942 | for (int i = 0; i < ackCount; ++i) |
||
1943 | { |
||
1944 | //int offset = length - (ackCount - i) * 4 - 1; |
||
1945 | uint ackID = packet.Header.AckList[i] - theirOffset; |
||
1946 | |||
1947 | for (int j = theirInjections.Count - 1; j >= 0; --j) |
||
1948 | if (ackID >= (uint)theirInjections[j]) |
||
1949 | --ackID; |
||
1950 | |||
1951 | packet.Header.AckList[i] = ackID; |
||
1952 | } |
||
1953 | } |
||
1954 | |||
1955 | if (packet.Type == PacketType.PacketAck) |
||
1956 | { |
||
1957 | PacketAckPacket pap = (PacketAckPacket)packet; |
||
1958 | foreach (PacketAckPacket.PacketsBlock pb in pap.Packets) |
||
1959 | { |
||
1960 | uint ackID = (uint)pb.ID - theirOffset; |
||
1961 | |||
1962 | for (int i = theirInjections.Count - 1; i >= 0; --i) |
||
1963 | if (ackID >= (uint)theirInjections[i]) |
||
1964 | --ackID; |
||
1965 | |||
1966 | pb.ID = ackID; |
||
1967 | |||
1968 | } |
||
1969 | |||
1970 | switch (packet.Header.Frequency) |
||
1971 | { |
||
1972 | case PacketFrequency.High: length = 7; break; |
||
1973 | case PacketFrequency.Medium: length = 8; break; |
||
1974 | case PacketFrequency.Low: length = 10; break; |
||
1975 | } |
||
1976 | |||
1977 | needsCopy = false; |
||
1978 | } |
||
1979 | |||
1980 | return packet; |
||
1981 | } |
||
1982 | } |
||
1983 | |||
1984 | // Checkers swap proxy addresses in for real addresses. A few constraints: |
||
1985 | // - Checkers must not alter the incoming packet. |
||
1986 | // - Checkers must return a freshly built packet, even if nothing's changed. |
||
1987 | // - The incoming packet's buffer may be longer than the length of the data it contains. |
||
1988 | // - The incoming packet's buffer must not be used after the checker returns. |
||
1989 | // This is all because checkers may be operating on data that's still in a scratch buffer. |
||
1990 | delegate Packet AddressChecker(Packet packet); |
||
1991 | |||
1992 | Dictionary<PacketType, AddressChecker> incomingCheckers = new Dictionary<PacketType, AddressChecker>(); |
||
1993 | Dictionary<PacketType, AddressChecker> outgoingCheckers = new Dictionary<PacketType, AddressChecker>(); |
||
1994 | |||
1995 | // InitializeAddressCheckers: initialize delegates that check packets for addresses that need proxying |
||
1996 | private void InitializeAddressCheckers() |
||
1997 | { |
||
1998 | // TODO: what do we do with mysteries and empty IPs? |
||
1999 | AddMystery(PacketType.OpenCircuit); |
||
2000 | //AddMystery(PacketType.AgentPresenceResponse); |
||
2001 | |||
2002 | incomingCheckers.Add(PacketType.TeleportFinish, new AddressChecker(CheckTeleportFinish)); |
||
2003 | incomingCheckers.Add(PacketType.CrossedRegion, new AddressChecker(CheckCrossedRegion)); |
||
2004 | incomingCheckers.Add(PacketType.EnableSimulator, new AddressChecker(CheckEnableSimulator)); |
||
2005 | //incomingCheckers.Add("UserLoginLocationReply", new AddressChecker(CheckUserLoginLocationReply)); |
||
2006 | } |
||
2007 | |||
2008 | // AddMystery: add a checker delegate that logs packets we're watching for development purposes |
||
2009 | private void AddMystery(PacketType type) |
||
2010 | { |
||
2011 | incomingCheckers.Add(type, new AddressChecker(LogIncomingMysteryPacket)); |
||
2012 | outgoingCheckers.Add(type, new AddressChecker(LogOutgoingMysteryPacket)); |
||
2013 | } |
||
2014 | |||
2015 | // GenericCheck: replace the sim address in a packet with our proxy address |
||
2016 | private void GenericCheck(ref uint simIP, ref ushort simPort, ref string simCaps, bool active) |
||
2017 | { |
||
2018 | IPAddress sim_ip = new IPAddress((long)simIP); |
||
2019 | |||
2020 | IPEndPoint realSim = new IPEndPoint(sim_ip, Convert.ToInt32(simPort)); |
||
2021 | IPEndPoint fakeSim = ProxySim(realSim); |
||
2022 | |||
2023 | simPort = (ushort)fakeSim.Port; |
||
2024 | byte[] bytes = fakeSim.Address.GetAddressBytes(); |
||
2025 | simIP = Utils.BytesToUInt(bytes); |
||
2026 | if (simCaps != null && simCaps.Length > 0) |
||
2027 | { |
||
2028 | CapInfo info = new CapInfo(simCaps, realSim, "SeedCapability"); |
||
2029 | info.AddDelegate(new CapsDelegate(FixupSeedCapsResponse)); |
||
2030 | lock (this) |
||
2031 | { |
||
2032 | KnownCaps[simCaps] = info; |
||
2033 | } |
||
2034 | simCaps = loginURI + simCaps; |
||
2035 | } |
||
2036 | |||
2037 | if (active) |
||
2038 | activeCircuit = realSim; |
||
2039 | } |
||
2040 | |||
2041 | // CheckTeleportFinish: check TeleportFinish packets |
||
2042 | private Packet CheckTeleportFinish(Packet packet) |
||
2043 | { |
||
2044 | TeleportFinishPacket tfp = (TeleportFinishPacket)packet; |
||
2045 | string simCaps = Encoding.UTF8.GetString(tfp.Info.SeedCapability).Replace("\0", ""); |
||
2046 | GenericCheck(ref tfp.Info.SimIP, ref tfp.Info.SimPort, ref simCaps, true); |
||
2047 | tfp.Info.SeedCapability = Utils.StringToBytes(simCaps); |
||
2048 | return (Packet)tfp; |
||
2049 | } |
||
2050 | |||
2051 | // CheckEnableSimulator: check EnableSimulator packets |
||
2052 | private Packet CheckEnableSimulator(Packet packet) |
||
2053 | { |
||
2054 | EnableSimulatorPacket esp = (EnableSimulatorPacket)packet; |
||
2055 | string simCaps = null; |
||
2056 | GenericCheck(ref esp.SimulatorInfo.IP, ref esp.SimulatorInfo.Port, ref simCaps, false); |
||
2057 | return (Packet)esp; |
||
2058 | } |
||
2059 | |||
2060 | // CheckCrossedRegion: check CrossedRegion packets |
||
2061 | private Packet CheckCrossedRegion(Packet packet) |
||
2062 | { |
||
2063 | CrossedRegionPacket crp = (CrossedRegionPacket)packet; |
||
2064 | string simCaps = Encoding.UTF8.GetString(crp.RegionData.SeedCapability).Replace("\0", ""); |
||
2065 | GenericCheck(ref crp.RegionData.SimIP, ref crp.RegionData.SimPort, ref simCaps, true); |
||
2066 | crp.RegionData.SeedCapability = Utils.StringToBytes(simCaps); |
||
2067 | return (Packet)crp; |
||
2068 | } |
||
2069 | |||
2070 | // LogPacket: log a packet dump |
||
2071 | private Packet LogPacket(Packet packet, string type) |
||
2072 | { |
||
2073 | OpenMetaverse.Logger.Log(type + " packet:\n" + packet, Helpers.LogLevel.Info); |
||
2074 | return packet; |
||
2075 | } |
||
2076 | |||
2077 | // LogIncomingMysteryPacket: log an incoming packet we're watching for development purposes |
||
2078 | private Packet LogIncomingMysteryPacket(Packet packet) |
||
2079 | { |
||
2080 | return LogPacket(packet, "incoming mystery"); |
||
2081 | } |
||
2082 | |||
2083 | // LogOutgoingMysteryPacket: log an outgoing packet we're watching for development purposes |
||
2084 | private Packet LogOutgoingMysteryPacket(Packet packet) |
||
2085 | { |
||
2086 | return LogPacket(packet, "outgoing mystery"); |
||
2087 | } |
||
2088 | |||
2089 | public void AddLoginRequestDelegate(XmlRpcRequestDelegate xmlRpcRequestDelegate) |
||
2090 | { |
||
2091 | lock (loginRequestDelegates) |
||
2092 | if (!loginRequestDelegates.Contains(xmlRpcRequestDelegate)) |
||
2093 | loginRequestDelegates.Add(xmlRpcRequestDelegate); |
||
2094 | |||
2095 | } |
||
2096 | |||
2097 | public void AddLoginResponseDelegate(XmlRpcResponseDelegate xmlRpcResponseDelegate) |
||
2098 | { |
||
2099 | lock (loginResponseDelegates) |
||
2100 | if (!loginResponseDelegates.Contains(xmlRpcResponseDelegate)) |
||
2101 | loginResponseDelegates.Add(xmlRpcResponseDelegate); |
||
2102 | } |
||
2103 | } |
||
2104 | |||
2105 | |||
2106 | // Describes the data format of a capability |
||
2107 | public enum CapsDataFormat |
||
2108 | { |
||
2109 | Binary = 0, |
||
2110 | OSD = 1 |
||
2111 | } |
||
2112 | |||
2113 | // Describes a caps URI |
||
2114 | public class CapInfo |
||
2115 | { |
||
2116 | private string uri; |
||
2117 | private IPEndPoint sim; |
||
2118 | private string type; |
||
2119 | private CapsDataFormat reqFmt; |
||
2120 | private CapsDataFormat respFmt; |
||
2121 | |||
2122 | private List<CapsDelegate> Delegates = new List<CapsDelegate>(); |
||
2123 | |||
2124 | |||
2125 | public CapInfo(string URI, IPEndPoint Sim, string CapType) |
||
2126 | : |
||
2127 | this(URI, Sim, CapType, CapsDataFormat.OSD, CapsDataFormat.OSD) { } |
||
2128 | public CapInfo(string URI, IPEndPoint Sim, string CapType, CapsDataFormat ReqFmt, CapsDataFormat RespFmt) |
||
2129 | { |
||
2130 | uri = URI; sim = Sim; type = CapType; reqFmt = ReqFmt; respFmt = RespFmt; |
||
2131 | } |
||
2132 | public string URI |
||
2133 | { |
||
2134 | get { return uri; } |
||
2135 | } |
||
2136 | public string CapType |
||
2137 | { |
||
2138 | get { return type; } /* EventQueueGet, etc */ |
||
2139 | } |
||
2140 | public IPEndPoint Sim |
||
2141 | { |
||
2142 | get { return sim; } |
||
2143 | } |
||
2144 | public CapsDataFormat ReqFmt |
||
2145 | { |
||
2146 | get { return reqFmt; } /* expected request format */ |
||
2147 | } |
||
2148 | public CapsDataFormat RespFmt |
||
2149 | { |
||
2150 | get { return respFmt; } /* expected response format */ |
||
2151 | } |
||
2152 | |||
2153 | public void AddDelegate(CapsDelegate deleg) |
||
2154 | { |
||
2155 | lock (this) |
||
2156 | { |
||
2157 | if (!Delegates.Contains(deleg)) |
||
2158 | { |
||
2159 | Delegates.Add(deleg); |
||
2160 | } |
||
2161 | } |
||
2162 | } |
||
2163 | public void RemoveDelegate(CapsDelegate deleg) |
||
2164 | { |
||
2165 | lock (this) |
||
2166 | { |
||
2167 | if (Delegates.Contains(deleg)) |
||
2168 | { |
||
2169 | Delegates.Remove(deleg); |
||
2170 | } |
||
2171 | } |
||
2172 | } |
||
2173 | |||
2174 | // inefficient, but avoids potential deadlocks. |
||
2175 | public List<CapsDelegate> GetDelegates() |
||
2176 | { |
||
2177 | lock (this) |
||
2178 | { |
||
2179 | return new List<CapsDelegate>(Delegates); |
||
2180 | } |
||
2181 | } |
||
2182 | } |
||
2183 | |||
2184 | // Information associated with a caps request/response |
||
2185 | public class CapsRequest |
||
2186 | { |
||
2187 | public CapsRequest(CapInfo info) |
||
2188 | { |
||
2189 | Info = info; |
||
2190 | } |
||
2191 | |||
2192 | public readonly CapInfo Info; |
||
2193 | |||
2194 | // The request |
||
2195 | public OSD Request = null; |
||
2196 | |||
2197 | // The corresponding response |
||
2198 | public OSD Response = null; |
||
2199 | |||
2200 | public byte[] RawRequest = null; |
||
2201 | public byte[] RawResponse = null; |
||
2202 | |||
2203 | public WebHeaderCollection RequestHeaders = new WebHeaderCollection(); |
||
2204 | public WebHeaderCollection ResponseHeaders = new WebHeaderCollection(); |
||
2205 | |||
2206 | public string FullUri = string.Empty; |
||
2207 | |||
2208 | } |
||
2209 | |||
2210 | // XmlRpcRequestDelegate: specifies a delegate to be called for XML-RPC requests |
||
2211 | public delegate void XmlRpcRequestDelegate(object sender, XmlRpcRequestEventArgs e); |
||
2212 | |||
2213 | // XmlRpcResponseDelegate: specifies a delegate to be called for XML-RPC responses |
||
2214 | public delegate void XmlRpcResponseDelegate(XmlRpcResponse response); |
||
2215 | |||
2216 | // PacketDelegate: specifies a delegate to be called when a packet passes through the proxy |
||
2217 | public delegate Packet PacketDelegate(Packet packet, IPEndPoint endPoint); |
||
2218 | |||
2219 | // Delegate for a caps request. Generally called twice - first with stage = CapsStage.Request |
||
2220 | // before the request is sent, then with stage = CapsStage.Response when the response is |
||
2221 | // received. Returning true causes all the subsequent delegates in that stage to be skipped, |
||
2222 | // and in the case of CapsStage.Request also prevents the request being forwarded. In this |
||
2223 | // case, you should set req.Response to the response you want to return. |
||
2224 | // Can modify req.Request and req.Response, with the expected effects. |
||
2225 | public delegate bool CapsDelegate(CapsRequest req, CapsStage stage); |
||
2226 | |||
2227 | // Direction: specifies whether a packet is going to the client (Incoming) or to a sim (Outgoing) |
||
2228 | public enum Direction |
||
2229 | { |
||
2230 | Incoming, |
||
2231 | Outgoing |
||
2232 | } |
||
2233 | public enum CapsStage |
||
2234 | { |
||
2235 | Request, |
||
2236 | Response |
||
2237 | } |
||
2238 | |||
2239 | public class XmlRpcRequestEventArgs : EventArgs |
||
2240 | { |
||
2241 | public XmlRpcRequest m_Request; |
||
2242 | |||
2243 | public XmlRpcRequestEventArgs(XmlRpcRequest request) |
||
2244 | { |
||
2245 | this.m_Request = request; |
||
2246 | } |
||
2247 | } |
||
2248 | } |
||
2249 | |||
2250 |