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   |   1382 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.