You have reached the legacy GHI Electronics, LLC website, for the new website please visit here. For the new forum please visit here.

This entry's latest version is outdated and must be revised. Please see the documentation for the latest API.

Name Service - Give your FEZ a real network name by WouterH

Feb. 19, 2011   |   Snippet   |   Licensed as GPL 3.0   |   1486 views

This extension enables you to give your FEZ a real network name.

F.e. you can name your FEZ "FEZCOBRA" and then perform a "ping fezcobra" from an other computer on your network.

Since I've put a lot of effort in this code, I'd like you to credit me if you use this extension. Otherwise, read RFC 1002 and write you own implementation Smiley

Enjoy!

[Dec 14, 2011]
- Implemented workarounds as found on http://www.cifs.org/wiki/IETF_STD_19_(NBT)_Bugs
- Modified license from APACHE 2.0 to GNU 3



Author Version Date
WouterH 1 05/04 '12 at 01:44pm
1 — Source
  1. using System;
  2. using System.Collections;
  3. using System.Net;
  4. using System.Net.Sockets;
  5. using System.Text;
  6. using System.Threading;
  7. using Microsoft.SPOT;
  8. using Microsoft.SPOT.Net.NetworkInformation;
  9.  
  10. namespace FastloadMedia.NETMF.Web
  11. {
  12. /// <summary>
  13. /// NetBios NameService class for .NET Micro Framework.
  14. /// Programmed by Wouter Huysentruit
  15. /// Copyright (C) 2011 Fastload-Media
  16. ///
  17. /// This program is free software: you can redistribute it and/or modify
  18. /// it under the terms of the GNU General Public License as published by
  19. /// the Free Software Foundation, either version 3 of the License, or
  20. /// any later version.
  21. ///
  22. /// This program is distributed in the hope that it will be useful,
  23. /// but WITHOUT ANY WARRANTY; without even the implied warranty of
  24. /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  25. /// GNU General Public License for more details.
  26. ///
  27. /// You should have received a copy of the GNU General Public License
  28. /// along with this program. If not, see <see cref="http://www.gnu.org/licenses"/>.
  29. /// </summary>
  30. public class NameService : IDisposable
  31. {
  32. #region Consts
  33.  
  34. private const ushort NAME_TRN_ID = 0x6703; // unique transaction id
  35. private const int BCAST_REQ_RETRY_TIMEOUT = 250;
  36. private const int BCAST_REQ_RETRY_COUNT = 3;
  37. private const int BCAST_NS_PORT = 137;
  38. private const int NAME_UPDATE_INTERVAL_MS = 10 * 60 * 1000; // 10 minutes, represented as ms
  39.  
  40. #endregion
  41.  
  42. #region Definitions
  43.  
  44. /// <summary>
  45. /// Available name types.
  46. /// </summary>
  47. public enum NameType
  48. {
  49. /// <summary>
  50. /// Unique name. (F.e. workstation)
  51. /// </summary>
  52. Unique,
  53.  
  54. /// <summary>
  55. /// Group name. (F.e. domain)
  56. /// </summary>
  57. Group
  58. }
  59.  
  60. /// <summary>
  61. /// Represents a single name as added to the namelist.
  62. /// </summary>
  63. private struct Name
  64. {
  65. public string UncompressedName;
  66. public NameType Type;
  67. }
  68.  
  69. /// <summary>
  70. /// Most used Microsoft suffixes for NetBIOS names.
  71. /// </summary>
  72. public enum MsSuffix : byte
  73. {
  74. /// <summary>
  75. /// Unique workstation computername or default group name.
  76. /// </summary>
  77. Default = 0x00,
  78.  
  79. /// <summary>
  80. /// Group name for master browser (<\\--__MSBROWSE__>).
  81. /// </summary>
  82. MasterBrowserGroup = 0x01,
  83.  
  84. /// <summary>
  85. /// Unique domainname.
  86. /// </summary>
  87. MasterBrowserUnique = 0x1D,
  88.  
  89. /// <summary>
  90. /// Group domain name.
  91. /// </summary>
  92. BrowserServiceElections = 0x1E,
  93.  
  94. /// <summary>
  95. /// Unique computername for file server.
  96. /// </summary>
  97. FileServerService = 0x20,
  98. }
  99.  
  100. /// <summary>
  101. /// Available flags used in the header.
  102. /// </summary>
  103. [Flags]
  104. private enum HeaderFlags : byte
  105. {
  106. /// <summary>
  107. /// Broadcast flag.
  108. /// </summary>
  109. Broadcast = 0x01,
  110.  
  111. /// <summary>
  112. /// Recursion available flag. Only valid in responses from a NetBIOS Name Server.
  113. /// Must be zero in all other responses.
  114. /// </summary>
  115. RecursionAvailable = 0x08,
  116.  
  117. /// <summary>
  118. /// Recursion desired flag. May only be set on a request to a NetBIOS Name Server.
  119. /// </summary>
  120. RecursionDesired = 0x10,
  121.  
  122. /// <summary>
  123. /// Truncation flag.
  124. /// Set if this message was truncated because the datagram carrying it would be greater than 576 bytes in length.
  125. /// </summary>
  126. Truncation = 0x20,
  127.  
  128. /// <summary>
  129. /// Must be zero if Response is false.
  130. /// If set, then the node responing is an authority for the domain name.
  131. /// </summary>
  132. AuthoritativeAnswer = 0x40
  133. }
  134.  
  135. /// <summary>
  136. /// Available opcodes used in the header.
  137. /// </summary>
  138. private enum HeaderOpcode : byte
  139. {
  140. Query = 0,
  141. Registration = 5,
  142. Release = 6,
  143. WACK = 7,
  144. Refresh = 8,
  145. Update = 255 // Special case: is a name registration request with RD bit cleared
  146. }
  147.  
  148. /// <summary>
  149. /// Packet header.
  150. /// </summary>
  151. private struct Header
  152. {
  153. /// <summary>
  154. /// Transaction ID (chosen by requestor).
  155. /// </summary>
  156. public ushort NameTrnId;
  157. /// <summary>
  158. /// True for response, false for request.
  159. /// </summary>
  160. public bool Response;
  161. /// <summary>
  162. /// Operation code.
  163. /// </summary>
  164. public HeaderOpcode Opcode;
  165. /// <summary>
  166. /// Flags.
  167. /// </summary>
  168. public HeaderFlags Flags;
  169. /// <summary>
  170. /// Result codes of request.
  171. /// </summary>
  172. public byte Rcode;
  173. /// <summary>
  174. /// Number of entries in the question section of a Name Service packet.
  175. /// Always zero for response. Must be non-zero for all NetBIOS Name requests.
  176. /// </summary>
  177. public ushort QdCount;
  178. /// <summary>
  179. /// Number of resource records in the answer section of a Name Service packet.
  180. /// </summary>
  181. public ushort AnCount;
  182. /// <summary>
  183. /// Number of resource records in the authority section of a Name Service packet.
  184. /// </summary>
  185. public ushort NsCount;
  186. /// <summary>
  187. /// Number of resource records in the additional records section of a Name Service packet.
  188. /// </summary>
  189. public ushort ArCount;
  190.  
  191. /// <summary>
  192. /// Parse a header represented as a byte array.
  193. /// Useful when receiving messages.
  194. /// </summary>
  195. /// <param name="data">The byte array of the header.</param>
  196. /// <returns>A header object.</returns>
  197. public static Header Parse(byte[] data)
  198. {
  199. if (data.Length < 12)
  200. throw new ArgumentException("Minimum 12 bytes are required");
  201.  
  202. Header header = new Header();
  203. int offset = 0;
  204. header.NameTrnId = (ushort)((data[offset++] << 8) + data[offset++]);
  205. ushort temp = (ushort)((data[offset++] << 8) + data[offset++]);
  206. header.Response = temp >= 0x8000;
  207. header.Opcode = (HeaderOpcode)((temp >> 11) & 0x0F);
  208. header.Flags = (HeaderFlags)((temp >> 4) & 0x7F);
  209. header.Rcode = (byte)(temp & 0x0F);
  210. header.QdCount = (ushort)((data[offset++] << 8) + data[offset++]);
  211. header.AnCount = (ushort)((data[offset++] << 8) + data[offset++]);
  212. header.NsCount = (ushort)((data[offset++] << 8) + data[offset++]);
  213. header.ArCount = (ushort)((data[offset++] << 8) + data[offset++]);
  214. return header;
  215. }
  216.  
  217. /// <summary>
  218. /// Gets the number of bytes that will be returned by the ToArray method.
  219. /// </summary>
  220. public int ByteSize
  221. {
  222. get { return 12; }
  223. }
  224.  
  225. /// <summary>
  226. /// Convert a header to a byte array.
  227. /// Useful for sending messages.
  228. /// </summary>
  229. /// <returns>The byte array representation of the header.</returns>
  230. public byte[] ToArray()
  231. {
  232. byte[] data = new byte[ByteSize];
  233. int offset = 0;
  234. data[offset++] = (byte)(NameTrnId >> 8);
  235. data[offset++] = (byte)NameTrnId;
  236. ushort temp = (ushort)(((ushort)Opcode << 11) + ((ushort)Flags << 4) + Rcode);
  237. if (Response) temp += 0x8000;
  238. data[offset++] = (byte)(temp >> 8);
  239. data[offset++] = (byte)temp;
  240. data[offset++] = (byte)(QdCount >> 8);
  241. data[offset++] = (byte)QdCount;
  242. data[offset++] = (byte)(AnCount >> 8);
  243. data[offset++] = (byte)AnCount;
  244. data[offset++] = (byte)(NsCount >> 8);
  245. data[offset++] = (byte)NsCount;
  246. data[offset++] = (byte)(ArCount >> 8);
  247. data[offset++] = (byte)ArCount;
  248. return data;
  249. }
  250. }
  251.  
  252. /// <summary>
  253. /// Available question types.
  254. /// </summary>
  255. private enum QuestionType : ushort
  256. {
  257. /// <summary>
  258. /// NetBIOS general Name Service Resource Record.
  259. /// </summary>
  260. NB = 0x0020,
  261. /// <summary>
  262. /// NetBIOS NODE STATUS Resource Record.
  263. /// </summary>
  264. NBSTAT = 0x0021
  265. }
  266.  
  267. /// <summary>
  268. /// Available question classes.
  269. /// </summary>
  270. private enum QuestionClass : ushort
  271. {
  272. /// <summary>
  273. /// Internet class.
  274. /// </summary>
  275. IN = 0x0001
  276. }
  277.  
  278. /// <summary>
  279. /// Packet question name record.
  280. /// </summary>
  281. private struct QuestionName
  282. {
  283. /// <summary>
  284. /// The NetBIOS name for the request.
  285. /// </summary>
  286. public string UncompressedName;
  287. /// <summary>
  288. /// The type of request.
  289. /// </summary>
  290. public QuestionType Type;
  291. /// <summary>
  292. /// The class of request.
  293. /// </summary>
  294. public QuestionClass Class;
  295.  
  296. /// <summary>
  297. /// Extract an uncompressed name from an array.
  298. /// </summary>
  299. /// <param name="data">The byte array.</param>
  300. /// <param name="offset">The offset to start extracting from.</param>
  301. /// <returns>The uncompressed name.</returns>
  302. internal static string ExtractName(byte[] data, ref int offset)
  303. {
  304. string result = "";
  305.  
  306. while (true)
  307. {
  308. byte length = data[offset++];
  309. if (length == 0)
  310. break; // Reached end of record.
  311.  
  312. if (result.Length > 0)
  313. { // Add a '.' in uncompressed format
  314. result += "CO";
  315. }
  316.  
  317. if ((length & 0xC0) != 0x00)
  318. { // Whooo, we have a pointer
  319. int address = (ushort)(((length & 0x3F) << 8) + data[offset++]);
  320. return ExtractName(data, ref address);
  321. }
  322.  
  323. for (int i = 0; i < length; i++)
  324. result += (char)data[offset++];
  325. }
  326.  
  327. return result;
  328. }
  329.  
  330. /// <summary>
  331. /// Parse a QuestionName represented as a byte array.
  332. /// </summary>
  333. /// <param name="data">The byte array.</param>
  334. /// <param name="offset">The offset to start parsing from.</param>
  335. /// <returns>A parsed QuestionName object.</returns>
  336. public static QuestionName Parse(byte[] data, ref int offset)
  337. {
  338. QuestionName name = new QuestionName();
  339. name.UncompressedName = ExtractName(data, ref offset);
  340. name.Type = (QuestionType)((data[offset++] << 8) + data[offset++]);
  341. name.Class = (QuestionClass)((data[offset++] << 8) + data[offset++]);
  342. return name;
  343. }
  344.  
  345. /// <summary>
  346. /// Gets the number of bytes that will be returned by the ToArray method.
  347. /// </summary>
  348. public int ByteSize
  349. {
  350. get
  351. {
  352. if (UncompressedName == null)
  353. throw new Exception("UncompressedName not set");
  354. return UncompressedName.Length + 2 + 4;
  355. }
  356. }
  357.  
  358. /// <summary>
  359. /// Convert a QuestionName to a byte array.
  360. /// Useful for sending messages.
  361. /// </summary>
  362. /// <returns>The byte array representation of the QuestionName.</returns>
  363. public byte[] ToArray()
  364. { // TODO: support '.' in names? Not needed though for normal netbios name services
  365. if (UncompressedName == null)
  366. throw new Exception("UncompressedName not set");
  367. byte[] result = new byte[ByteSize];
  368. int offset = 0;
  369. result[offset++] = (byte)UncompressedName.Length;
  370. for (int i = 0; i < UncompressedName.Length; i++)
  371. result[offset++] = (byte)UncompressedName[i];
  372. result[offset++] = 0;
  373. result[offset++] = (byte)((ushort)Type >> 8);
  374. result[offset++] = (byte)Type;
  375. result[offset++] = (byte)((ushort)Class >> 8);
  376. result[offset++] = (byte)Class;
  377.  
  378. if (offset != result.Length)
  379. throw new Exception("Length mismatch");
  380.  
  381. return result;
  382. }
  383. }
  384.  
  385. /// <summary>
  386. /// Available resource record types.
  387. /// </summary>
  388. private enum ResourceRecordType : ushort
  389. {
  390. /// <summary>
  391. /// IP address record.
  392. /// </summary>
  393. A = 0x0001,
  394. /// <summary>
  395. /// Name Server record.
  396. /// </summary>
  397. NS = 0x0002,
  398. /// <summary>
  399. /// NULL resource record (waiting for acknowledgement response).
  400. /// </summary>
  401. NULL = 0x000A,
  402. /// <summary>
  403. /// General NetBIOS record.
  404. /// </summary>
  405. NB = 0x0020,
  406. /// <summary>
  407. /// NetBIOS Node Status resource record.
  408. /// </summary>
  409. NBSTAT = 0x0021
  410. }
  411.  
  412. /// <summary>
  413. /// Available resource record classes.
  414. /// </summary>
  415. private enum ResourceRecordClass : ushort
  416. {
  417. /// <summary>
  418. /// Internet class.
  419. /// </summary>
  420. IN = 0x0001
  421. }
  422.  
  423. /// <summary>
  424. /// Packet resource record.
  425. /// </summary>
  426. private struct ResourceRecord
  427. {
  428. /// <summary>
  429. /// The NetBIOS name corresponding to this resource record.
  430. /// </summary>
  431. public string UncompressedName;
  432. /// <summary>
  433. /// The record type code.
  434. /// </summary>
  435. public ResourceRecordType Type;
  436. /// <summary>
  437. /// The record class code.
  438. /// </summary>
  439. public ResourceRecordClass Class;
  440. /// <summary>
  441. /// Time to Live of the resource record's name.
  442. /// </summary>
  443. public uint TTL;
  444. /// <summary>
  445. /// Class and Type dependent field. Contains the resource information.
  446. /// </summary>
  447. public byte[] Data;
  448.  
  449. /// <summary>
  450. /// Parse a ResourceRecord represented as a byte array.
  451. /// </summary>
  452. /// <param name="data">The byte array.</param>
  453. /// <param name="offset">The offset to start parsing from.</param>
  454. /// <returns>A parsed ResourceRecord.</returns>
  455. public static ResourceRecord Parse(byte[] data, ref int offset)
  456. {
  457. ResourceRecord record = new ResourceRecord();
  458. record.UncompressedName = QuestionName.ExtractName(data, ref offset);
  459. record.Type = (ResourceRecordType)((data[offset++] << 8) + data[offset++]);
  460. record.Class = (ResourceRecordClass)((data[offset++] << 8) + data[offset++]);
  461. record.TTL = (uint)((data[offset++] << 24) + (data[offset++] << 16) + (data[offset++] << 8) + data[offset++]);
  462. int length = (ushort)((data[offset++] << 8) + data[offset++]);
  463. if (length > 0)
  464. {
  465. record.Data = new byte[length];
  466. for (int i = 0; i < length; i++)
  467. record.Data[i] = data[offset++];
  468. }
  469. else
  470. record.Data = null;
  471. return record;
  472. }
  473.  
  474. /// <summary>
  475. /// Gets the number of bytes that will be returned by the ToArray method.
  476. /// </summary>
  477. public int ByteSize
  478. {
  479. get
  480. {
  481. if (UncompressedName == null)
  482. throw new Exception("UncompressedName not set");
  483. return UncompressedName.Length + 2 + 4 + 4 + 2 + (Data != null ? Data.Length : 0);
  484. }
  485. }
  486.  
  487. /// <summary>
  488. /// Convert a ResourceRecord to a byte array.
  489. /// Useful for sending messages.
  490. /// </summary>
  491. /// <returns>The byte array representation of the ResourceRecord.</returns>
  492. public byte[] ToArray()
  493. {
  494. if (UncompressedName == null)
  495. throw new Exception("UncompressedName not set");
  496. byte[] result = new byte[ByteSize];
  497. int offset = 0;
  498. result[offset++] = (byte)UncompressedName.Length;
  499. for (int i = 0; i < UncompressedName.Length; i++)
  500. result[offset++] = (byte)UncompressedName[i];
  501. result[offset++] = 0;
  502. result[offset++] = (byte)((ushort)Type >> 8);
  503. result[offset++] = (byte)Type;
  504. result[offset++] = (byte)((ushort)Class >> 8);
  505. result[offset++] = (byte)Class;
  506. result[offset++] = (byte)(TTL >> 24);
  507. result[offset++] = (byte)(TTL >> 16);
  508. result[offset++] = (byte)(TTL >> 8);
  509. result[offset++] = (byte)TTL;
  510. int length = Data != null ? Data.Length : 0;
  511. result[offset++] = (byte)(length >> 8);
  512. result[offset++] = (byte)length;
  513. for (int i = 0; i < length; i++)
  514. result[offset++] = Data[i];
  515.  
  516. if (offset != result.Length)
  517. throw new Exception("Length mismatch");
  518.  
  519. return result;
  520. }
  521. }
  522.  
  523. /// <summary>
  524. /// Full packet (header + data).
  525. /// </summary>
  526. private struct Packet
  527. {
  528. public Header Header;
  529. public QuestionName[] QuestionEntries;
  530. public ResourceRecord[] AnswerResourceRecords;
  531. public ResourceRecord[] AuthorityResourceRecords;
  532. public ResourceRecord[] AdditionalResourceRecords;
  533.  
  534. /// <summary>
  535. /// Parses a packet from incomming data.
  536. /// </summary>
  537. /// <param name="data">Byte array containing the incomming data.</param>
  538. /// <returns>A parsed Packet.</returns>
  539. public static Packet Parse(byte[] data)
  540. {
  541. Packet packet = new Packet();
  542. packet.Header = Header.Parse(data);
  543. int offset = packet.Header.ByteSize;
  544.  
  545. if (packet.Header.QdCount > 0)
  546. {
  547. packet.QuestionEntries = new QuestionName[packet.Header.QdCount];
  548. for (int i = 0; i < packet.Header.QdCount; i++)
  549. packet.QuestionEntries[i] = QuestionName.Parse(data, ref offset);
  550. }
  551. else
  552. packet.QuestionEntries = null;
  553.  
  554. if (packet.Header.AnCount > 0)
  555. {
  556. packet.AnswerResourceRecords = new ResourceRecord[packet.Header.AnCount];
  557. for (int i = 0; i < packet.Header.AnCount; i++)
  558. packet.AnswerResourceRecords[i] = ResourceRecord.Parse(data, ref offset);
  559. }
  560. else
  561. packet.AnswerResourceRecords = null;
  562.  
  563. if (packet.Header.NsCount > 0)
  564. {
  565. packet.AuthorityResourceRecords = new ResourceRecord[packet.Header.NsCount];
  566. for (int i = 0; i < packet.Header.NsCount; i++)
  567. packet.AuthorityResourceRecords[i] = ResourceRecord.Parse(data, ref offset);
  568. }
  569. else
  570. packet.AuthorityResourceRecords = null;
  571.  
  572. if (packet.Header.ArCount > 0)
  573. {
  574. packet.AdditionalResourceRecords = new ResourceRecord[packet.Header.ArCount];
  575. for (int i = 0; i < packet.Header.ArCount; i++)
  576. packet.AdditionalResourceRecords[i] = ResourceRecord.Parse(data, ref offset);
  577. }
  578. else
  579. packet.AdditionalResourceRecords = null;
  580.  
  581. return packet;
  582. }
  583.  
  584. /// <summary>
  585. /// Gets the number of bytes that will be returned by the ToArray method.
  586. /// </summary>
  587. public int ByteSize
  588. {
  589. get
  590. {
  591. int result = Header.ByteSize;
  592.  
  593. if (QuestionEntries != null)
  594. foreach (QuestionName name in QuestionEntries)
  595. result += name.ByteSize;
  596.  
  597. if (AnswerResourceRecords != null)
  598. foreach (ResourceRecord record in AnswerResourceRecords)
  599. result += record.ByteSize;
  600.  
  601. if (AuthorityResourceRecords != null)
  602. foreach (ResourceRecord record in AuthorityResourceRecords)
  603. result += record.ByteSize;
  604.  
  605. if (AdditionalResourceRecords != null)
  606. foreach (ResourceRecord record in AdditionalResourceRecords)
  607. result += record.ByteSize;
  608.  
  609. return result;
  610. }
  611. }
  612.  
  613. /// <summary>
  614. /// Prepares a packet for transmission.
  615. /// </summary>
  616. /// <returns>A byte array containing the packet data.</returns>
  617. public byte[] ToArray()
  618. {
  619. byte[] result = new byte[ByteSize];
  620. int offset = 0;
  621.  
  622. byte[] source = Header.ToArray();
  623. Array.Copy(source, 0, result, offset, source.Length);
  624. offset += source.Length;
  625.  
  626. if (QuestionEntries != null)
  627. foreach (QuestionName name in QuestionEntries)
  628. {
  629. source = name.ToArray();
  630. Array.Copy(source, 0, result, offset, source.Length);
  631. offset += source.Length;
  632. }
  633.  
  634. if (AnswerResourceRecords != null)
  635. foreach (ResourceRecord record in AnswerResourceRecords)
  636. {
  637. source = record.ToArray();
  638. Array.Copy(source, 0, result, offset, source.Length);
  639. offset += source.Length;
  640. }
  641.  
  642. if (AuthorityResourceRecords != null)
  643. foreach (ResourceRecord record in AuthorityResourceRecords)
  644. {
  645. source = record.ToArray();
  646. Array.Copy(source, 0, result, offset, source.Length);
  647. offset += source.Length;
  648. }
  649.  
  650. if (AdditionalResourceRecords != null)
  651. foreach (ResourceRecord record in AdditionalResourceRecords)
  652. {
  653. source = record.ToArray();
  654. Array.Copy(source, 0, result, offset, source.Length);
  655. offset += source.Length;
  656. }
  657.  
  658. if (offset != result.Length)
  659. throw new Exception("Length mismatch");
  660.  
  661. return result;
  662. }
  663. }
  664.  
  665. #endregion
  666.  
  667. #region Declarations
  668.  
  669. private Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
  670. private EndPoint broadcastEndPoint = new IPEndPoint(IPAddress.Parse("255.255.255.255"), BCAST_NS_PORT);
  671. private byte[] localIP = null;
  672. private byte[] localMacAddress = null;
  673. private Thread thread;
  674. private ExtendedTimer updateTimer;
  675. private bool terminate = false;
  676.  
  677. private ArrayList nameList = new ArrayList();
  678.  
  679. private bool capture = false;
  680. private bool denyCaptured = false;
  681.  
  682. #endregion
  683.  
  684. #region Construction / destruction
  685.  
  686. /// <summary>
  687. /// Creates a brand new name service object.
  688. /// Since there is only one UDP 137 port, you should use this class as singleton.
  689. /// </summary>
  690. public NameService()
  691. {
  692. foreach (NetworkInterface networkInterface in NetworkInterface.GetAllNetworkInterfaces())
  693. {
  694. localIP = IPAddress.Parse(networkInterface.IPAddress).GetAddressBytes();
  695. localMacAddress = networkInterface.PhysicalAddress;
  696. break;
  697. }
  698.  
  699. socket.Bind(new IPEndPoint(IPAddress.Any, BCAST_NS_PORT));
  700. updateTimer = new ExtendedTimer(new TimerCallback(OnUpdate), null, Timeout.Infinite, Timeout.Infinite);
  701. thread = new Thread(new ThreadStart(SocketThread));
  702. thread.Start();
  703. thread.Priority = ThreadPriority.AboveNormal;
  704. Thread.Sleep(0);
  705. }
  706.  
  707. /// <summary>
  708. /// Releases used resources.
  709. /// </summary>
  710. public void Dispose()
  711. {
  712. if (updateTimer != null)
  713. {
  714. updateTimer.Dispose();
  715. updateTimer = null;
  716. }
  717.  
  718. // Shut down socket first, so the ReceiveFrom method in thread gets unblocked
  719. if (socket != null)
  720. {
  721. socket.Close();
  722. socket = null;
  723. }
  724.  
  725. if (thread != null)
  726. {
  727. terminate = true;
  728. thread.Join();
  729. thread = null;
  730. }
  731. }
  732.  
  733. #endregion
  734.  
  735. #region Static methods
  736.  
  737. /// <summary>
  738. /// Converts an uncompressed NetBIOS name to a compressed, human-readable name.
  739. /// </summary>
  740. /// <param name="name">The uncompressed NetBIOS name.</param>
  741. /// <param name="suffix">The name suffix as introduced by Microsoft.</param>
  742. /// <returns>The compressed, human-readable name.</returns>
  743. private static string CompressName(string name, out MsSuffix suffix)
  744. {
  745. if (name.Length != 32)
  746. throw new ArgumentException("Unsupported name length, should be 32 characters", "name");
  747.  
  748. suffix = MsSuffix.Default;
  749.  
  750. int offset = 0;
  751. char[] result = new char[15];
  752.  
  753. for (int i = 0; i < 16; i++)
  754. {
  755. byte b1 = (byte)(name[offset++] - 'A');
  756. byte b2 = (byte)(name[offset++] - 'A');
  757.  
  758. if ((b1 > 15) || (b2 > 15))
  759. throw new ArgumentException("Invalid characters in name", "name");
  760.  
  761. b1 <<= 4;
  762. b1 += b2;
  763.  
  764. if (i < 15)
  765. result[i] = (char)b1;
  766. else
  767. suffix = (MsSuffix)b1;
  768. }
  769.  
  770. return new string(result).TrimEnd(new char[] { ' ' });
  771. }
  772.  
  773. /// <summary>
  774. /// Converts a human-readable name to an uncompressed NetBIOS name.
  775. /// </summary>
  776. /// <param name="name">The compressed, human-readable name.</param>
  777. /// <param name="suffix">The name suffix as introduced by Microsoft.</param>
  778. /// <returns>The uncompressed NetBIOS name.</returns>
  779. private static string UncompressName(string name, MsSuffix suffix)
  780. {
  781. if (name.Length > 15)
  782. throw new ArgumentException("Name cannot contain more than 15 characters");
  783.  
  784. char[] result = new char[32];
  785. int offset = 0;
  786.  
  787. for (int i = 0; i < 15; i++)
  788. {
  789. char c = i < name.Length ? name[i] : ' ';
  790. result[offset++] = (char)('A' + (c >> 4));
  791. result[offset++] = (char)('A' + (c & 15));
  792. }
  793. result[offset++] = (char)('A' + ((byte)suffix >> 4));
  794. result[offset++] = (char)('A' + ((byte)suffix & 15));
  795.  
  796. return new string(result);
  797. }
  798.  
  799. #endregion
  800.  
  801. #region Thread & timer
  802.  
  803. private void SocketThread()
  804. {
  805. byte[] buffer = new byte[1024];
  806. EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 137);
  807.  
  808. while (!terminate)
  809. {
  810. int count = socket.ReceiveFrom(buffer, ref remoteEndPoint); // Blocking call, returns 0 when socket is closed
  811.  
  812. if (count == 0)
  813. break; // Socket has been closed
  814.  
  815. // Don't check own messages
  816. if ((remoteEndPoint as IPEndPoint).Address.Equals(IPAddress.Loopback))
  817. continue;
  818.  
  819. ProcessReceivedPacket(buffer, count, remoteEndPoint);
  820. }
  821. }
  822.  
  823. private void OnUpdate(object o)
  824. {
  825. lock (nameList)
  826. foreach (Name name in nameList)
  827. Request(name, HeaderOpcode.Refresh);
  828.  
  829. updateTimer.Change(NAME_UPDATE_INTERVAL_MS, Timeout.Infinite);
  830. }
  831.  
  832. #endregion
  833.  
  834. #region Private methods
  835.  
  836. private void ProcessReceivedPacket(byte[] data, int size, EndPoint remoteEndPoint)
  837. {
  838. // TODO: verify the size one day
  839. Packet packet = Packet.Parse(data);
  840. //Header header = Header.Parse(data); // Only capture header initially and not the full packet (need 4 speed)
  841.  
  842. if (capture && (packet.Header.NameTrnId == NAME_TRN_ID) && (packet.Header.Opcode != 0))
  843. denyCaptured = true; // Other node denied our registration request
  844.  
  845. if (!packet.Header.Response && (packet.Header.QdCount > 0))
  846. { // We received a request
  847. switch (packet.Header.Opcode)
  848. {
  849. case HeaderOpcode.Query:
  850. if (packet.QuestionEntries[0].UncompressedName == "CKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
  851. { // "*" received
  852. StatusResponse(packet.Header.NameTrnId, remoteEndPoint);
  853. }
  854. else
  855. {
  856. lock (nameList)
  857. {
  858. foreach (Name name in nameList)
  859. {
  860. if (name.UncompressedName == packet.QuestionEntries[0].UncompressedName)
  861. { // We own the name
  862. Response(name, HeaderOpcode.Query, 0, packet.Header.NameTrnId, remoteEndPoint);
  863. }
  864. }
  865. }
  866. }
  867. break;
  868. case HeaderOpcode.Registration:
  869. lock (nameList)
  870. {
  871. foreach (Name name in nameList)
  872. {
  873. if (name.Type == NameType.Group)
  874. continue;
  875.  
  876. if (name.UncompressedName == packet.QuestionEntries[0].UncompressedName)
  877. { // We own the name, send negative response (0x06 ACT_ERR : Active error, name owned)
  878. Response(name, HeaderOpcode.Registration, 6, packet.Header.NameTrnId, remoteEndPoint);
  879. }
  880. }
  881. }
  882. break;
  883. }
  884. }
  885. }
  886.  
  887. private void StartCapture()
  888. {
  889. denyCaptured = false;
  890. capture = true;
  891. }
  892.  
  893. private bool StopCapture()
  894. {
  895. capture = false;
  896. return !denyCaptured;
  897. }
  898.  
  899. private void Request(Name name, HeaderOpcode opcode)
  900. {
  901. bool recursionDesired = true;
  902.  
  903. switch (opcode)
  904. {
  905. case HeaderOpcode.Update:
  906. opcode = HeaderOpcode.Registration;
  907. recursionDesired = false;
  908. break;
  909. case HeaderOpcode.Release:
  910. case HeaderOpcode.WACK:
  911. case HeaderOpcode.Refresh:
  912. recursionDesired = false;
  913. break;
  914. }
  915.  
  916. Packet packet = new Packet();
  917. packet.Header.NameTrnId = NAME_TRN_ID;
  918. packet.Header.Opcode = opcode;
  919. packet.Header.Flags = HeaderFlags.Broadcast;
  920. if (recursionDesired) packet.Header.Flags |= HeaderFlags.RecursionDesired;
  921. packet.Header.Rcode = 0;
  922. packet.Header.QdCount = 1;
  923. packet.Header.ArCount = 1;
  924.  
  925. packet.QuestionEntries = new QuestionName[1];
  926. packet.QuestionEntries[0].UncompressedName = name.UncompressedName;
  927. packet.QuestionEntries[0].Type = QuestionType.NB;
  928. packet.QuestionEntries[0].Class = QuestionClass.IN;
  929.  
  930. packet.AdditionalResourceRecords = new ResourceRecord[1];
  931. packet.AdditionalResourceRecords[0].UncompressedName = name.UncompressedName; // TODO: Should use a pointer here
  932. packet.AdditionalResourceRecords[0].Type = ResourceRecordType.NB;
  933. packet.AdditionalResourceRecords[0].Class = ResourceRecordClass.IN;
  934. packet.AdditionalResourceRecords[0].TTL = 0;
  935.  
  936. byte[] data = new byte[6];
  937. data[0] = (byte)(name.Type == NameType.Group ? 0x80 : 0x00); // NB_FLAGS / bit 15: Group name flag, bit 16-15: 00 B node, 01 P node, 10 M node, 11 reserved, bit 14-8: reserved
  938. data[1] = 0x00; // NB_FLAGS
  939. // NB_ADDRESS
  940. for (int i = 0; i < 4; i++)
  941. data[i + 2] = localIP[i];
  942.  
  943. packet.AdditionalResourceRecords[0].Data = data;
  944.  
  945. data = packet.ToArray();
  946.  
  947. lock (socket)
  948. socket.SendTo(data, broadcastEndPoint);
  949. }
  950.  
  951. private void Response(Name name, HeaderOpcode opcode, byte rcode, ushort nameTrnId, EndPoint remoteEndPoint)
  952. {
  953. Packet packet = new Packet();
  954. packet.Header.Response = true;
  955. packet.Header.NameTrnId = nameTrnId;
  956. packet.Header.Opcode = opcode;
  957. packet.Header.Flags = HeaderFlags.AuthoritativeAnswer | HeaderFlags.RecursionDesired;
  958. packet.Header.Rcode = rcode;
  959. packet.Header.AnCount = 1;
  960.  
  961. packet.AnswerResourceRecords = new ResourceRecord[1];
  962. packet.AnswerResourceRecords[0].UncompressedName = name.UncompressedName;
  963. packet.AnswerResourceRecords[0].Type = ResourceRecordType.NB;
  964. packet.AnswerResourceRecords[0].Class = ResourceRecordClass.IN;
  965. packet.AnswerResourceRecords[0].TTL = 0;
  966.  
  967. byte[] data = new byte[6];
  968. data[0] = (byte)(name.Type == NameType.Group ? 0x80 : 0x00); // NB_FLAGS / bit 15: Group name flag, bit 16-15: 00 B node, 01 P node, 10 M node, 11 reserved, bit 14-8: reserved
  969. data[1] = 0x00; // NB_FLAGS
  970. // NB_ADDRESS
  971. for (int i = 0; i < 4; i++)
  972. data[i + 2] = localIP[i];
  973.  
  974. packet.AnswerResourceRecords[0].Data = data;
  975.  
  976. data = packet.ToArray();
  977.  
  978. try
  979. {
  980. lock (socket)
  981. socket.SendTo(data, remoteEndPoint);
  982. }
  983. catch
  984. {
  985. // Handles situations where the remote host is not accessable through the network.
  986. }
  987. }
  988.  
  989. private void StatusResponse(ushort nameTrnId, EndPoint remoteEndPoint)
  990. {
  991. lock (nameList)
  992. if (nameList.Count == 0)
  993. return;
  994.  
  995. Packet packet = new Packet();
  996. packet.Header.Response = true;
  997. packet.Header.NameTrnId = nameTrnId;
  998. packet.Header.Opcode = (HeaderOpcode)0;
  999. packet.Header.Flags = HeaderFlags.AuthoritativeAnswer;
  1000. packet.Header.Rcode = 0;
  1001. packet.Header.AnCount = 1;
  1002.  
  1003. packet.AnswerResourceRecords = new ResourceRecord[1];
  1004. packet.AnswerResourceRecords[0].UncompressedName = "CKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";//uncompressedName;
  1005. packet.AnswerResourceRecords[0].Type = ResourceRecordType.NBSTAT;
  1006. packet.AnswerResourceRecords[0].Class = ResourceRecordClass.IN;
  1007. packet.AnswerResourceRecords[0].TTL = 0;
  1008.  
  1009. int length = 0;
  1010. byte[] data;
  1011. lock (nameList)
  1012. {
  1013. length += 1; // NUM_NAMES
  1014. foreach (Name name in nameList)
  1015. {
  1016. length += 16;
  1017. length += 2; // NAME_FLAGS
  1018. }
  1019. length += 46; // STATISTICS
  1020.  
  1021. int offset = 0;
  1022. data = new byte[length];
  1023. data[offset++] = (byte)nameList.Count; // NUM_NAMES
  1024.  
  1025. bool first = true;
  1026. foreach (Name name in nameList)
  1027. {
  1028. MsSuffix suffix;
  1029. string value = CompressName(name.UncompressedName, out suffix);
  1030. while (value.Length < 15)
  1031. value += ' ';
  1032.  
  1033. Array.Copy(Encoding.UTF8.GetBytes(value), 0, data, offset, 15);
  1034. offset += 15;
  1035. data[offset++] = (byte)suffix;
  1036.  
  1037. // NAME_FLAGS
  1038. //+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
  1039. //| G | ONT |DRG|CNF|ACT|PRM| RESERVED |
  1040. //+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
  1041.  
  1042. data[offset++] = (byte)((name.Type == NameType.Group ? 0x80 : 0x00) + 0x04 + (first ? 0x02 : 0x00)); // treat first name as permanent name
  1043. data[offset++] = 0;
  1044.  
  1045. first = false;
  1046. }
  1047.  
  1048. // Statistics - only fill in mac address
  1049. Array.Copy(localMacAddress, 0, data, offset, localMacAddress.Length);
  1050. offset += localMacAddress.Length;
  1051. }
  1052.  
  1053. packet.AnswerResourceRecords[0].Data = data;
  1054.  
  1055. data = packet.ToArray();
  1056.  
  1057. try
  1058. {
  1059. lock (socket)
  1060. socket.SendTo(data, remoteEndPoint);
  1061. }
  1062. catch
  1063. {
  1064. // Handles situations where the remote host is not accessable through the network.
  1065. }
  1066. }
  1067.  
  1068. #endregion
  1069.  
  1070. #region Public methods
  1071.  
  1072. /// <summary>
  1073. /// Add a netbios name.
  1074. /// </summary>
  1075. /// <param name="name">The name to add.</param>
  1076. /// <param name="type">The type to use.</param>
  1077. /// <param name="suffix">The suffix to use.</param>
  1078. /// <returns>True on success, false on failure.</returns>
  1079. public bool AddName(string name, NameType type, MsSuffix suffix)
  1080. {
  1081. Name node = new Name();
  1082. node.UncompressedName = UncompressName(name, suffix);
  1083. node.Type = type;
  1084.  
  1085. // Send request
  1086. StartCapture();
  1087.  
  1088. for (int i = 0; i < BCAST_REQ_RETRY_COUNT; i++)
  1089. {
  1090. Request(node, HeaderOpcode.Registration);
  1091. Thread.Sleep(3 * BCAST_REQ_RETRY_TIMEOUT); // Three times, otherwise FEZ can't follow :(
  1092.  
  1093. if (denyCaptured)
  1094. break;
  1095. }
  1096.  
  1097. if (!StopCapture())
  1098. return false; // Name in use
  1099.  
  1100. Request(node, HeaderOpcode.Update);
  1101.  
  1102. lock (nameList)
  1103. nameList.Add(node);
  1104.  
  1105. updateTimer.Change(NAME_UPDATE_INTERVAL_MS, Timeout.Infinite);
  1106.  
  1107. return true;
  1108. }
  1109.  
  1110. /// <summary>
  1111. /// Remove a netbios name.
  1112. /// </summary>
  1113. /// <param name="name">The name to remove.</param>
  1114. /// <param name="type">The type used.</param>
  1115. /// <param name="suffix">The suffix used.</param>
  1116. /// <returns>True on success, false on failure.</returns>
  1117. public bool RemoveName(string name, NameType type, MsSuffix suffix)
  1118. {
  1119. Name node = new Name();
  1120. node.UncompressedName = UncompressName(name, suffix);
  1121. node.Type = type;
  1122.  
  1123. lock (nameList)
  1124. {
  1125. int i = 0;
  1126. for (; i < nameList.Count; i++)
  1127. {
  1128. Name n = (Name)nameList[i];
  1129. if ((n.UncompressedName == node.UncompressedName) && (n.Type == node.Type))
  1130. break;
  1131. }
  1132.  
  1133. if (i >= nameList.Count)
  1134. return false; // Name not found
  1135.  
  1136. nameList.RemoveAt(i);
  1137.  
  1138. if (nameList.Count == 0)
  1139. updateTimer.Change(Timeout.Infinite, Timeout.Infinite);
  1140. }
  1141.  
  1142. // Send request
  1143. for (int i = 0; i < BCAST_REQ_RETRY_COUNT; i++)
  1144. {
  1145. Request(node, HeaderOpcode.Release);
  1146. Thread.Sleep(BCAST_REQ_RETRY_TIMEOUT);
  1147. }
  1148.  
  1149. return true;
  1150. }
  1151.  
  1152. #endregion
  1153. }
  1154. }
  1155.