Is there any way to query a list of valid domains from ASP.NET C#, similar to the list shown when logging into widows? I would like to provide this to the client users so they can select the appropriate domain with which to login to an intranet web application. I've tried using Forest.GetCurrentForest but I always seem to only get one domain back, when I definitely know there are others.
UPDATE: (CODE)
using System;
using System.Configuration;
using System.DirectoryServices.AccountManagement;
using System.DirectoryServices.ActiveDirectory;
namespace Domains {
class Program {
static void Main(string[] args) {
try {
using (Forest forest = Forest.GetCurrentForest()) {
Console.WriteLine("FOREST");
Console.WriteLine(" {0}", forest.Name);
Console.WriteLine("-------------------------------------------------------------------------------");
Console.WriteLine(" DOMAINS");
foreach (Domain domain in forest.Domains) {
Console.WriteLine(String.Format(" {0}", domain.Name));
Console.WriteLine(" TRUSTS");
TrustRelationshipInformationCollection domainTrusts = domain.GetAllTrustRelationships();
if (domainTrusts.Count == 0) {
Console.WriteLine(" N/A");
} else {
foreach (TrustRelationshipInformation trust in domainTrusts) {
DirectoryContext x = new DirectoryContext(DirectoryContextType.Domain, trust.TargetName);
Console.WriteLine(String.Format(" {0} -> {1}", trust.SourceName, trust.TargetName));
}
}
domain.Dispose();
}
Console.WriteLine("-------------------------------------------------------------------------------");
Console.WriteLine(" TRUSTS");
TrustRelationshipInformationCollection forestTrusts = forest.GetAllTrustRelationships();
if (forestTrusts.Count == 0) {
Console.WriteLine(" N/A");
} else {
foreach (TrustRelationshipInformation trust in forestTrusts) {
Console.WriteLine(String.Format(" {0} -> {1}", trust.SourceName, trust.TargetName));
}
}
}
}
catch (Exception ex) {
Console.WriteLine(ex.Message);
}
Console.WriteLine("\nPress ESC to exit...");
do {
while (!Console.KeyAvailable) {
// Do something
}
} while (Console.ReadKey(true).Key != ConsoleKey.Escape);
}
}
}
Now when I go to log into the machine directly (remote desktop, etc.) I get a list of 6 domains I can log into, but when the above code runs on the same machine (currently just a console app for testing, not ASP.NET enabled yet), I get one domain, the current domain I'm logged into on the machine.
EDIT:
I think maybe I'm getting confused, perhaps what I am really looking for is the NetBios domain names. Because I just realized the domain + all the trusts equal the count of domains I'm looking for, but these are the full names, not short names I expected.
UPDATE:
So I was able to acquire the netbiosname of the main domains using an LDAP query, but I'm not sure how to go about getting the netbiosname for the trusted domains...
If you are looking for all domains in teh forest use:
Forest.Domains
property. returns a DomainCollection.
GetCurrentForest returns only Forest for the current user context.
Forest.GetCurrentForest().Domains
Should return all domains.
Related
I need to copy and/or move files across servers to the other side of my
firewall. I was wondering if anyone can tell me what port(s) I will need to
open to run these methods in my C# program?
class MoveIt
{
public static void Main()
{
var localPath = #"c:\temp\";
var remotePath = #"\\MyRemoteServer\MyShare\MyPath\"
try
{
if (File.Exists(localPath + "MyTestFile.txt") &&
Directory.Exists(remotePath))
{
File.Move(localPath + "MyTestFile.txt", remotePath +
"MyTestFile.txt");
}
}
catch (Exception e)
{
Console.WriteLine("The process failed: {0}", e.ToString());
}
}
}
You need at least TCP 445, and to be sure you also want TCP 137-139, though this latter group is only if you're still stuck using NetBIOS for smb name resolution.
I'm trying to create a windows service in C# that will copy all the files from a network drive and paste it into a local drive (let's say in C drive). When I run the test case, the program runs successfully but when I install and run the windows service, the 'Access is denied' error comes in the log file.
I tried Map Network Drive (API) solution but that solution didn't work. either.
Here's the sample code that I've used to get all the files from a network drive and paste it into the local drive folder
Service1.cs
public partial class Service1 : ServiceBase
{
private Timer _timer;
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
try
{
DoWork();
}
catch (Exception e)
{
WriteErrorLog(e);
}
}
private void DoWork()
{
_timer = new Timer();
_timer.Interval = 5000;
_timer.Enabled = true;
_timer.Elapsed += _timer_Elapsed;
Update();
}
private void Update()
{
RevidAddinController.Update_AutodeskAddinFolder_With_ArchcorpUpdatedAddinFiles(Configuration.AutodeskVersion, Configuration.AutodeskRevitAddinFolderPath);
}
private void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
Update();
}
private void WriteErrorLog(Exception ex)
{
StreamWriter sw = null;
try
{
sw = new StreamWriter(AppDomain.CurrentDomain.BaseDirectory + "\\Logfile.txt", true);
sw.WriteLine(DateTime.Now.ToString() + " ; " + ex.Source.ToString().Trim() + "; " + ex.Message.ToString().Trim());
sw.Flush();
sw.Close();
}
catch
{
}
}
protected override void OnStop()
{
}
}
RevidAddinController.cs
public static class RevidAddinController
{
public static IEnumerable<AddinStatus> Update_AutodeskAddinFolder_With_ArchcorpUpdatedAddinFiles(List<string> autoDeskVersion, string addinInstallationPath)
{
var networkDrive = ActivateNetworkDrive();
var allAutodeskVersionPath = Util.GetAllAutodeskAddinLibraryFolderPaths(autoDeskVersion, addinInstallationPath);
List<FileData> latestArchcorpAddins = new List<FileData>();
foreach (var autodeskAddinFolder in allAutodeskVersionPath)
{
var archorpAddinFiles = Util.GetAllExternalRevitAddinFilesFromArchcorpAddinFolderPath(Configuration.ArchcorpAddinFolderPath);
var autodeskAddinFiles = Util.GetAllExternalRevitAddinFilesLocationFromAutodeskAddinFolderPath(autodeskAddinFolder);
var latestAddins = Util.GetUpdatedRevitAddinFromArchcorpFolderPath(autodeskAddinFolder, archorpAddinFiles, autodeskAddinFiles)
.Where(addin => !addin.FileName.Contains(Configuration.DeleteAddinNamePrefix));
latestArchcorpAddins.AddRange(latestAddins);
}
List<AddinStatus> addinCopyStatus = new List<AddinStatus>();
foreach (var autodeskAddinPath in allAutodeskVersionPath)
{
foreach (var newArchcorpAddin in latestArchcorpAddins)
{
addinCopyStatus.Add(Util.InstallNewAddinFile(newArchcorpAddin, autodeskAddinPath));
}
}
return addinCopyStatus;
}
/// <summary>
/// Map the network drive path
/// </summary>
/// <returns></returns>
public static NetworkDrive ActivateNetworkDrive()
{
NetworkDrive oNetDrive = new aejw.Network.NetworkDrive();
try
{
oNetDrive.LocalDrive = "O:";
oNetDrive.ShareName = #"\\acdxbfs1\Organisation";
oNetDrive.Force = true;
oNetDrive.Persistent = true;
oNetDrive.MapDrive();
}
catch (Exception err)
{
throw err;
}
return oNetDrive;
}
}
The complete code can be found on the gist here. Would really appreciate if someone reviews the code and provides any feedback/solution to this problem.
Running a service under the default Local System Account, will have no concept of the share. These are set up under user accounts.
Your 2 options
Run your service under a User Account which has those shares mapped
Access your share via and ip address instead of the drive letter. However, you will need to set the file/folder permissions accordingly.
The service does run as Local System (as previously named). If you have a mapped network drive to a local drive letter, the service cannot use it (because a mapped network drive is always only mapped for the user context, not the whole computer/system). However the service can access the share by UNC \\server\share. You can view the UNC path if you only have a mapped network drive by typing 'net use' inside a command prompt.
If you run your program as a user Windows does automatically authenticate you at the remote share (if not already done by adding a mapped network drive). Therefor Local System is the computer account you need to set the access permissions of the target share to the computername eg workstation1$ (only available inside a domain cause a workgroup does not know the other computers). This has to be done for the file permissions and the share permissions because both are independent and can restrict you from the access.
As an alternative you can authenticate at the remote network share with an user and password - there is an excellent thread in stackoverflow which you can find here which does show how you can achieve this.
Naturally you can also set the service to a user/password in the services manager (services.msc - double click your service and go to the logon tab) who has access to the share. By doing this, the user will be granted the 'login as service' permission which is necessary for this.
If the network file is shared with the local system account then you need to Log In as "Local System Account",
The advantage of running your services as the "Local System account" is that the service has complete unrestricted access to local resources.
But there are some disadvantages also, so be careful to not install unauthorized services as service gets full unrestricted access. Also if the service has some bugs it may lead to performance issues.
I need to learn how to use SMO within a C# program, so the first thing I did was start a new, console app and then began putting in the basics. I decided to make the app accept parameters so I can pass in things like usernames, logins, etc. As I work on it and build it I have a PowerShell window open where I can call the app, give it parameters or not, etc. But something weird is happening which I don't understand. Sometimes when I run the app in the PowerShell window, it's then deleted for some reason. Why is it doing that? I discovered it when it first gave me the following error message:
Program 'SmoListLogins.exe' failed to run: The system cannot find the file specifiedAt line:1 char:1 + .\SmoListLogins.exe "MYCOMPANY\Rod"
My SmoListLogins.exe program isn't there. Naturally I can easily re-create it, but I don't understand why its being deleted.
So you can see what I'm working with, here's the source code. I took it from a MSDN article and have added a little bit:
using System;
using System.Data;
using Microsoft.SqlServer.Management.Smo;
namespace SmoListLogins
{
class Program
{
static void Main(string[] args)
{
if (args.Length > 0)
{
var userName = args[0];
ListLogins(userName);
}
else
{
ListLogins();
}
}
static private void ListLogins(string userName = "")
{
var userNamePassed = (userName != "");
Server srv = new Server("YOURSQLINSTANCE");
//Iterate through each database and display.
foreach (Database db in srv.Databases)
{
Console.WriteLine("========");
Console.WriteLine("Login Mappings for the database: " + db.Name);
Console.WriteLine(" ");
//Run the EnumLoginMappings method and return details of database user-login mappings to a DataTable object variable.
DataTable d;
try
{
d = db.EnumLoginMappings();
//Display the mapping information.
foreach (DataRow r in d.Rows)
{
var userNameMatches = false;
var starting = true;
foreach (DataColumn c in r.Table.Columns)
{
if (!userNamePassed)
{
Console.WriteLine(c.ColumnName + " = " + r[c]);
}
else
{
if (starting)
{
starting = false;
if (userName == r[c].ToString())
{
userNameMatches = true;
}
}
if (userNameMatches)
{
Console.WriteLine(c.ColumnName + " = " + r[c]);
}
}
}
}
}
catch (Exception ex)
{
Console.WriteLine($"Error processing database: {db.Name}");
Console.WriteLine($"Error: {ex.Message}");
Console.WriteLine();
}
}
}
}
}
I believe I now know what was deleting my executable. It wasn't something I thought of, so I'm sharing with everyone the answer. Today I got an email from our chief security officer, informing me that the program I wrote was being blocked by Symantec Endpoint Protection. In my testing I'd run my app over and over again. After a few iterations of that it would disappear. It didn't occur to me that it was our corporate AV that might be doing it. Now it looks as though that is exactly what was going on.
Thank you everyone for your input in trying to help me resolve this. I hope that if anyone else encounters this problem they might consider the possibility of their AV as the reason why the app they wrote disappears.
How do I detect when the internet is idle (no download/upload) using C#, and initiate a download when it is?
If you are waiting for a moment where there are no connections... you have to know that there are a lot of connections even when you think you are not using Internet.
Try giving a look at WireShark and Prcomon (from sysitnerals) to have an idea.
Notwork traffic
Note: if you are using .NET on Mac or Linux via Mono, you should know that this APIs don't port well to other operating systems. So, what I describe here is only for Windows.
If what you want is to have an idea of the traffic, you can try using System.Net.NetworkInformation.IPGlobalStatistics.
You can do so, like this:
var properties = IPGlobalProperties.GetIPGlobalProperties();
// IPGlobalStatistics for IPv4
var ipv4stat = properties.GetIPv4GlobalStatistics();
// IPGlobalStatistics for IPv6
var ipv6stat = properties.GetIPv6GlobalStatistics();
From the example at MSDN:
Console.WriteLine(" Forwarding enabled ...................... : {0}",
ipstat.ForwardingEnabled);
Console.WriteLine(" Interfaces .............................. : {0}",
ipstat.NumberOfInterfaces);
Console.WriteLine(" IP addresses ............................ : {0}",
ipstat.NumberOfIPAddresses);
Console.WriteLine(" Routes .................................. : {0}",
ipstat.NumberOfRoutes);
Console.WriteLine(" Default TTL ............................. : {0}",
ipstat.DefaultTtl);
Console.WriteLine("");
Console.WriteLine(" Inbound Packet Data:");
Console.WriteLine(" Received ............................ : {0}",
ipstat.ReceivedPackets);
Console.WriteLine(" Forwarded ........................... : {0}",
ipstat.ReceivedPacketsForwarded);
Console.WriteLine(" Delivered ........................... : {0}",
ipstat.ReceivedPacketsDelivered);
Console.WriteLine(" Discarded ........................... : {0}",
ipstat.ReceivedPacketsDiscarded);
Console.WriteLine(" Header Errors ....................... : {0}",
ipstat.ReceivedPacketsWithHeadersErrors);
Console.WriteLine(" Address Errors ...................... : {0}",
ipstat.ReceivedPacketsWithAddressErrors);
Console.WriteLine(" Unknown Protocol Errors ............. : {0}",
ipstat.ReceivedPacketsWithUnknownProtocol);
Console.WriteLine("");
Console.WriteLine(" Outbound Packet Data:");
Console.WriteLine(" Requested ........................... : {0}",
ipstat.OutputPacketRequests);
Console.WriteLine(" Discarded ........................... : {0}",
ipstat.OutputPacketsDiscarded);
Console.WriteLine(" No Routing Discards ................. : {0}",
ipstat.OutputPacketsWithNoRoute);
Console.WriteLine(" Routing Entry Discards .............. : {0}",
ipstat.OutputPacketRoutingDiscards);
Console.WriteLine("");
Console.WriteLine(" Reassembly Data:");
Console.WriteLine(" Reassembly Timeout .................. : {0}",
ipstat.PacketReassemblyTimeout);
Console.WriteLine(" Reassemblies Required ............... : {0}",
ipstat.PacketReassembliesRequired);
Console.WriteLine(" Packets Reassembled ................. : {0}",
ipstat.PacketsReassembled);
Console.WriteLine(" Packets Fragmented .................. : {0}",
ipstat.PacketsFragmented);
Console.WriteLine(" Fragment Failures ................... : {0}",
ipstat.PacketFragmentFailures);
Console.WriteLine("");
Is Internet Available?
If you need to get a notification when the Internet is avaliable, you can subscribe to System.Net.NetworkInformation.NetworkChange.NetworkAvailabilityChanged. That way you will get a notification event if the Internet is connected or disconnected (Don't forget to unsubscribe).
Example:
var handler = new NetworkAddressChangedEventHandler
(
(sender, args) =>
{
//handle notification
}
);
System.Net.NetworkInformation.NetworkChange += handler;
// Unsubscribe:
System.Net.NetworkInformation.NetworkChange -= handler;
You may be interested in knowing what Network adapters are Up or Down and how much traffic each one has... for that see below.
Idle & Idle time
Let's define "Idle". I would say "Idle" means that the Internet is available (check above), and if it hasn't been used for a given ammount of time.
So, the other thing you got to do is is calling NetworkInterface.GetAllNetworkInterfaces that will give you an array of System.Net.NetworkInformation.NetworkInterface on which you can call the method GetIPStatistics to get an IPStatistics object for that particular netowork interface. You can also read the OperationalStatus property to know if the particular interface is Up or not
Example:
NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces();
foreach (var adapter in adapters)
{
// Is Up, Down, something else?
Console.WriteLine(" {0} is {1}", adapter.Name, dapter.OperationalStatus);
var stats = adapter.GetIPStatistics();
// Read some properties
Console.WriteLine(" Bytes Recieved: {0}", stats.BytesReceived);
Console.WriteLine(" Bytes Sent: {0}", stats.BytesSent);
}
What you will need to do is store this information in a way you can query it, to check if it has changed:
// Fields
Dictionary<string, Tuple<long, long>> data
= new Dictionary<string, Tuple<long, long>>();
bool IsInternetIdle()
{
bool idle = true;
NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces();
foreach (var adapter in adapters)
{
var stats = adapter.GetIPStatistics();
Tuple<long, long> info;
if (!data.TryGetValue(adapter.Name, out info))
{
//New Adapter
info = new Tuple<long, long>
(
stats .BytesReceived,
stats .BytesSent
);
data.Add(adapter.Name, info);
}
else
{
if
(
info.Item1 != stats .BytesReceived
|| info.Item2 != stats .BytesSent
)
{
idle = false;
break;
}
}
}
//Console.WriteLine("Is Idle: {0}", idle.ToString());
return idle;
}
And add some logic to handle idle time:
// Fields
Stopwatch watch = new Stopwatch();
static TimeSpan? GetInternetIdleTime()
{
if (IsInternetIdle())
{
if (!watch.IsRunning)
{
watch.Start();
}
//Console.WriteLine("Idle Time: {0}", XmlConvert.ToString(watch.Elapsed));
return watch.Elapsed;
}
else
{
watch.Stop();
watch.Reset();
return null;
}
}
Example usage:
GetInternetIdleTime(); //preemptive call
Thread.Sleep(1000);
var result = GetInternetIdleTime();
if (result.HasValue)
{
Console.WriteLine("Idle time: {0}", result.Value);
}
else
{
Console.WriteLine("Not Idle");
}
Console.ReadKey();
Words of caution
Remember to unsubscribe your event handler.
This is intended to work on Windows only.
Remember that the first run of IsInternetIdle (described above) all the network adapters are new. You may want to do a preemptive call to GetInternetIdleTime (described above) before stating using them.
The methods IsInternetIdle and GetInternetIdleTime are intended to be used only when Internet is available. You could add checks to see if the individual network adapters are Up.
The result of GetInternetIdleTime described above is not the total time that the connections has been inactive, but the time since it was discovered that the connections are inactive. You may want to call GetInternetIdleTime in a timer (of if your application has a main loop - say, it's a game - you can call it with a given frequency).
If a netwokr adaptes is Active, it doesn't mean that it is using Internet. Maybe it is connected to some Intranet. There is no way to tell if "Internet" is reachable. You should check for conectivity with individual servers yourself. Don't know what to check? It can be problematic because DNS can be overrrided locally... but you can try example.or, InterNIC.net or even ntp.ord.
Alternative
Since this is for Windows anyway, you can try using WMI to the network adapters information, for that I'll refer you to Find only physical network adapters with WMI Win32_NetworkAdapter class by Mladen Prajdic.
Digging deeper
You can emulate what WireShark does by using PCapDotNet, you will find examples at CodeProject.com. Let me DuckDuckGo that for you.
I'm working on a service that needs to detect user states for all user(s) logged on to a single machine. Specifically, I want to check to see whether or not the screen saver is active and whether or not their session is locked.
This code will run under a system-level service, and has no visible UI, so that may rule out several options (trapping WM messages, etc).
Aside from normal workstations, I'd like for this to work on terminal servers that have multiple users logged in to it. Due to these requirements I'm wondering if several Win32 APIs will need to be involved.
Any ideas on where to begin?
As a serivce you can use the event OnSessionChange to catch all your relevant moments.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceProcess;
using System.Diagnostics;
namespace MyCode
{
class MyService : ServiceBase
{
public MyService()
{
this.CanHandleSessionChangeEvent = true;
}
protected override void OnSessionChange(SessionChangeDescription changeDescription)
{
switch (changeDescription.Reason)
{
case SessionChangeReason.SessionLogon:
Debug.WriteLine(changeDescription.SessionId + " logon");
break;
case SessionChangeReason.SessionLogoff:
Debug.WriteLine(changeDescription.SessionId + " logoff");
break;
case SessionChangeReason.SessionLock:
Debug.WriteLine(changeDescription.SessionId + " lock");
break;
case SessionChangeReason.SessionUnlock:
Debug.WriteLine(changeDescription.SessionId + " unlock");
break;
}
base.OnSessionChange(changeDescription);
}
}
}
I'm sure it is possible to identify the user based on changeDescription.SessionId. But at the moment i don't know how...
EDIT: This should be a possibilty
public static WindowsIdentity GetUserName(int sessionId)
{
foreach (Process p in Process.GetProcesses())
{
if(p.SessionId == sessionId) {
return new WindowsIdentity(p.Handle);
}
}
return null;
}
MSDN Links
system.serviceprocess.servicebase.onsessionchange
system.serviceprocess.sessionchangedescription
system.serviceprocess.sessionchangereason
The most straightforward way would be to have a small app running in each user's session. Each instance of this app could communicate with the main instance of the service.
Windows tries pretty hard to keep logon sessions separate -- both between services and the interactive desktop, and between individual Terminal Services sessions -- so it gets very tricky to access this sort of information about a user's session unless your app is running in that session to begin with.
A simpler solution would be to use Cassia, which wraps the various TS APIs, to check how long users have been idle for:
using Cassia;
foreach (ITerminalServicesSession session in new TerminalServicesManager().GetSessions())
{
if ((session.CurrentTime - session.LastInputTime > TimeSpan.FromMinutes(10)) &&
(!string.IsNullOrEmpty(session.UserName)))
{
Console.WriteLine("Session {0} (User {1}) is idle", session.SessionId, session.UserName);
}
}