PageRenderTime 73ms CodeModel.GetById 39ms RepoModel.GetById 0ms app.codeStats 0ms

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

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

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