mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-11-20 04:18:51 -07:00
604 lines
22 KiB
C#
604 lines
22 KiB
C#
// This code is derived from jcifs smb client library <jcifs at samba dot org>
|
|
// Ported by J. Arturo <webmaster at komodosoft dot net>
|
|
//
|
|
// This library is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU Lesser General Public
|
|
// License as published by the Free Software Foundation; either
|
|
// version 2.1 of the License, or (at your option) any later version.
|
|
//
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
// License along with this library; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using SharpCifs.Netbios;
|
|
using SharpCifs.Util;
|
|
using SharpCifs.Util.Sharpen;
|
|
using Extensions = SharpCifs.Util.Sharpen.Extensions;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace SharpCifs
|
|
{
|
|
/// <summary>
|
|
/// <p>Under normal conditions it is not necessary to use
|
|
/// this class to use jCIFS properly.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <p>Under normal conditions it is not necessary to use
|
|
/// this class to use jCIFS properly. Name resolusion is
|
|
/// handled internally to the <code>jcifs.smb</code> package.
|
|
/// <p>
|
|
/// This class is a wrapper for both
|
|
/// <see cref="Jcifs.Netbios.NbtAddress">Jcifs.Netbios.NbtAddress</see>
|
|
/// and
|
|
/// <see cref="System.Net.IPAddress">System.Net.IPAddress</see>
|
|
/// . The name resolution mechanisms
|
|
/// used will systematically query all available configured resolution
|
|
/// services including WINS, broadcasts, DNS, and LMHOSTS. See
|
|
/// <a href="../../resolver.html">Setting Name Resolution Properties</a>
|
|
/// and the <code>jcifs.resolveOrder</code> property. Changing
|
|
/// jCIFS name resolution properties can greatly affect the behavior of
|
|
/// the client and may be necessary for proper operation.
|
|
/// <p>
|
|
/// This class should be used in favor of <tt>InetAddress</tt> to resolve
|
|
/// hostnames on LANs and WANs that support a mixture of NetBIOS/WINS and
|
|
/// DNS resolvable hosts.
|
|
/// </remarks>
|
|
public class UniAddress
|
|
{
|
|
private const int ResolverWins = 0;
|
|
|
|
private const int ResolverBcast = 1;
|
|
|
|
private const int ResolverDns = 2;
|
|
|
|
private const int ResolverLmhosts = 3;
|
|
|
|
private static int[] _resolveOrder;
|
|
|
|
private static IPAddress _baddr;
|
|
|
|
private static LogStream _log = LogStream.GetInstance();
|
|
|
|
static UniAddress()
|
|
{
|
|
string ro = Config.GetProperty("jcifs.resolveOrder");
|
|
IPAddress nbns = NbtAddress.GetWinsAddress();
|
|
try
|
|
{
|
|
_baddr = Config.GetInetAddress("jcifs.netbios.baddr",
|
|
Extensions.GetAddressByName("255.255.255.255"));
|
|
}
|
|
catch (UnknownHostException)
|
|
{
|
|
}
|
|
if (string.IsNullOrEmpty(ro))
|
|
{
|
|
if (nbns == null)
|
|
{
|
|
_resolveOrder = new int[3];
|
|
_resolveOrder[0] = ResolverLmhosts;
|
|
_resolveOrder[1] = ResolverDns;
|
|
_resolveOrder[2] = ResolverBcast;
|
|
}
|
|
else
|
|
{
|
|
_resolveOrder = new int[4];
|
|
_resolveOrder[0] = ResolverLmhosts;
|
|
_resolveOrder[1] = ResolverWins;
|
|
_resolveOrder[2] = ResolverDns;
|
|
_resolveOrder[3] = ResolverBcast;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int[] tmp = new int[4];
|
|
StringTokenizer st = new StringTokenizer(ro, ",");
|
|
int i = 0;
|
|
while (st.HasMoreTokens())
|
|
{
|
|
string s = st.NextToken().Trim();
|
|
if (Runtime.EqualsIgnoreCase(s, "LMHOSTS"))
|
|
{
|
|
tmp[i++] = ResolverLmhosts;
|
|
}
|
|
else
|
|
{
|
|
if (Runtime.EqualsIgnoreCase(s, "WINS"))
|
|
{
|
|
if (nbns == null)
|
|
{
|
|
if (_log.Level > 1)
|
|
{
|
|
_log.WriteLine(
|
|
"UniAddress resolveOrder specifies WINS however the "
|
|
+ "jcifs.netbios.wins property has not been set");
|
|
}
|
|
continue;
|
|
}
|
|
tmp[i++] = ResolverWins;
|
|
}
|
|
else
|
|
{
|
|
if (Runtime.EqualsIgnoreCase(s, "BCAST"))
|
|
{
|
|
tmp[i++] = ResolverBcast;
|
|
}
|
|
else
|
|
{
|
|
if (Runtime.EqualsIgnoreCase(s, "DNS"))
|
|
{
|
|
tmp[i++] = ResolverDns;
|
|
}
|
|
else
|
|
{
|
|
if (_log.Level > 1)
|
|
{
|
|
_log.WriteLine("unknown resolver method: " + s);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_resolveOrder = new int[i];
|
|
Array.Copy(tmp, 0, _resolveOrder, 0, i);
|
|
}
|
|
}
|
|
|
|
internal class Sem
|
|
{
|
|
internal Sem(int count)
|
|
{
|
|
this.Count = count;
|
|
}
|
|
|
|
internal int Count;
|
|
}
|
|
|
|
internal class QueryThread : Thread
|
|
{
|
|
internal Sem Sem;
|
|
|
|
internal string Host;
|
|
|
|
internal string Scope;
|
|
|
|
internal int Type;
|
|
|
|
internal NbtAddress[] Ans;
|
|
|
|
internal IPAddress Svr;
|
|
|
|
internal UnknownHostException Uhe;
|
|
|
|
internal QueryThread(Sem sem,
|
|
string host,
|
|
int type,
|
|
string scope,
|
|
IPAddress svr)
|
|
: base("JCIFS-QueryThread: " + host)
|
|
{
|
|
this.Sem = sem;
|
|
this.Host = host;
|
|
this.Type = type;
|
|
this.Scope = scope;
|
|
this.Svr = svr;
|
|
}
|
|
|
|
public override void Run()
|
|
{
|
|
try
|
|
{
|
|
//Ans = new [] { NbtAddress.GetByName(Host, Type, Scope, Svr) };
|
|
Ans = NbtAddress.GetAllByName(Host, Type, Scope, Svr);
|
|
}
|
|
catch (UnknownHostException uhe)
|
|
{
|
|
this.Uhe = uhe;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Uhe = new UnknownHostException(ex.Message);
|
|
}
|
|
finally
|
|
{
|
|
lock (Sem)
|
|
{
|
|
Sem.Count--;
|
|
Runtime.Notify(Sem);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <exception cref="UnknownHostException"></exception>
|
|
internal static NbtAddress[] LookupServerOrWorkgroup(string name, IPAddress svr)
|
|
{
|
|
Sem sem = new Sem(2);
|
|
int type = NbtAddress.IsWins(svr) ? unchecked(0x1b) : unchecked(0x1d);
|
|
QueryThread q1X = new QueryThread(sem, name, type, null, svr);
|
|
QueryThread q20 = new QueryThread(sem, name, unchecked(0x20), null, svr);
|
|
q1X.SetDaemon(true);
|
|
q20.SetDaemon(true);
|
|
try
|
|
{
|
|
lock (sem)
|
|
{
|
|
q1X.Start();
|
|
q20.Start();
|
|
|
|
while (sem.Count > 0 && q1X.Ans == null && q20.Ans == null)
|
|
{
|
|
Runtime.Wait(sem);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
throw new UnknownHostException(name);
|
|
}
|
|
if (q1X.Ans != null)
|
|
{
|
|
return q1X.Ans;
|
|
}
|
|
if (q20.Ans != null)
|
|
{
|
|
return q20.Ans;
|
|
}
|
|
throw q1X.Uhe;
|
|
}
|
|
|
|
/// <summary>Determines the address of a host given it's host name.</summary>
|
|
/// <remarks>
|
|
/// Determines the address of a host given it's host name. The name can be a
|
|
/// machine name like "jcifs.samba.org", or an IP address like "192.168.1.15".
|
|
/// </remarks>
|
|
/// <param name="hostname">NetBIOS or DNS hostname to resolve</param>
|
|
/// <exception cref="UnknownHostException">if there is an error resolving the name
|
|
/// </exception>
|
|
public static UniAddress GetByName(string hostname)
|
|
{
|
|
return GetByName(hostname, false);
|
|
}
|
|
|
|
internal static bool IsDotQuadIp(string hostname)
|
|
{
|
|
if (char.IsDigit(hostname[0]))
|
|
{
|
|
int i;
|
|
int len;
|
|
int dots;
|
|
char[] data;
|
|
i = dots = 0;
|
|
len = hostname.Length;
|
|
data = hostname.ToCharArray();
|
|
while (i < len && char.IsDigit(data[i++]))
|
|
{
|
|
if (i == len && dots == 3)
|
|
{
|
|
// probably an IP address
|
|
return true;
|
|
}
|
|
if (i < len && data[i] == '.')
|
|
{
|
|
dots++;
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
internal static bool IsAllDigits(string hostname)
|
|
{
|
|
for (int i = 0; i < hostname.Length; i++)
|
|
{
|
|
if (char.IsDigit(hostname[i]) == false)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// <summary>Lookup <tt>hostname</tt> and return it's <tt>UniAddress</tt>.</summary>
|
|
/// <remarks>
|
|
/// Lookup <tt>hostname</tt> and return it's <tt>UniAddress</tt>. If the
|
|
/// <tt>possibleNTDomainOrWorkgroup</tt> parameter is <tt>true</tt> an
|
|
/// addtional name query will be performed to locate a master browser.
|
|
/// </remarks>
|
|
/// <exception cref="UnknownHostException"></exception>
|
|
public static UniAddress GetByName(string hostname, bool possibleNtDomainOrWorkgroup)
|
|
{
|
|
UniAddress[] addrs = GetAllByName(hostname,
|
|
possibleNtDomainOrWorkgroup);
|
|
return addrs[0];
|
|
}
|
|
|
|
/// <exception cref="UnknownHostException"></exception>
|
|
public static UniAddress[] GetAllByName(string hostname, bool possibleNtDomainOrWorkgroup)
|
|
{
|
|
object addr;
|
|
int i;
|
|
if (string.IsNullOrEmpty(hostname))
|
|
{
|
|
throw new UnknownHostException();
|
|
}
|
|
if (IsDotQuadIp(hostname))
|
|
{
|
|
UniAddress[] addrs = new UniAddress[1];
|
|
addrs[0] = new UniAddress(NbtAddress.GetByName(hostname));
|
|
return addrs;
|
|
}
|
|
for (i = 0; i < _resolveOrder.Length; i++)
|
|
{
|
|
try
|
|
{
|
|
switch (_resolveOrder[i])
|
|
{
|
|
case ResolverLmhosts:
|
|
{
|
|
if ((addr = Lmhosts.GetByName(hostname)) == null)
|
|
{
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ResolverWins:
|
|
{
|
|
if (hostname == NbtAddress.MasterBrowserName
|
|
|| hostname.Length > 15)
|
|
{
|
|
// invalid netbios name
|
|
continue;
|
|
}
|
|
if (possibleNtDomainOrWorkgroup)
|
|
{
|
|
addr = LookupServerOrWorkgroup(hostname,
|
|
NbtAddress.GetWinsAddress());
|
|
}
|
|
else
|
|
{
|
|
addr = NbtAddress.GetByName(hostname,
|
|
unchecked(0x20),
|
|
null,
|
|
NbtAddress.GetWinsAddress());
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ResolverBcast:
|
|
{
|
|
if (hostname.Length > 15)
|
|
{
|
|
// invalid netbios name
|
|
continue;
|
|
}
|
|
|
|
try
|
|
{
|
|
if (possibleNtDomainOrWorkgroup)
|
|
{
|
|
NbtAddress[] iaddrs = LookupServerOrWorkgroup(hostname,
|
|
_baddr);
|
|
|
|
UniAddress[] addrs = new UniAddress[iaddrs.Length];
|
|
for (int ii = 0; ii < iaddrs.Length; ii++)
|
|
{
|
|
addrs[ii] = new UniAddress(iaddrs[ii]);
|
|
}
|
|
return addrs;
|
|
|
|
}
|
|
else
|
|
{
|
|
addr = NbtAddress.GetByName(hostname,
|
|
unchecked(0x20),
|
|
null,
|
|
_baddr);
|
|
}
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (i == _resolveOrder.Length - 1)
|
|
{
|
|
throw ex;
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ResolverDns:
|
|
{
|
|
if (IsAllDigits(hostname))
|
|
{
|
|
throw new UnknownHostException(hostname);
|
|
}
|
|
|
|
IPAddress[] iaddrs = Extensions.GetAddressesByName(hostname);
|
|
|
|
if (iaddrs == null || iaddrs.Length == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
return iaddrs.Select(iaddr => new UniAddress(iaddr)).ToArray();
|
|
}
|
|
|
|
default:
|
|
{
|
|
// Success
|
|
throw new UnknownHostException(hostname);
|
|
}
|
|
}
|
|
UniAddress[] addrs1 = new UniAddress[1];
|
|
addrs1[0] = new UniAddress(addr);
|
|
return addrs1;
|
|
}
|
|
catch (IOException)
|
|
{
|
|
}
|
|
}
|
|
// Success
|
|
// Failure
|
|
throw new UnknownHostException(hostname);
|
|
}
|
|
|
|
internal object Addr;
|
|
|
|
internal string CalledName;
|
|
|
|
/// <summary>
|
|
/// Create a <tt>UniAddress</tt> by wrapping an <tt>InetAddress</tt> or
|
|
/// <tt>NbtAddress</tt>.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Create a <tt>UniAddress</tt> by wrapping an <tt>InetAddress</tt> or
|
|
/// <tt>NbtAddress</tt>.
|
|
/// </remarks>
|
|
public UniAddress(object addr)
|
|
{
|
|
if (addr == null)
|
|
{
|
|
throw new ArgumentException();
|
|
}
|
|
this.Addr = addr;
|
|
}
|
|
|
|
/// <summary>Return the IP address of this address as a 32 bit integer.</summary>
|
|
/// <remarks>Return the IP address of this address as a 32 bit integer.</remarks>
|
|
public override int GetHashCode()
|
|
{
|
|
return Addr.GetHashCode();
|
|
}
|
|
|
|
/// <summary>Compare two addresses for equality.</summary>
|
|
/// <remarks>
|
|
/// Compare two addresses for equality. Two <tt>UniAddress</tt>s are equal
|
|
/// if they are both <tt>UniAddress</tt>' and refer to the same IP address.
|
|
/// </remarks>
|
|
public override bool Equals(object obj)
|
|
{
|
|
return obj is UniAddress && Addr.Equals(((UniAddress)obj).Addr);
|
|
}
|
|
|
|
/// <summary>Guess first called name to try for session establishment.</summary>
|
|
/// <remarks>
|
|
/// Guess first called name to try for session establishment. This
|
|
/// method is used exclusively by the <tt>jcifs.smb</tt> package.
|
|
/// </remarks>
|
|
public virtual string FirstCalledName()
|
|
{
|
|
if (Addr is NbtAddress)
|
|
{
|
|
return ((NbtAddress)Addr).FirstCalledName();
|
|
}
|
|
CalledName = ((IPAddress)Addr).GetHostAddress();
|
|
if (IsDotQuadIp(CalledName))
|
|
{
|
|
CalledName = NbtAddress.SmbserverName;
|
|
}
|
|
else
|
|
{
|
|
int i = CalledName.IndexOf('.');
|
|
if (i > 1 && i < 15)
|
|
{
|
|
CalledName = Runtime.Substring(CalledName, 0, i).ToUpper();
|
|
}
|
|
else
|
|
{
|
|
if (CalledName.Length > 15)
|
|
{
|
|
CalledName = NbtAddress.SmbserverName;
|
|
}
|
|
else
|
|
{
|
|
CalledName = CalledName.ToUpper();
|
|
}
|
|
}
|
|
}
|
|
return CalledName;
|
|
}
|
|
|
|
/// <summary>Guess next called name to try for session establishment.</summary>
|
|
/// <remarks>
|
|
/// Guess next called name to try for session establishment. This
|
|
/// method is used exclusively by the <tt>jcifs.smb</tt> package.
|
|
/// </remarks>
|
|
public virtual string NextCalledName()
|
|
{
|
|
if (Addr is NbtAddress)
|
|
{
|
|
return ((NbtAddress)Addr).NextCalledName();
|
|
}
|
|
if (CalledName != NbtAddress.SmbserverName)
|
|
{
|
|
CalledName = NbtAddress.SmbserverName;
|
|
return CalledName;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <summary>Return the underlying <tt>NbtAddress</tt> or <tt>InetAddress</tt>.</summary>
|
|
/// <remarks>Return the underlying <tt>NbtAddress</tt> or <tt>InetAddress</tt>.</remarks>
|
|
public virtual object GetAddress()
|
|
{
|
|
return Addr;
|
|
}
|
|
|
|
/// <summary>Return the hostname of this address such as "MYCOMPUTER".</summary>
|
|
/// <remarks>Return the hostname of this address such as "MYCOMPUTER".</remarks>
|
|
public virtual string GetHostName()
|
|
{
|
|
if (Addr is NbtAddress)
|
|
{
|
|
return ((NbtAddress)Addr).GetHostName();
|
|
}
|
|
return ((IPAddress)Addr).GetHostAddress();
|
|
}
|
|
|
|
/// <summary>Return the IP address as text such as "192.168.1.15".</summary>
|
|
/// <remarks>Return the IP address as text such as "192.168.1.15".</remarks>
|
|
public virtual string GetHostAddress()
|
|
{
|
|
if (Addr is NbtAddress)
|
|
{
|
|
return ((NbtAddress)Addr).GetHostAddress();
|
|
}
|
|
return ((IPAddress)Addr).GetHostAddress();
|
|
}
|
|
|
|
public virtual IPAddress GetHostIpAddress()
|
|
{
|
|
return (IPAddress)Addr;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return the a text representation of this address such as
|
|
/// <tt>MYCOMPUTER/192.168.1.15</tt>.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Return the a text representation of this address such as
|
|
/// <tt>MYCOMPUTER/192.168.1.15</tt>.
|
|
/// </remarks>
|
|
public override string ToString()
|
|
{
|
|
return Addr.ToString();
|
|
}
|
|
}
|
|
}
|