PageRenderTime 65ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/otp.net/Otp/AbstractConnection.cs

https://github.com/babo/jungerl
C# | 1469 lines | 1035 code | 199 blank | 235 comment | 114 complexity | 479e9ac440b6226e71ed20121e215265 MD5 | raw file
Possible License(s): LGPL-2.1, BSD-3-Clause, AGPL-1.0

Large files files are truncated, but you can click here to view the full file

  1. /*``The contents of this file are subject to the Erlang Public License,
  2. * Version 1.1, (the "License"); you may not use this file except in
  3. * compliance with the License. You should have received a copy of the
  4. * Erlang Public License along with this software. If not, it can be
  5. * retrieved via the world wide web at http://www.erlang.org/.
  6. *
  7. * Software distributed under the License is distributed on an "AS IS"
  8. * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  9. * the License for the specific language governing rights and limitations
  10. * under the License.
  11. *
  12. * The Initial Developer of the Original Code is Ericsson Utvecklings AB.
  13. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
  14. * AB. All Rights Reserved.''
  15. *
  16. * Converted from Java to C# by Vlad Dumitrescu (vlad_Dumitrescu@hotmail.com)
  17. */
  18. using System;
  19. using System.Diagnostics;
  20. namespace Otp
  21. {
  22. /*
  23. * Maintains a connection between a C# process and a remote Erlang,
  24. * C# or C node. The object maintains connection state and allows
  25. * data to be sent to and received from the peer.
  26. *
  27. * <p> This abstract class provides the neccesary methods to maintain
  28. * the actual connection and encode the messages and headers in the
  29. * proper format according to the Erlang distribution protocol.
  30. * Subclasses can use these methods to provide a more or less
  31. * transparent communication channel as desired. </p>
  32. *
  33. * <p> Note that no receive methods are provided. Subclasses must
  34. * provide methods for message delivery, and may implement their own
  35. * receive methods. <p>
  36. *
  37. * <p> If an exception occurs in any of the methods in this class, the
  38. * connection will be closed and must be reopened in order to resume
  39. * communication with the peer. This will be indicated to the subclass
  40. * by passing the exception to its delivery() method. </p>
  41. *
  42. * <p> The System property OtpConnection.trace can be used to change
  43. * the initial trace level setting for all connections. Normally the
  44. * initial trace level is 0 and connections are not traced unless
  45. * {@link #setTraceLevel setTraceLevel()} is used to change the
  46. * setting for a particular connection. OtpConnection.trace can be
  47. * used to turn on tracing by default for all connections. </p>
  48. **/
  49. public abstract class AbstractConnection
  50. {
  51. static AbstractConnection()
  52. {
  53. {
  54. // trace this connection?
  55. System.String trace = "0"; // value "0" to "4"
  56. try
  57. {
  58. if (trace != null)
  59. defaultLevel = System.Int32.Parse(trace);
  60. }
  61. catch (System.FormatException)
  62. {
  63. defaultLevel = 0;
  64. }
  65. random = new System.Random();
  66. }
  67. }
  68. protected internal const int headerLen = 2048; // more than enough
  69. protected internal static readonly byte passThrough = 0x70;
  70. protected internal static readonly byte version = 0x83;
  71. // Erlang message header tags
  72. protected internal const int linkTag = 1;
  73. protected internal const int sendTag = 2;
  74. protected internal const int exitTag = 3;
  75. protected internal const int unlinkTag = 4;
  76. protected internal const int nodeLinkTag = 5;
  77. protected internal const int regSendTag = 6;
  78. protected internal const int groupLeaderTag = 7;
  79. protected internal const int exit2Tag = 8;
  80. protected internal const int sendTTTag = 12;
  81. protected internal const int exitTTTag = 13;
  82. protected internal const int regSendTTTag = 16;
  83. protected internal const int exit2TTTag = 18;
  84. // MD5 challenge messsage tags
  85. protected internal const int ChallengeReply = 'r';
  86. protected internal const int ChallengeAck = 'a';
  87. protected internal const int ChallengeStatus = 's';
  88. private bool done = false;
  89. protected internal bool connected = false; // connection status
  90. protected internal System.Net.Sockets.TcpClient socket; // communication channel
  91. protected internal OtpPeer peer; // who are we connected to
  92. protected internal OtpLocalNode self; // this nodes id
  93. internal System.String name; // local name of this connection
  94. protected internal bool cookieOk = false; // already checked the cookie for this connection
  95. protected internal bool sendCookie = true; // Send cookies in messages?
  96. protected internal System.Threading.Thread thread;
  97. // tracelevel constants
  98. protected internal static int defaultLevel = 0;
  99. protected internal static int sendThreshold = 1;
  100. protected internal static int ctrlThreshold = 2;
  101. protected internal static int handshakeThreshold = 3;
  102. protected internal int _traceLevel;
  103. public int traceLevel
  104. {
  105. get
  106. {
  107. return _traceLevel;
  108. }
  109. /*
  110. * <p> Set the trace level for this connection. Normally tracing is
  111. * off by default unless System property OtpConnection.trace was
  112. * set. </p>
  113. *
  114. * <p> The following levels are valid: 0 turns off tracing
  115. * completely, 1 shows ordinary send and receive messages, 2 shows
  116. * control messages such as link and unlink, 3 shows handshaking at
  117. * connection setup, and 4 shows communication with Epmd. Each level
  118. * includes the information shown by the lower ones. </p>
  119. *
  120. **/
  121. set
  122. {
  123. int oldLevel = _traceLevel;
  124. // pin the value
  125. if (value < 0)
  126. _traceLevel = 0;
  127. else if (value > 4)
  128. _traceLevel = 4;
  129. else
  130. _traceLevel = value;
  131. }
  132. }
  133. protected internal static System.Random random = null;
  134. /*
  135. * Accept an incoming connection from a remote node. Used by {@link
  136. * OtpSelf#accept() OtpSelf.accept()} to create a connection
  137. * based on data received when handshaking with the peer node, when
  138. * the remote node is the connection intitiator.
  139. *
  140. * @exception C#.io.IOException if it was not possible to connect to the peer.
  141. * @exception OtpAuthException if handshake resulted in an authentication error
  142. */
  143. protected internal AbstractConnection(OtpLocalNode self, System.Net.Sockets.TcpClient s)
  144. {
  145. this.self = self;
  146. this.peer = new OtpPeer();
  147. this.socket = s;
  148. this.socket.NoDelay = true;
  149. this.traceLevel = defaultLevel;
  150. if (traceLevel >= handshakeThreshold)
  151. {
  152. System.Console.Out.WriteLine("<- ACCEPT FROM ?");
  153. /*
  154. System.Net.Sockets.Socket sock = s.Client;
  155. //UPGRADE_TODO: Expression C#.net.Socket.getInetAddress could not be converted.;
  156. //UPGRADE_TODO: Expression C#.net.Socket.getPort could not be converted.;
  157. System.Console.Out.WriteLine("<- ACCEPT FROM " +
  158. IPAddress.Parse(((IPEndPoint)s.RemoteEndPoint).Address.ToString())+ ":" +
  159. ((IPEndPoint)s.RemoteEndPoint).Port.ToString());
  160. */
  161. }
  162. // get his info
  163. recvName(this.peer);
  164. // now find highest common dist value
  165. if ((peer._proto != self._proto) || (self._distHigh < peer._distLow) || (self._distLow > peer._distHigh))
  166. {
  167. close();
  168. throw new System.IO.IOException("No common protocol found - cannot accept connection");
  169. }
  170. // highest common version: min(peer.distHigh, self.distHigh)
  171. peer.distChoose = (peer._distHigh > self._distHigh?self._distHigh:peer._distHigh);
  172. doAccept();
  173. this.name = peer.node();
  174. }
  175. /*
  176. * Intiate and open a connection to a remote node.
  177. *
  178. * @exception C#.io.IOException if it was not possible to connect to the peer.
  179. * @exception OtpAuthException if handshake resulted in an authentication error.
  180. */
  181. protected internal AbstractConnection(OtpLocalNode self, OtpPeer other)
  182. {
  183. this.peer = other;
  184. this.self = self;
  185. this.socket = null;
  186. int port;
  187. this.traceLevel = defaultLevel;
  188. //this.IsBackground = true;
  189. // now get a connection between the two...
  190. port = OtpEpmd.lookupPort(peer);
  191. // now find highest common dist value
  192. if ((peer._proto != self._proto) || (self._distHigh < peer._distLow) || (self._distLow > peer._distHigh))
  193. {
  194. throw new System.IO.IOException("No common protocol found - cannot connect");
  195. }
  196. // highest common version: min(peer.distHigh, self.distHigh)
  197. peer.distChoose = (peer._distHigh > self._distHigh ? self._distHigh : peer._distHigh);
  198. doConnect(port);
  199. this.name = peer.node();
  200. this.connected = true;
  201. }
  202. /*
  203. * Deliver communication exceptions to the recipient.
  204. **/
  205. public abstract void deliver(System.Exception e);
  206. /*
  207. * Deliver messages to the recipient.
  208. **/
  209. public abstract void deliver(OtpMsg msg);
  210. /*
  211. * Send a pre-encoded message to a named process on a remote node.
  212. *
  213. * @param dest the name of the remote process.
  214. * @param payload the encoded message to send.
  215. *
  216. * @exception C#.io.IOException if the connection is not active or
  217. * a communication error occurs.
  218. **/
  219. protected internal virtual void sendBuf(Erlang.Pid from, System.String dest, OtpOutputStream payload)
  220. {
  221. if (!connected)
  222. {
  223. throw new System.IO.IOException("Not connected");
  224. }
  225. OtpOutputStream header = new OtpOutputStream(headerLen);
  226. // preamble: 4 byte length + "passthrough" tag + version
  227. header.write4BE(0); // reserve space for length
  228. header.write1(passThrough);
  229. header.write1(version);
  230. // header info
  231. header.write_tuple_head(4);
  232. header.write_long(regSendTag);
  233. header.write_any(from);
  234. if (sendCookie)
  235. header.write_atom(self.cookie());
  236. else
  237. header.write_atom("");
  238. header.write_atom(dest);
  239. // version for payload
  240. header.write1(version);
  241. // fix up length in preamble
  242. header.poke4BE(0, header.count() + payload.count() - 4);
  243. do_send(header, payload);
  244. }
  245. /*
  246. * Send a pre-encoded message to a process on a remote node.
  247. *
  248. * @param dest the Erlang PID of the remote process.
  249. * @param msg the encoded message to send.
  250. *
  251. * @exception C#.io.IOException if the connection is not active
  252. * or a communication error occurs.
  253. **/
  254. protected internal virtual void sendBuf(Erlang.Pid from, Erlang.Pid dest, OtpOutputStream payload)
  255. {
  256. if (!connected)
  257. {
  258. throw new System.IO.IOException("Not connected");
  259. }
  260. OtpOutputStream header = new OtpOutputStream(headerLen);
  261. // preamble: 4 byte length + "passthrough" tag + version
  262. header.write4BE(0); // reserve space for length
  263. header.write1(passThrough);
  264. header.write1(version);
  265. // header info
  266. header.write_tuple_head(3);
  267. header.write_long(sendTag);
  268. if (sendCookie)
  269. header.write_atom(self.cookie());
  270. else
  271. header.write_atom("");
  272. header.write_any(dest);
  273. // version for payload
  274. header.write1(version);
  275. // fix up length in preamble
  276. header.poke4BE(0, header.count() + payload.count() - 4);
  277. do_send(header, payload);
  278. }
  279. /*Send an auth error to peer because he sent a bad cookie.
  280. * The auth error uses his cookie (not revealing ours).
  281. * This is just like send_reg otherwise
  282. */
  283. private void cookieError(OtpLocalNode local, Erlang.Atom cookie)
  284. {
  285. try
  286. {
  287. OtpOutputStream header = new OtpOutputStream(headerLen);
  288. // preamble: 4 byte length + "passthrough" tag + version
  289. header.write4BE(0); // reserve space for length
  290. header.write1(passThrough);
  291. header.write1(version);
  292. header.write_tuple_head(4);
  293. header.write_long(regSendTag);
  294. header.write_any(local.createPid()); // disposable pid
  295. header.write_atom(cookie.atomValue()); // important: his cookie, not mine...
  296. header.write_atom("auth");
  297. // version for payload
  298. header.write1(version);
  299. // the payload
  300. // the no_auth message (copied from Erlang) Don't change this (Erlang will crash)
  301. // {$gen_cast, {print, "~n** Unauthorized cookie ~w **~n", [foo@aule]}}
  302. Erlang.Object[] msg = new Erlang.Object[2];
  303. Erlang.Object[] msgbody = new Erlang.Object[3];
  304. msgbody[0] = new Erlang.Atom("print");
  305. msgbody[1] = new Erlang.String("~n** Bad cookie sent to " + local + " **~n");
  306. // Erlang will crash and burn if there is no third argument here...
  307. msgbody[2] = new Erlang.List(); // empty list
  308. msg[0] = new Erlang.Atom("$gen_cast");
  309. msg[1] = new Erlang.Tuple(msgbody);
  310. OtpOutputStream payload = new OtpOutputStream(new Erlang.Tuple(msg));
  311. // fix up length in preamble
  312. header.poke4BE(0, header.count() + payload.count() - 4);
  313. try
  314. {
  315. do_send(header, payload);
  316. }
  317. catch (System.IO.IOException)
  318. {
  319. } // ignore
  320. }
  321. finally
  322. {
  323. close();
  324. throw new OtpAuthException("Remote cookie not authorized: " + cookie.atomValue());
  325. }
  326. }
  327. // link to pid
  328. /*
  329. * Create a link between the local node and the specified process on
  330. * the remote node. If the link is still active when the remote
  331. * process terminates, an exit signal will be sent to this
  332. * connection. Use {@link #sendUnlink unlink()} to remove the link.
  333. *
  334. * @param dest the Erlang PID of the remote process.
  335. *
  336. * @exception C#.io.IOException if the connection is not active
  337. * or a communication error occurs.
  338. **/
  339. protected internal virtual void sendLink(Erlang.Pid from, Erlang.Pid dest)
  340. {
  341. if (!connected)
  342. {
  343. throw new System.IO.IOException("Not connected");
  344. }
  345. OtpOutputStream header = new OtpOutputStream(headerLen);
  346. // preamble: 4 byte length + "passthrough" tag
  347. header.write4BE(0); // reserve space for length
  348. header.write1(passThrough);
  349. header.write1(version);
  350. // header
  351. header.write_tuple_head(3);
  352. header.write_long(linkTag);
  353. header.write_any(from);
  354. header.write_any(dest);
  355. // fix up length in preamble
  356. header.poke4BE(0, header.count() - 4);
  357. do_send(header);
  358. }
  359. /*
  360. * Remove a link between the local node and the specified process on
  361. * the remote node. This method deactivates links created with
  362. * {@link #sendLink link()}.
  363. *
  364. * @param dest the Erlang PID of the remote process.
  365. *
  366. * @exception C#.io.IOException if the connection is not active or
  367. * a communication error occurs.
  368. **/
  369. protected internal virtual void sendUnlink(Erlang.Pid from, Erlang.Pid dest)
  370. {
  371. if (!connected)
  372. {
  373. throw new System.IO.IOException("Not connected");
  374. }
  375. OtpOutputStream header = new OtpOutputStream(headerLen);
  376. // preamble: 4 byte length + "passthrough" tag
  377. header.write4BE(0); // reserve space for length
  378. header.write1(passThrough);
  379. header.write1(version);
  380. // header
  381. header.write_tuple_head(3);
  382. header.write_long(unlinkTag);
  383. header.write_any(from);
  384. header.write_any(dest);
  385. // fix up length in preamble
  386. header.poke4BE(0, header.count() - 4);
  387. do_send(header);
  388. }
  389. /*used internally when "processes" terminate */
  390. protected internal virtual void sendExit(Erlang.Pid from, Erlang.Pid dest, System.String reason)
  391. {
  392. sendExit(exitTag, from, dest, reason);
  393. }
  394. /*
  395. * Send an exit signal to a remote process.
  396. *
  397. * @param dest the Erlang PID of the remote process.
  398. * @param reason a string describing the exit reason.
  399. *
  400. * @exception C#.io.IOException if the connection is not active or
  401. * a communication error occurs.
  402. **/
  403. protected internal virtual void sendExit2(Erlang.Pid from, Erlang.Pid dest, System.String reason)
  404. {
  405. sendExit(exit2Tag, from, dest, reason);
  406. }
  407. private void sendExit(int tag, Erlang.Pid from, Erlang.Pid dest, System.String reason)
  408. {
  409. if (!connected)
  410. {
  411. throw new System.IO.IOException("Not connected");
  412. }
  413. OtpOutputStream header = new OtpOutputStream(headerLen);
  414. // preamble: 4 byte length + "passthrough" tag
  415. header.write4BE(0); // reserve space for length
  416. header.write1(passThrough);
  417. header.write1(version);
  418. // header
  419. header.write_tuple_head(4);
  420. header.write_long(tag);
  421. header.write_any(from);
  422. header.write_any(dest);
  423. header.write_string(reason);
  424. // fix up length in preamble
  425. header.poke4BE(0, header.count() - 4);
  426. do_send(header);
  427. }
  428. public virtual void Start()
  429. {
  430. if (!connected)
  431. {
  432. deliver(new System.IO.IOException("Not connected"));
  433. return ;
  434. }
  435. byte[] lbuf = new byte[4];
  436. OtpInputStream ibuf;
  437. Erlang.Object traceobj;
  438. int len;
  439. byte[] tock = new byte[]{0, 0, 0, 0};
  440. try
  441. {
  442. while (!done)
  443. {
  444. // don't return until we get a real message
  445. // or a failure of some kind (e.g. EXIT)
  446. // read length and read buffer must be atomic!
  447. do
  448. {
  449. // read 4 bytes - get length of incoming packet
  450. // socket.getInputStream().read(lbuf);
  451. readSock(socket, lbuf);
  452. ibuf = new OtpInputStream(lbuf);
  453. len = ibuf.read4BE();
  454. // received tick? send tock!
  455. if (len == 0)
  456. lock(this)
  457. {
  458. System.Byte[] temp_bytearray;
  459. temp_bytearray = tock;
  460. if (socket != null)
  461. ((System.IO.Stream) socket.GetStream()).Write(temp_bytearray, 0, temp_bytearray.Length);
  462. }
  463. }
  464. while (len == 0); // tick_loop
  465. // got a real message (maybe) - read len bytes
  466. byte[] tmpbuf = new byte[len];
  467. // i = socket.getInputStream().read(tmpbuf);
  468. readSock(socket, tmpbuf);
  469. ibuf = new OtpInputStream(tmpbuf);
  470. if (ibuf.read1() != passThrough)
  471. {
  472. goto receive_loop_brk;
  473. }
  474. // got a real message (really)
  475. Erlang.Atom reason = null;
  476. Erlang.Atom cookie = null;
  477. Erlang.Object tmp = null;
  478. Erlang.Tuple head = null;
  479. Erlang.Atom toName;
  480. Erlang.Pid to;
  481. Erlang.Pid from;
  482. int tag;
  483. // decode the header
  484. tmp = ibuf.read_any();
  485. if (!(tmp is Erlang.Tuple))
  486. {
  487. goto receive_loop_brk;
  488. }
  489. head = (Erlang.Tuple) tmp;
  490. if (!(head.elementAt(0) is Erlang.Long))
  491. {
  492. goto receive_loop_brk;
  493. }
  494. // lets see what kind of message this is
  495. tag = (int) ((Erlang.Long) (head.elementAt(0))).longValue();
  496. switch (tag)
  497. {
  498. case sendTag:
  499. case sendTTTag:
  500. // { SEND, Cookie, ToPid, TraceToken }
  501. if (!cookieOk)
  502. {
  503. // we only check this once, he can send us bad cookies later if he likes
  504. if (!(head.elementAt(1) is Erlang.Atom))
  505. {
  506. goto receive_loop_brk;
  507. }
  508. cookie = (Erlang.Atom) head.elementAt(1);
  509. if (sendCookie)
  510. {
  511. if (!cookie.atomValue().Equals(self.cookie()))
  512. {
  513. cookieError(self, cookie);
  514. }
  515. }
  516. else
  517. {
  518. if (!cookie.atomValue().Equals(""))
  519. {
  520. cookieError(self, cookie);
  521. }
  522. }
  523. cookieOk = true;
  524. }
  525. if (traceLevel >= sendThreshold)
  526. {
  527. System.Console.Out.WriteLine("<- " + headerType(head) + " " + head.ToString());
  528. /*show received payload too */
  529. long mark = ibuf.Position;
  530. traceobj = ibuf.read_any();
  531. if (traceobj != null)
  532. System.Console.Out.WriteLine(" " + traceobj.ToString());
  533. else
  534. System.Console.Out.WriteLine(" (null)");
  535. ibuf.Seek(mark, System.IO.SeekOrigin.Begin);
  536. }
  537. to = (Erlang.Pid) (head.elementAt(2));
  538. deliver(new OtpMsg(to, ibuf));
  539. break;
  540. case regSendTag:
  541. case regSendTTTag:
  542. // { REG_SEND, FromPid, Cookie, ToName, TraceToken }
  543. if (!cookieOk)
  544. {
  545. // we only check this once, he can send us bad cookies later if he likes
  546. if (!(head.elementAt(2) is Erlang.Atom))
  547. {
  548. goto receive_loop_brk;
  549. }
  550. cookie = (Erlang.Atom) head.elementAt(2);
  551. if (sendCookie)
  552. {
  553. if (!cookie.atomValue().Equals(self.cookie()))
  554. {
  555. cookieError(self, cookie);
  556. }
  557. }
  558. else
  559. {
  560. if (!cookie.atomValue().Equals(""))
  561. {
  562. cookieError(self, cookie);
  563. }
  564. }
  565. cookieOk = true;
  566. }
  567. if (traceLevel >= sendThreshold)
  568. {
  569. System.Console.Out.WriteLine("<- " + headerType(head) + " " + head.ToString());
  570. /*show received payload too */
  571. long mark = ibuf.Position;
  572. traceobj = ibuf.read_any();
  573. if (traceobj != null)
  574. System.Console.Out.WriteLine(" " + traceobj.ToString());
  575. else
  576. System.Console.Out.WriteLine(" (null)");
  577. ibuf.Seek(mark, System.IO.SeekOrigin.Begin);
  578. }
  579. from = (Erlang.Pid) (head.elementAt(1));
  580. toName = (Erlang.Atom) (head.elementAt(3));
  581. deliver(new OtpMsg(from, toName.atomValue(), ibuf));
  582. break;
  583. case exitTag:
  584. case exit2Tag:
  585. // { EXIT2, FromPid, ToPid, Reason }
  586. if (!(head.elementAt(3) is Erlang.Atom))
  587. {
  588. goto receive_loop_brk;
  589. }
  590. if (traceLevel >= ctrlThreshold)
  591. {
  592. System.Console.Out.WriteLine("<- " + headerType(head) + " " + head.ToString());
  593. }
  594. from = (Erlang.Pid) (head.elementAt(1));
  595. to = (Erlang.Pid) (head.elementAt(2));
  596. reason = (Erlang.Atom) head.elementAt(3);
  597. deliver(new OtpMsg(tag, from, to, reason));
  598. break;
  599. case exitTTTag:
  600. case exit2TTTag:
  601. // { EXIT2, FromPid, ToPid, TraceToken, Reason }
  602. // as above, but bifferent element number
  603. if (!(head.elementAt(4) is Erlang.Atom))
  604. {
  605. goto receive_loop_brk;
  606. }
  607. if (traceLevel >= ctrlThreshold)
  608. {
  609. System.Console.Out.WriteLine("<- " + headerType(head) + " " + head.ToString());
  610. }
  611. from = (Erlang.Pid) (head.elementAt(1));
  612. to = (Erlang.Pid) (head.elementAt(2));
  613. reason = (Erlang.Atom) head.elementAt(4);
  614. deliver(new OtpMsg(tag, from, to, reason));
  615. break;
  616. case linkTag:
  617. case unlinkTag:
  618. // { UNLINK, FromPid, ToPid}
  619. if (traceLevel >= ctrlThreshold)
  620. {
  621. System.Console.Out.WriteLine("<- " + headerType(head) + " " + head.ToString());
  622. }
  623. from = (Erlang.Pid) (head.elementAt(1));
  624. to = (Erlang.Pid) (head.elementAt(2));
  625. deliver(new OtpMsg(tag, from, to));
  626. break;
  627. // absolutely no idea what to do with these, so we ignore them...
  628. case groupLeaderTag:
  629. case nodeLinkTag:
  630. // { NODELINK }
  631. // (just show trace)
  632. if (traceLevel >= ctrlThreshold)
  633. {
  634. System.Console.Out.WriteLine("<- " + headerType(head) + " " + head.ToString());
  635. }
  636. break;
  637. default:
  638. // garbage?
  639. goto receive_loop_brk;
  640. }
  641. }
  642. receive_loop_brk: ;
  643. // end receive_loop
  644. // this section reachable only with break
  645. // we have received garbage from peer
  646. deliver(new Erlang.Exit("Remote is sending garbage"));
  647. }
  648. catch (OtpAuthException e)
  649. {
  650. deliver(e);
  651. }
  652. catch (Erlang.DecodeException)
  653. {
  654. deliver(new Erlang.Exit("Remote is sending garbage"));
  655. }
  656. catch (System.IO.IOException)
  657. {
  658. deliver(new Erlang.Exit("Remote has closed connection"));
  659. }
  660. finally
  661. {
  662. close();
  663. System.Console.Out.WriteLine("exit connection "+System.Threading.Thread.CurrentThread.Name);
  664. }
  665. }
  666. /*
  667. * Close the connection to the remote node.
  668. **/
  669. public virtual void close()
  670. {
  671. done = true;
  672. connected = false;
  673. lock(this)
  674. {
  675. try
  676. {
  677. if (socket != null)
  678. {
  679. if (traceLevel >= ctrlThreshold)
  680. {
  681. System.Console.Out.WriteLine("-> CLOSE");
  682. }
  683. socket.Close();
  684. //thread.Interrupt();
  685. }
  686. }
  687. catch (System.IO.IOException)
  688. {
  689. /*ignore socket close errors */
  690. }
  691. finally
  692. {
  693. socket = null;
  694. }
  695. }
  696. }
  697. ~AbstractConnection()
  698. {
  699. close();
  700. }
  701. /*
  702. * Determine if the connection is still alive. Note that this method
  703. * only reports the status of the connection, and that it is
  704. * possible that there are unread messages waiting in the receive
  705. * queue.
  706. *
  707. * @return true if the connection is alive.
  708. **/
  709. public virtual bool isConnected()
  710. {
  711. return connected;
  712. }
  713. // used by send and send_reg (message types with payload)
  714. protected internal virtual void do_send(OtpOutputStream header, OtpOutputStream payload)
  715. {
  716. lock(this)
  717. {
  718. try
  719. {
  720. if (traceLevel >= sendThreshold)
  721. {
  722. // Need to decode header and output buffer to show trace message!
  723. // First make OtpInputStream, then decode.
  724. try
  725. {
  726. Erlang.Object h = (header.getOtpInputStream(5)).read_any();
  727. System.Console.Out.WriteLine("-> " + headerType(h) + " " + h.ToString());
  728. Erlang.Object o = (payload.getOtpInputStream(0)).read_any();
  729. System.Console.Out.WriteLine(" " + o.ToString());
  730. o = null;
  731. }
  732. catch (Erlang.DecodeException e)
  733. {
  734. System.Console.Out.WriteLine(" " + "can't decode output buffer:" + e);
  735. }
  736. }
  737. header.writeTo((System.IO.Stream) socket.GetStream());
  738. payload.writeTo((System.IO.Stream) socket.GetStream());
  739. }
  740. catch (System.IO.IOException e)
  741. {
  742. close();
  743. throw e;
  744. }
  745. }
  746. }
  747. // used by the other message types
  748. protected internal virtual void do_send(OtpOutputStream header)
  749. {
  750. lock(this)
  751. {
  752. try
  753. {
  754. if (traceLevel >= ctrlThreshold)
  755. {
  756. try
  757. {
  758. Erlang.Object h = (header.getOtpInputStream(5)).read_any();
  759. System.Console.Out.WriteLine("-> " + headerType(h) + " " + h);
  760. }
  761. catch (Erlang.DecodeException e)
  762. {
  763. System.Console.Out.WriteLine(" " + "can't decode output buffer: " + e);
  764. }
  765. }
  766. header.writeTo((System.IO.Stream) socket.GetStream());
  767. }
  768. catch (System.IO.IOException e)
  769. {
  770. close();
  771. throw e;
  772. }
  773. }
  774. }
  775. protected internal virtual System.String headerType(Erlang.Object h)
  776. {
  777. int tag = - 1;
  778. if (h is Erlang.Tuple)
  779. {
  780. tag = (int) (((Erlang.Long) (((Erlang.Tuple) h).elementAt(0))).longValue());
  781. }
  782. switch (tag)
  783. {
  784. case linkTag:
  785. return "LINK";
  786. case sendTag:
  787. return "SEND";
  788. case exitTag:
  789. return "EXIT";
  790. case unlinkTag:
  791. return "UNLINK";
  792. case nodeLinkTag:
  793. return "NODELINK";
  794. case regSendTag:
  795. return "REG_SEND";
  796. case groupLeaderTag:
  797. return "GROUP_LEADER";
  798. case exit2Tag:
  799. return "EXIT2";
  800. case sendTTTag:
  801. return "SEND_TT";
  802. case exitTTTag:
  803. return "EXIT_TT";
  804. case regSendTTTag:
  805. return "REG_SEND_TT";
  806. case exit2TTTag:
  807. return "EXIT2_TT";
  808. }
  809. return "(unknown type)";
  810. }
  811. /*this method now throws exception if we don't get full read */
  812. protected internal virtual int readSock(System.Net.Sockets.TcpClient s, byte[] b)
  813. {
  814. int got = 0;
  815. int len = (int) (b.Length);
  816. int i;
  817. System.IO.Stream is_Renamed = null;
  818. lock(this)
  819. {
  820. if (s == null)
  821. {
  822. throw new System.IO.IOException("expected " + len + " bytes, socket was closed");
  823. }
  824. is_Renamed = (System.IO.Stream) s.GetStream();
  825. }
  826. while (got < len)
  827. {
  828. i = is_Renamed.Read(b, got, len - got);
  829. if (i < 0)
  830. {
  831. throw new System.IO.IOException("expected " + len + " bytes, got EOF after " + got + " bytes");
  832. }
  833. else
  834. got += i;
  835. }
  836. return got;
  837. }
  838. protected internal virtual void doAccept()
  839. {
  840. try
  841. {
  842. sendStatus("ok");
  843. int our_challenge = genChallenge();
  844. sendChallenge(peer.distChoose, self.flags, our_challenge);
  845. int her_challenge = recvChallengeReply(our_challenge);
  846. byte[] our_digest = genDigest(her_challenge, self.cookie());
  847. sendChallengeAck(our_digest);
  848. connected = true;
  849. cookieOk = true;
  850. sendCookie = false;
  851. }
  852. catch (System.IO.IOException ie)
  853. {
  854. close();
  855. throw ie;
  856. }
  857. catch (OtpAuthException ae)
  858. {
  859. close();
  860. throw ae;
  861. }
  862. catch (System.Exception)
  863. {
  864. System.String nn = peer.node();
  865. close();
  866. throw new System.IO.IOException("Error accepting connection from " + nn);
  867. }
  868. if (traceLevel >= handshakeThreshold)
  869. System.Console.Out.WriteLine("<- MD5 ACCEPTED " + peer.host());
  870. }
  871. protected internal virtual void doConnect(int port)
  872. {
  873. try
  874. {
  875. socket = new System.Net.Sockets.TcpClient(peer.host(), port);
  876. socket.NoDelay = true;
  877. Debug.WriteLine("-> MD5 CONNECT TO " + peer.host() + ": " + port);
  878. if (traceLevel >= handshakeThreshold)
  879. System.Console.Out.WriteLine("-> MD5 CONNECT TO " + peer.host() + ":" + port);
  880. sendName(peer.distChoose, self.flags);
  881. recvStatus();
  882. int her_challenge = recvChallenge();
  883. byte[] our_digest = genDigest(her_challenge, self.cookie());
  884. int our_challenge = genChallenge();
  885. sendChallengeReply(our_challenge, our_digest);
  886. recvChallengeAck(our_challenge);
  887. cookieOk = true;
  888. sendCookie = false;
  889. }
  890. catch (OtpAuthException ae)
  891. {
  892. close();
  893. throw ae;
  894. }
  895. //catch (System.Exception)
  896. //{
  897. // close();
  898. // throw new System.IO.IOException("Cannot connect to peer node");
  899. //}
  900. }
  901. // This is nooo good as a challenge,
  902. // XXX fix me.
  903. static protected internal int genChallenge()
  904. {
  905. return random.Next();
  906. }
  907. // Used to debug print a message digest
  908. internal static System.String hex0(byte x)
  909. {
  910. char[] tab = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
  911. int uint_Renamed;
  912. if (x < 0)
  913. {
  914. uint_Renamed = x & 0x7F;
  915. uint_Renamed |= (1 << 7);
  916. }
  917. else
  918. {
  919. uint_Renamed = (int) x;
  920. }
  921. return "" + tab[SupportClass.URShift(uint_Renamed, 4)] + tab[uint_Renamed & 0xF];
  922. }
  923. internal static System.String hex(byte[] b)
  924. {
  925. System.Text.StringBuilder sb = new System.Text.StringBuilder();
  926. try
  927. {
  928. int i;
  929. for (i = 0; i < b.Length; ++i)
  930. sb.Append(hex0(b[i]));
  931. }
  932. catch (System.Exception)
  933. {
  934. // Debug function, ignore errors.
  935. }
  936. return sb.ToString();
  937. }
  938. protected internal virtual byte[] genDigest(int challenge, System.String cookie)
  939. {
  940. int i;
  941. long ch2;
  942. if (challenge < 0)
  943. {
  944. ch2 = 1L << 31;
  945. ch2 |= (long) (challenge & 0x7FFFFFFFL);
  946. }
  947. else
  948. {
  949. ch2 = (long) challenge;
  950. }
  951. System.Security.Cryptography.MD5CryptoServiceProvider context
  952. = new System.Security.Cryptography.MD5CryptoServiceProvider();
  953. byte[] tmp = context.ComputeHash(System.Text.Encoding.UTF8.GetBytes(cookie+System.Convert.ToString(ch2)));
  954. byte[] res = new byte[tmp.Length];
  955. for (i = 0; i < tmp.Length; ++i)
  956. {
  957. res[i] = (byte) (tmp[i] & 0xFF);
  958. }
  959. return res;
  960. }
  961. protected internal virtual void sendName(int dist, int flags)
  962. {
  963. OtpOutputStream obuf = new OtpOutputStream();
  964. System.String str = self.node();
  965. obuf.write2BE(str.Length + 7); // 7 bytes + nodename
  966. obuf.write1(AbstractNode.NTYPE_R6);
  967. obuf.write2BE(dist);
  968. obuf.write4BE(flags);
  969. //UPGRADE_NOTE: This code will be optimized in the future;
  970. byte[] tmpBytes;
  971. int i;
  972. string tmpStr;
  973. tmpStr = str;
  974. tmpBytes = new byte[tmpStr.Length];
  975. i = 0;
  976. while (i < tmpStr.Length)
  977. {
  978. tmpBytes[i] = (byte) tmpStr[i];
  979. i++;
  980. }
  981. obuf.write(tmpBytes);
  982. obuf.writeTo((System.IO.Stream) socket.GetStream());
  983. if (traceLevel >= handshakeThreshold)
  984. {
  985. System.Console.Out.WriteLine("-> " + "HANDSHAKE sendName" + " flags=" + flags + " dist=" + dist + " local=" + self);
  986. }
  987. }
  988. protected internal virtual void sendChallenge(int dist, int flags, int challenge)
  989. {
  990. OtpOutputStream obuf = new OtpOutputStream();
  991. System.String str = self.node();
  992. obuf.write2BE(str.Length + 11); // 11 bytes + nodename
  993. obuf.write1(AbstractNode.NTYPE_R6);
  994. obuf.write2BE(dist);
  995. obuf.write4BE(flags);
  996. obuf.write4BE(challenge);
  997. //UPGRADE_NOTE: This code will be optimized in the future;
  998. byte[] tmpBytes;
  999. int i;
  1000. string tmpStr;
  1001. tmpStr = str;
  1002. tmpBytes = new byte[tmpStr.Length];
  1003. i = 0;
  1004. while (i < tmpStr.Length)
  1005. {
  1006. tmpBytes[i] = (byte) tmpStr[i];
  1007. i++;
  1008. }
  1009. obuf.write(tmpBytes);
  1010. obuf.writeTo((System.IO.Stream) socket.GetStream());
  1011. if (traceLevel >= handshakeThreshold)
  1012. {
  1013. System.Console.Out.WriteLine("-> " + "HANDSHAKE sendChallenge" + " flags=" + flags + " dist=" + dist + " challenge=" + challenge + " local=" + self);
  1014. }
  1015. }
  1016. protected internal virtual byte[] read2BytePackage()
  1017. {
  1018. byte[] lbuf = new byte[2];
  1019. byte[] tmpbuf;
  1020. readSock(socket, lbuf);
  1021. OtpInputStream ibuf = new OtpInputStream(lbuf);
  1022. int len = ibuf.read2BE();
  1023. tmpbuf = new byte[len];
  1024. readSock(socket, tmpbuf);
  1025. return tmpbuf;
  1026. }
  1027. protected internal virtual void recvName(OtpPeer peer)
  1028. {
  1029. System.String hisname = "";
  1030. try
  1031. {
  1032. byte[] tmpbuf = read2BytePackage();
  1033. OtpInputStream ibuf = new OtpInputStream(tmpbuf);
  1034. byte[] tmpname;
  1035. int len = (int) (tmpbuf.Length);
  1036. peer.ntype = ibuf.read1();
  1037. if (peer.ntype != AbstractNode.NTYPE_R6)
  1038. {
  1039. throw new System.IO.IOException("Unknown remote node type");
  1040. }
  1041. peer._distLow = (peer._distHigh = ibuf.read2BE());
  1042. if (peer._distLow < 5)
  1043. {
  1044. throw new System.IO.IOException("Unknown remote node type");
  1045. }
  1046. peer.flags = ibuf.read4BE();
  1047. tmpname = new byte[len - 7];
  1048. ibuf.readN(tmpname);
  1049. char[] tmpChar;
  1050. tmpChar = new char[tmpname.Length];
  1051. tmpname.CopyTo(tmpChar, 0);
  1052. hisname = new System.String(tmpChar);
  1053. // Set the old nodetype parameter to indicate hidden/normal status
  1054. // When the old handshake is removed, the ntype should also be.
  1055. if ((peer.flags & AbstractNode.dFlagPublished) != 0)
  1056. peer.ntype = AbstractNode.NTYPE_R4_ERLANG;
  1057. else
  1058. peer.ntype = AbstractNode.NTYPE_R4_HIDDEN;
  1059. if ((peer.flags & AbstractNode.dFlagExtendedReferences) == 0)
  1060. {
  1061. throw new System.IO.IOException("Handshake failed - peer cannot handle extended references");
  1062. }
  1063. if (OtpSystem.useExtendedPidsPorts() && (peer.flags & AbstractNode.dFlagExtendedPidsPorts) == 0)
  1064. if (true && (peer.flags & AbstractNode.dFlagExtendedPidsPorts) == 0)
  1065. {
  1066. throw new System.IO.IOException("Handshake failed - peer cannot handle extended pids and ports");
  1067. }
  1068. }
  1069. catch (Erlang.DecodeException)
  1070. {
  1071. throw new System.IO.IOException("Handshake failed - not enough data");
  1072. }
  1073. int i = hisname.IndexOf((System.Char) '@', 0);
  1074. peer._node = hisname;
  1075. peer._alive = hisname.Substring(0, (i) - (0));
  1076. peer._host = hisname.Substring(i + 1, (hisname.Length) - (i + 1));
  1077. if (traceLevel >= handshakeThreshold)
  1078. {
  1079. System.Console.Out.WriteLine("<- " + "HANDSHAKE" + " ntype=" + peer.ntype + " dist=" + peer._distHigh + " remote=" + peer);
  1080. }
  1081. }
  1082. protected internal virtual int recvChallenge()
  1083. {
  1084. int challenge;
  1085. try
  1086. {
  1087. byte[] buf = read2BytePackage();
  1088. OtpInputStream ibuf = new OtpInputStream(buf);
  1089. peer.ntype = ibuf.read1();
  1090. if (peer.ntype != AbstractNode.NTYPE_R6)
  1091. {
  1092. throw new System.IO.IOException("Unexpected peer type");
  1093. }
  1094. peer._distLow = (peer._distHigh = ibuf.read2BE());
  1095. peer.flags = ibuf.read4BE();
  1096. challenge = ibuf.read4BE();
  1097. byte[] tmpname = new byte[buf.Length - 11];
  1098. ibuf.readN(tmpname);
  1099. char[] tmpChar;
  1100. tmpChar = new char[tmpname.Length];
  1101. tmpname.CopyTo(tmpChar, 0);
  1102. System.String hisname = new System.String(tmpChar);
  1103. int i = hisname.IndexOf((System.Char) '@', 0);
  1104. peer._node = hisname;
  1105. peer._alive = hisname.Substring(0, (i) - (0));
  1106. peer._host = hisname.Substring(i + 1, (hisname.Length) - (i + 1));
  1107. }
  1108. catch (Erlang.DecodeException)
  1109. {
  1110. throw new System.IO.IOException("Handshake failed - not enough data");
  1111. }
  1112. if (traceLevel >= handshakeThreshold)
  1113. {
  1114. System.Console.Out.WriteLine("<- " + "HANDSHAKE recvChallenge" + " from=" + peer._node + " challenge=" + challenge + " local=" + self);
  1115. }
  1116. return challenge;
  1117. }
  1118. protected internal virtual void sendChallengeReply(int challenge, byte[] digest)
  1119. {
  1120. OtpOutputStream obuf = new OtpOutputStream();
  1121. obuf.write2BE(21);
  1122. obuf.write1(ChallengeReply);
  1123. obuf.write4BE(challenge);
  1124. obuf.write(digest);
  1125. obuf.writeTo((System.IO.Stream) socket.GetStream());
  1126. if (traceLevel >= handshakeThreshold)
  1127. {
  1128. System.Console.Out.WriteLine("-> " + "HANDSHAKE sendChallengeReply" + " challenge=" + challenge + " digest=" + hex(digest) + " local=" + self);

Large files files are truncated, but you can click here to view the full file