Upstream tarball 20080304
[amule.git] / src / libs / ec / c# / amuleRemote.cs
blobce65680688b05dcf648a82ea30681bc51c209dd2
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2003-2008 lfroen ( lfroen@gmail.com / http://www.amule.org )
6 //
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
9 // respective authors.
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 using System;
26 using System.IO;
27 using System.Security;
28 using System.Security.Permissions;
29 using System.Threading;
30 using System.Collections.Generic;
31 using System.Text;
32 using System.Net;
33 using System.Net.Sockets;
35 namespace amule.net
38 // Define the state object for the callback.
39 // Use hostName to correlate calls with the proper result.
40 public class ResolveState {
41 IPHostEntry resolvedIPs;
43 public string errorMsg;
45 public IPHostEntry IPs
47 get { return resolvedIPs; }
48 set { resolvedIPs = value; }
52 public class ConnectState {
53 public Socket sock;
54 public string errorMsg;
56 public ConnectState(Socket s)
58 sock = s;
62 public abstract class amuleECHandler {
63 public amuleECHandler()
67 public abstract void HandlePacket(ecProto.ecPacket packet);
70 class amuleLogicHandler : amuleECHandler {
71 amuleRemote m_owner;
72 bool m_auth_result = false;
74 public amuleLogicHandler(amuleRemote o)
76 m_owner = o;
79 public override void HandlePacket(ecProto.ecPacket packet)
81 if ( packet.Opcode() == ECOpCodes.EC_OP_AUTH_OK ) {
82 Console.WriteLine("amuleLogicHandler : Authenticated OK");
83 m_auth_result = true;
84 } else {
85 Console.WriteLine("amuleLogicHandler : Authentication failed. Core reply was {0}", packet.Opcode());
89 public bool AuthResult()
91 return m_auth_result;
95 class amuleRemote {
96 amuleECHandler m_handler = null;
98 static ManualResetEvent m_socket_op_Done = new ManualResetEvent(false);
100 // Record the IPs in the state object for later use.
101 static void GetHostEntryCallback(IAsyncResult ar)
103 ResolveState ioContext = (ResolveState)ar.AsyncState;
104 try {
105 ioContext.IPs = Dns.EndGetHostEntry(ar);
106 } catch (SocketException e) {
107 ioContext.errorMsg = e.Message;
109 m_socket_op_Done.Set();
112 Socket m_s;
114 static void ConnectCallback(IAsyncResult ar)
116 // Retrieve the socket from the state object.
117 ConnectState state = (ConnectState)ar.AsyncState;
118 try {
119 // Complete the connection.
120 state.sock.EndConnect(ar);
122 Console.WriteLine("Socket connected to {0}",
123 state.sock.RemoteEndPoint.ToString());
125 // Signal that the connection has been made.
126 m_socket_op_Done.Set();
127 } catch (Exception e) {
128 state.errorMsg = e.Message;
132 byte[] m_rx_buffer = new byte[32 * 1024];
133 MemoryStream m_rx_mem_stream = null;
134 int m_rx_byte_count = 0;
135 int m_rx_remaining_count = 0;
137 byte[] m_tx_buffer = new byte[32 * 1024];
138 MemoryStream m_tx_mem_stream = null;
140 //LinkedList<byte[]> m_tx_queue;
141 BinaryReader m_sock_reader = null;
142 BinaryWriter m_sock_writer = null;
144 static void RxCallback(IAsyncResult ar)
146 amuleRemote o = (amuleRemote)ar.AsyncState;
147 Console.WriteLine("RxCallback signalled, calling EndReceive");
148 int bytesRead = o.m_s.EndReceive(ar);
149 if ( bytesRead == 0 ) {
150 // remote side closed connection.
151 // indicate error to caller
152 o.m_rx_byte_count = -1;
153 m_socket_op_Done.Set();
154 return;
156 o.m_rx_remaining_count -= bytesRead;
157 Console.WriteLine("RxCallback: got {0} bytes, waiting for {1}",
158 bytesRead, o.m_rx_remaining_count);
159 // are we still waiting for flags and size?
160 if (o.m_rx_byte_count < 8) {
161 if ((o.m_rx_byte_count + bytesRead) >= 8) {
162 // got flags and packet size - may proceed.
163 Int32 flags = o.m_sock_reader.ReadInt32();
164 Int32 val32 = o.m_sock_reader.ReadInt32();
166 o.m_rx_remaining_count = (int)IPAddress.NetworkToHostOrder(val32) - (bytesRead - 8);
167 Console.WriteLine("RxCallback: expecting packet size={0}", o.m_rx_remaining_count);
168 if ( o.m_rx_buffer.Length <= (o.m_rx_remaining_count+o.m_rx_byte_count) ) {
169 byte [] new_buffer = new byte[o.m_rx_remaining_count + o.m_rx_buffer.Length + 1];
170 o.m_rx_buffer.CopyTo(new_buffer, 0);
171 o.m_rx_buffer = new_buffer;
173 // update stream reader with new buffer
175 o.m_rx_mem_stream = new MemoryStream(o.m_rx_buffer);
176 o.m_sock_reader = new BinaryReader(o.m_rx_mem_stream);
179 } else {
180 if ( o.m_rx_remaining_count == 0 ) {
182 // Packet received - call handler
184 if ( o.m_handler != null ) {
185 o.m_rx_mem_stream.Seek(0, SeekOrigin.Begin);
186 Console.WriteLine("Packet received - call handler\n");
187 ecProto.ecPacket p = new ecProto.ecPacket(o.m_sock_reader);
188 o.m_handler.HandlePacket(p);
190 m_socket_op_Done.Set();
192 // Keep waiting for more packets
194 o.StartReceive();
195 return;
198 o.m_rx_byte_count += bytesRead;
200 // not just yet - keep waiting
201 o.m_s.BeginReceive(o.m_rx_buffer, o.m_rx_byte_count, o.m_rx_remaining_count,
202 SocketFlags.None, new AsyncCallback(RxCallback), o);
205 static void TxCallback(IAsyncResult ar)
207 amuleRemote o = (amuleRemote)ar.AsyncState;
208 Console.WriteLine("TxCallback signalled, calling EndWrite");
209 o.m_s.EndSend(ar);
210 m_socket_op_Done.Set();
213 public bool SendPacket(ecProto.ecPacket packet)
215 m_tx_mem_stream.Seek(0, SeekOrigin.Begin);
216 packet.Write(m_sock_writer);
218 m_socket_op_Done.Reset();
219 m_s.BeginSend(m_tx_buffer, 0, packet.PacketSize(),
220 SocketFlags.None, new AsyncCallback(TxCallback), this);
222 return true;
224 public void StartReceive()
226 m_socket_op_Done.Reset();
227 // reply packet is supposed to have at least 8 bytes
228 m_rx_remaining_count = 8;
229 m_rx_byte_count = 0;
230 m_rx_mem_stream.Seek(0, SeekOrigin.Begin);
231 m_s.BeginReceive(m_rx_buffer, 0, 8, SocketFlags.None, new AsyncCallback(RxCallback), this);
234 [DnsPermission(SecurityAction.Demand, Unrestricted = true)]
235 public bool ConnectToCore(string host, int port, string pass, ref string error)
237 try {
238 m_socket_op_Done.Reset();
239 ResolveState resolveContext = new ResolveState();
240 Dns.BeginGetHostEntry(host,
241 new AsyncCallback(GetHostEntryCallback), resolveContext);
243 // Wait here until the resolve completes (the callback calls .Set())
244 m_socket_op_Done.WaitOne();
245 if ( resolveContext.IPs == null ) {
246 error = resolveContext.errorMsg;
247 return false;
249 Console.WriteLine("Resolved: '{0}' -> '{1}", host,resolveContext.IPs.AddressList[0]);
250 Console.WriteLine("Connecting to {0}:{1}", resolveContext.IPs.AddressList[0], port);
251 IPEndPoint remoteEP = new IPEndPoint(resolveContext.IPs.AddressList[0], port);
252 m_s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
254 m_socket_op_Done.Reset();
255 ConnectState connectContext = new ConnectState(m_s);
256 m_s.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), connectContext);
257 if ( !m_socket_op_Done.WaitOne(1000,true) ) {
258 error = "Timeout during connection. Possible firewall";
259 return false;
262 if ( connectContext.errorMsg != null) {
263 error = connectContext.errorMsg;
264 return false;
267 m_tx_mem_stream = new MemoryStream(m_tx_buffer);
268 m_sock_writer = new BinaryWriter(m_tx_mem_stream);
269 m_rx_mem_stream = new MemoryStream(m_rx_buffer);
270 m_sock_reader = new BinaryReader(m_rx_mem_stream);
272 ecProto.ecLoginPacket p = new ecProto.ecLoginPacket("amule.net", "0.0.1", pass);
273 SendPacket(p);
275 if (!m_socket_op_Done.WaitOne(1000, true)) {
276 // Was unable to send login request for 1sec. Line must be really slow
277 return false;
280 m_handler = new amuleLogicHandler(this);
281 StartReceive();
283 // FIXME: must be able to cancel this read.
284 m_socket_op_Done.WaitOne();
285 if ( m_rx_byte_count == -1 ) {
286 // remote side terminated connection
287 Console.WriteLine("Connection terminated on remote side");
289 Console.WriteLine("Connect done");
290 bool result = ((amuleLogicHandler)m_handler).AuthResult();
291 m_handler = null;
292 return result;
293 } catch (Exception e) {
294 error = e.Message;
295 return false;
299 public void SetECHandler(amuleECHandler h)
301 m_handler = h;