UniAddress.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  1. // This code is derived from jcifs smb client library <jcifs at samba dot org>
  2. // Ported by J. Arturo <webmaster at komodosoft dot net>
  3. //
  4. // This library is free software; you can redistribute it and/or
  5. // modify it under the terms of the GNU Lesser General Public
  6. // License as published by the Free Software Foundation; either
  7. // version 2.1 of the License, or (at your option) any later version.
  8. //
  9. // This library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. // Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public
  15. // License along with this library; if not, write to the Free Software
  16. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  17. using System;
  18. using System.IO;
  19. using System.Linq;
  20. using System.Net;
  21. using SharpCifs.Netbios;
  22. using SharpCifs.Util;
  23. using SharpCifs.Util.Sharpen;
  24. using Extensions = SharpCifs.Util.Sharpen.Extensions;
  25. using System.Threading.Tasks;
  26. namespace SharpCifs
  27. {
  28. /// <summary>
  29. /// <p>Under normal conditions it is not necessary to use
  30. /// this class to use jCIFS properly.
  31. /// </summary>
  32. /// <remarks>
  33. /// <p>Under normal conditions it is not necessary to use
  34. /// this class to use jCIFS properly. Name resolusion is
  35. /// handled internally to the <code>jcifs.smb</code> package.
  36. /// <p>
  37. /// This class is a wrapper for both
  38. /// <see cref="Jcifs.Netbios.NbtAddress">Jcifs.Netbios.NbtAddress</see>
  39. /// and
  40. /// <see cref="System.Net.IPAddress">System.Net.IPAddress</see>
  41. /// . The name resolution mechanisms
  42. /// used will systematically query all available configured resolution
  43. /// services including WINS, broadcasts, DNS, and LMHOSTS. See
  44. /// <a href="../../resolver.html">Setting Name Resolution Properties</a>
  45. /// and the <code>jcifs.resolveOrder</code> property. Changing
  46. /// jCIFS name resolution properties can greatly affect the behavior of
  47. /// the client and may be necessary for proper operation.
  48. /// <p>
  49. /// This class should be used in favor of <tt>InetAddress</tt> to resolve
  50. /// hostnames on LANs and WANs that support a mixture of NetBIOS/WINS and
  51. /// DNS resolvable hosts.
  52. /// </remarks>
  53. public class UniAddress
  54. {
  55. private const int ResolverWins = 0;
  56. private const int ResolverBcast = 1;
  57. private const int ResolverDns = 2;
  58. private const int ResolverLmhosts = 3;
  59. private static int[] _resolveOrder;
  60. private static IPAddress _baddr;
  61. private static LogStream _log = LogStream.GetInstance();
  62. static UniAddress()
  63. {
  64. string ro = Config.GetProperty("jcifs.resolveOrder");
  65. IPAddress nbns = NbtAddress.GetWinsAddress();
  66. try
  67. {
  68. _baddr = Config.GetInetAddress("jcifs.netbios.baddr",
  69. Extensions.GetAddressByName("255.255.255.255"));
  70. }
  71. catch (UnknownHostException)
  72. {
  73. }
  74. if (string.IsNullOrEmpty(ro))
  75. {
  76. if (nbns == null)
  77. {
  78. _resolveOrder = new int[3];
  79. _resolveOrder[0] = ResolverLmhosts;
  80. _resolveOrder[1] = ResolverDns;
  81. _resolveOrder[2] = ResolverBcast;
  82. }
  83. else
  84. {
  85. _resolveOrder = new int[4];
  86. _resolveOrder[0] = ResolverLmhosts;
  87. _resolveOrder[1] = ResolverWins;
  88. _resolveOrder[2] = ResolverDns;
  89. _resolveOrder[3] = ResolverBcast;
  90. }
  91. }
  92. else
  93. {
  94. int[] tmp = new int[4];
  95. StringTokenizer st = new StringTokenizer(ro, ",");
  96. int i = 0;
  97. while (st.HasMoreTokens())
  98. {
  99. string s = st.NextToken().Trim();
  100. if (Runtime.EqualsIgnoreCase(s, "LMHOSTS"))
  101. {
  102. tmp[i++] = ResolverLmhosts;
  103. }
  104. else
  105. {
  106. if (Runtime.EqualsIgnoreCase(s, "WINS"))
  107. {
  108. if (nbns == null)
  109. {
  110. if (_log.Level > 1)
  111. {
  112. _log.WriteLine(
  113. "UniAddress resolveOrder specifies WINS however the "
  114. + "jcifs.netbios.wins property has not been set");
  115. }
  116. continue;
  117. }
  118. tmp[i++] = ResolverWins;
  119. }
  120. else
  121. {
  122. if (Runtime.EqualsIgnoreCase(s, "BCAST"))
  123. {
  124. tmp[i++] = ResolverBcast;
  125. }
  126. else
  127. {
  128. if (Runtime.EqualsIgnoreCase(s, "DNS"))
  129. {
  130. tmp[i++] = ResolverDns;
  131. }
  132. else
  133. {
  134. if (_log.Level > 1)
  135. {
  136. _log.WriteLine("unknown resolver method: " + s);
  137. }
  138. }
  139. }
  140. }
  141. }
  142. }
  143. _resolveOrder = new int[i];
  144. Array.Copy(tmp, 0, _resolveOrder, 0, i);
  145. }
  146. }
  147. internal class Sem
  148. {
  149. internal Sem(int count)
  150. {
  151. this.Count = count;
  152. }
  153. internal int Count;
  154. }
  155. internal class QueryThread : Thread
  156. {
  157. internal Sem Sem;
  158. internal string Host;
  159. internal string Scope;
  160. internal int Type;
  161. internal NbtAddress[] Ans;
  162. internal IPAddress Svr;
  163. internal UnknownHostException Uhe;
  164. internal QueryThread(Sem sem,
  165. string host,
  166. int type,
  167. string scope,
  168. IPAddress svr)
  169. : base("JCIFS-QueryThread: " + host)
  170. {
  171. this.Sem = sem;
  172. this.Host = host;
  173. this.Type = type;
  174. this.Scope = scope;
  175. this.Svr = svr;
  176. }
  177. public override void Run()
  178. {
  179. try
  180. {
  181. //Ans = new [] { NbtAddress.GetByName(Host, Type, Scope, Svr) };
  182. Ans = NbtAddress.GetAllByName(Host, Type, Scope, Svr);
  183. }
  184. catch (UnknownHostException uhe)
  185. {
  186. this.Uhe = uhe;
  187. }
  188. catch (Exception ex)
  189. {
  190. Uhe = new UnknownHostException(ex.Message);
  191. }
  192. finally
  193. {
  194. lock (Sem)
  195. {
  196. Sem.Count--;
  197. Runtime.Notify(Sem);
  198. }
  199. }
  200. }
  201. }
  202. /// <exception cref="UnknownHostException"></exception>
  203. internal static NbtAddress[] LookupServerOrWorkgroup(string name, IPAddress svr)
  204. {
  205. Sem sem = new Sem(2);
  206. int type = NbtAddress.IsWins(svr) ? unchecked(0x1b) : unchecked(0x1d);
  207. QueryThread q1X = new QueryThread(sem, name, type, null, svr);
  208. QueryThread q20 = new QueryThread(sem, name, unchecked(0x20), null, svr);
  209. q1X.SetDaemon(true);
  210. q20.SetDaemon(true);
  211. try
  212. {
  213. lock (sem)
  214. {
  215. q1X.Start();
  216. q20.Start();
  217. while (sem.Count > 0 && q1X.Ans == null && q20.Ans == null)
  218. {
  219. Runtime.Wait(sem);
  220. }
  221. }
  222. }
  223. catch (Exception)
  224. {
  225. throw new UnknownHostException(name);
  226. }
  227. if (q1X.Ans != null)
  228. {
  229. return q1X.Ans;
  230. }
  231. if (q20.Ans != null)
  232. {
  233. return q20.Ans;
  234. }
  235. throw q1X.Uhe;
  236. }
  237. /// <summary>Determines the address of a host given it's host name.</summary>
  238. /// <remarks>
  239. /// Determines the address of a host given it's host name. The name can be a
  240. /// machine name like "jcifs.samba.org", or an IP address like "192.168.1.15".
  241. /// </remarks>
  242. /// <param name="hostname">NetBIOS or DNS hostname to resolve</param>
  243. /// <exception cref="UnknownHostException">if there is an error resolving the name
  244. /// </exception>
  245. public static UniAddress GetByName(string hostname)
  246. {
  247. return GetByName(hostname, false);
  248. }
  249. internal static bool IsDotQuadIp(string hostname)
  250. {
  251. if (char.IsDigit(hostname[0]))
  252. {
  253. int i;
  254. int len;
  255. int dots;
  256. char[] data;
  257. i = dots = 0;
  258. len = hostname.Length;
  259. data = hostname.ToCharArray();
  260. while (i < len && char.IsDigit(data[i++]))
  261. {
  262. if (i == len && dots == 3)
  263. {
  264. // probably an IP address
  265. return true;
  266. }
  267. if (i < len && data[i] == '.')
  268. {
  269. dots++;
  270. i++;
  271. }
  272. }
  273. }
  274. return false;
  275. }
  276. internal static bool IsAllDigits(string hostname)
  277. {
  278. for (int i = 0; i < hostname.Length; i++)
  279. {
  280. if (char.IsDigit(hostname[i]) == false)
  281. {
  282. return false;
  283. }
  284. }
  285. return true;
  286. }
  287. /// <summary>Lookup <tt>hostname</tt> and return it's <tt>UniAddress</tt>.</summary>
  288. /// <remarks>
  289. /// Lookup <tt>hostname</tt> and return it's <tt>UniAddress</tt>. If the
  290. /// <tt>possibleNTDomainOrWorkgroup</tt> parameter is <tt>true</tt> an
  291. /// addtional name query will be performed to locate a master browser.
  292. /// </remarks>
  293. /// <exception cref="UnknownHostException"></exception>
  294. public static UniAddress GetByName(string hostname, bool possibleNtDomainOrWorkgroup)
  295. {
  296. UniAddress[] addrs = GetAllByName(hostname,
  297. possibleNtDomainOrWorkgroup);
  298. return addrs[0];
  299. }
  300. /// <exception cref="UnknownHostException"></exception>
  301. public static UniAddress[] GetAllByName(string hostname, bool possibleNtDomainOrWorkgroup)
  302. {
  303. object addr;
  304. int i;
  305. if (string.IsNullOrEmpty(hostname))
  306. {
  307. throw new UnknownHostException();
  308. }
  309. if (IsDotQuadIp(hostname))
  310. {
  311. UniAddress[] addrs = new UniAddress[1];
  312. addrs[0] = new UniAddress(NbtAddress.GetByName(hostname));
  313. return addrs;
  314. }
  315. for (i = 0; i < _resolveOrder.Length; i++)
  316. {
  317. try
  318. {
  319. switch (_resolveOrder[i])
  320. {
  321. case ResolverLmhosts:
  322. {
  323. if ((addr = Lmhosts.GetByName(hostname)) == null)
  324. {
  325. continue;
  326. }
  327. break;
  328. }
  329. case ResolverWins:
  330. {
  331. if (hostname == NbtAddress.MasterBrowserName
  332. || hostname.Length > 15)
  333. {
  334. // invalid netbios name
  335. continue;
  336. }
  337. if (possibleNtDomainOrWorkgroup)
  338. {
  339. addr = LookupServerOrWorkgroup(hostname,
  340. NbtAddress.GetWinsAddress());
  341. }
  342. else
  343. {
  344. addr = NbtAddress.GetByName(hostname,
  345. unchecked(0x20),
  346. null,
  347. NbtAddress.GetWinsAddress());
  348. }
  349. break;
  350. }
  351. case ResolverBcast:
  352. {
  353. if (hostname.Length > 15)
  354. {
  355. // invalid netbios name
  356. continue;
  357. }
  358. try
  359. {
  360. if (possibleNtDomainOrWorkgroup)
  361. {
  362. NbtAddress[] iaddrs = LookupServerOrWorkgroup(hostname,
  363. _baddr);
  364. UniAddress[] addrs = new UniAddress[iaddrs.Length];
  365. for (int ii = 0; ii < iaddrs.Length; ii++)
  366. {
  367. addrs[ii] = new UniAddress(iaddrs[ii]);
  368. }
  369. return addrs;
  370. }
  371. else
  372. {
  373. addr = NbtAddress.GetByName(hostname,
  374. unchecked(0x20),
  375. null,
  376. _baddr);
  377. }
  378. }
  379. catch (Exception ex)
  380. {
  381. if (i == _resolveOrder.Length - 1)
  382. {
  383. throw ex;
  384. }
  385. else
  386. {
  387. continue;
  388. }
  389. }
  390. break;
  391. }
  392. case ResolverDns:
  393. {
  394. if (IsAllDigits(hostname))
  395. {
  396. throw new UnknownHostException(hostname);
  397. }
  398. IPAddress[] iaddrs = Extensions.GetAddressesByName(hostname);
  399. if (iaddrs == null || iaddrs.Length == 0)
  400. {
  401. continue;
  402. }
  403. return iaddrs.Select(iaddr => new UniAddress(iaddr)).ToArray();
  404. }
  405. default:
  406. {
  407. // Success
  408. throw new UnknownHostException(hostname);
  409. }
  410. }
  411. UniAddress[] addrs1 = new UniAddress[1];
  412. addrs1[0] = new UniAddress(addr);
  413. return addrs1;
  414. }
  415. catch (IOException)
  416. {
  417. }
  418. }
  419. // Success
  420. // Failure
  421. throw new UnknownHostException(hostname);
  422. }
  423. internal object Addr;
  424. internal string CalledName;
  425. /// <summary>
  426. /// Create a <tt>UniAddress</tt> by wrapping an <tt>InetAddress</tt> or
  427. /// <tt>NbtAddress</tt>.
  428. /// </summary>
  429. /// <remarks>
  430. /// Create a <tt>UniAddress</tt> by wrapping an <tt>InetAddress</tt> or
  431. /// <tt>NbtAddress</tt>.
  432. /// </remarks>
  433. public UniAddress(object addr)
  434. {
  435. if (addr == null)
  436. {
  437. throw new ArgumentException();
  438. }
  439. this.Addr = addr;
  440. }
  441. /// <summary>Return the IP address of this address as a 32 bit integer.</summary>
  442. /// <remarks>Return the IP address of this address as a 32 bit integer.</remarks>
  443. public override int GetHashCode()
  444. {
  445. return Addr.GetHashCode();
  446. }
  447. /// <summary>Compare two addresses for equality.</summary>
  448. /// <remarks>
  449. /// Compare two addresses for equality. Two <tt>UniAddress</tt>s are equal
  450. /// if they are both <tt>UniAddress</tt>' and refer to the same IP address.
  451. /// </remarks>
  452. public override bool Equals(object obj)
  453. {
  454. return obj is UniAddress && Addr.Equals(((UniAddress)obj).Addr);
  455. }
  456. /// <summary>Guess first called name to try for session establishment.</summary>
  457. /// <remarks>
  458. /// Guess first called name to try for session establishment. This
  459. /// method is used exclusively by the <tt>jcifs.smb</tt> package.
  460. /// </remarks>
  461. public virtual string FirstCalledName()
  462. {
  463. if (Addr is NbtAddress)
  464. {
  465. return ((NbtAddress)Addr).FirstCalledName();
  466. }
  467. CalledName = ((IPAddress)Addr).GetHostAddress();
  468. if (IsDotQuadIp(CalledName))
  469. {
  470. CalledName = NbtAddress.SmbserverName;
  471. }
  472. else
  473. {
  474. int i = CalledName.IndexOf('.');
  475. if (i > 1 && i < 15)
  476. {
  477. CalledName = Runtime.Substring(CalledName, 0, i).ToUpper();
  478. }
  479. else
  480. {
  481. if (CalledName.Length > 15)
  482. {
  483. CalledName = NbtAddress.SmbserverName;
  484. }
  485. else
  486. {
  487. CalledName = CalledName.ToUpper();
  488. }
  489. }
  490. }
  491. return CalledName;
  492. }
  493. /// <summary>Guess next called name to try for session establishment.</summary>
  494. /// <remarks>
  495. /// Guess next called name to try for session establishment. This
  496. /// method is used exclusively by the <tt>jcifs.smb</tt> package.
  497. /// </remarks>
  498. public virtual string NextCalledName()
  499. {
  500. if (Addr is NbtAddress)
  501. {
  502. return ((NbtAddress)Addr).NextCalledName();
  503. }
  504. if (CalledName != NbtAddress.SmbserverName)
  505. {
  506. CalledName = NbtAddress.SmbserverName;
  507. return CalledName;
  508. }
  509. return null;
  510. }
  511. /// <summary>Return the underlying <tt>NbtAddress</tt> or <tt>InetAddress</tt>.</summary>
  512. /// <remarks>Return the underlying <tt>NbtAddress</tt> or <tt>InetAddress</tt>.</remarks>
  513. public virtual object GetAddress()
  514. {
  515. return Addr;
  516. }
  517. /// <summary>Return the hostname of this address such as "MYCOMPUTER".</summary>
  518. /// <remarks>Return the hostname of this address such as "MYCOMPUTER".</remarks>
  519. public virtual string GetHostName()
  520. {
  521. if (Addr is NbtAddress)
  522. {
  523. return ((NbtAddress)Addr).GetHostName();
  524. }
  525. return ((IPAddress)Addr).GetHostAddress();
  526. }
  527. /// <summary>Return the IP address as text such as "192.168.1.15".</summary>
  528. /// <remarks>Return the IP address as text such as "192.168.1.15".</remarks>
  529. public virtual string GetHostAddress()
  530. {
  531. if (Addr is NbtAddress)
  532. {
  533. return ((NbtAddress)Addr).GetHostAddress();
  534. }
  535. return ((IPAddress)Addr).GetHostAddress();
  536. }
  537. public virtual IPAddress GetHostIpAddress()
  538. {
  539. return (IPAddress)Addr;
  540. }
  541. /// <summary>
  542. /// Return the a text representation of this address such as
  543. /// <tt>MYCOMPUTER/192.168.1.15</tt>.
  544. /// </summary>
  545. /// <remarks>
  546. /// Return the a text representation of this address such as
  547. /// <tt>MYCOMPUTER/192.168.1.15</tt>.
  548. /// </remarks>
  549. public override string ToString()
  550. {
  551. return Addr.ToString();
  552. }
  553. }
  554. }