【问题标题】:Using C# to reference a port number to service name使用 C# 将端口号引用到服务名称
【发布时间】:2012-11-06 07:08:47
【问题描述】:

在 Linux 上使用 MONO。

如何使用 C# 将端口号与服务配对?

示例:

port 80 = http
port 443 = https

并将其输出到控制台,我需要这个用于我构建的简单端口扫描器。

我知道这个函数存在于 ruby​​ 中:

Socket.getservbyport(80, "tcp")

【问题讨论】:

  • 你可以尝试解析%WINDIR%\System32\drivers\etc\services,但我不确定是否有API函数(如果有,我怀疑它只是Win32 API,而不是.NET)
  • 好吧,如果我可以补充一下,我在单声道(C#)中使用 Linux,所以这对我没有帮助,但我确信他们有一个内置函数。
  • 原来 Win32 API 的名称与 ruby​​ 完全相同 - getservbyport

标签: c# ruby mono port


【解决方案1】:

单 Linux 版本

我有一点时间,所以在我的 Ubuntu Linux 机器上设置 Mono 进行测试。 getservbyport() 和 getservbyname() 的 Mono PInvoke 实现比在 Windows 上更简单(只需加载内置网络内容的 libc)。这是示例代码以供参考,以防万一有人想要它;)

namespace SocketUtil
{
    using System;
    using System.Net;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    using System.Runtime.Serialization;

    [Serializable]
    public class SocketUtilException : Exception
    {

        public SocketUtilException()
        {
        }

        public SocketUtilException(string message)
            : base(message)
        {
        }

        public SocketUtilException(string message, Exception inner)
            : base(message, inner)
        {
        }

        protected SocketUtilException(
            SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
        }
    }

    public static class SocketUtil
    {

        [StructLayoutAttribute(LayoutKind.Sequential)]
        public struct servent
        {
            public string s_name;
            public IntPtr s_aliases;
            public ushort s_port;
            public string s_proto;
        }


        [DllImport("libc", SetLastError = true, CharSet = CharSet.Ansi)]
        private static extern IntPtr getservbyname(string name, string proto);
        [DllImport("libc", SetLastError = true, CharSet = CharSet.Ansi)]
        private static extern IntPtr getservbyport(ushort port, string proto);

        public static string GetServiceByPort(ushort port, string protocol, out List<string> aliases)
        {
            var netport = unchecked((ushort)IPAddress.HostToNetworkOrder(unchecked((short)port)));
            var result = getservbyport(netport, protocol);
            if (IntPtr.Zero == result)
            {
                throw new SocketUtilException(
                    string.Format("Could not resolve service for port {0}", port));
            }
            var srvent = (servent)Marshal.PtrToStructure(result, typeof(servent));
            aliases = GetAliases(srvent);
            return srvent.s_name;

        }

        private static List<string> GetAliases(servent srvent)
        {
            var aliases = new List<string>();
            if (srvent.s_aliases != IntPtr.Zero)
            {
                IntPtr cb;

                for (var i = 0;
                    (cb = Marshal.ReadIntPtr(srvent.s_aliases, i)) != IntPtr.Zero;
                    i += Marshal.SizeOf(cb))
                {
                    aliases.Add(Marshal.PtrToStringAnsi(cb));
                }
            }
            return aliases;
        }

        public static ushort GetServiceByName(string service, string protocol, out List<string> aliases)
        {

            var result = getservbyname(service, protocol);
            if (IntPtr.Zero == result)
            {
                throw new SocketUtilException(
                    string.Format("Could not resolve port for service {0}", service));
            }

            var srvent = (servent)Marshal.PtrToStructure(result, typeof(servent));
            aliases = GetAliases(srvent);
            var hostport = IPAddress.NetworkToHostOrder(unchecked((short)srvent.s_port));
            return unchecked((ushort)hostport);

        }
    }
    class Program
    {

        static void Main(string[] args)
        {

            try
            {
                List<string> aliases;
                var port = SocketUtil.GetServiceByName("https", "tcp", out aliases);

                Console.WriteLine("https runs on port {0}", port);
                foreach (var alias in aliases)
                {
                    Console.WriteLine(alias);
                }

                Console.WriteLine("Reverse call:{0}", SocketUtil.GetServiceByPort(port, "tcp", out aliases));

            }
            catch (SocketUtilException exception)
            {
                Console.WriteLine(exception.Message);
                if (exception.InnerException != null)
                {
                    Console.WriteLine(exception.InnerException.Message);
                }
            }

        }
    }
}

【讨论】:

  • 因为这是在 MONO 中完全实现的,并且不需要从 Windows 复制文件,这对我来说就是答案。
【解决方案2】:

Windows 版本

更新:看到发布者在提出问题后添加了 Linux 和 Mono 标签为时已晚,因此编写了一个 Windows 实现。在 Linux 上使用第二篇文章中的 Mono 版本。

Mohammad 的解决方案在许多方面都比其他解决方案更便携。 getservbyname() 和 getservbyport() 依赖于平台,需要使用 P/Invoke 才能在 Windows 上与 c# 一起使用,而且很可能在 Mono 上也是如此。

要在 Mono 中实现以下代码,您需要使用特定于平台的 API 进行 PInvoke(标头为 netdb.h) - 请注意 WSAStartUp() 和 WSACleanUp() 是无关紧要的特定于 Windows 的套接字初始化函数在 Linux 系统上。目前没有单声道设置,因此无法提供特定于 linux 的解决方案,但如果您愿意跳过这些环节,这里有一个 windows(32 位)示例,可以让您的代码基于:

namespace SocketTest
{
    using System;
    using System.Net;
    using System.Net.Sockets;
    using System.Runtime.InteropServices;
    using System.Runtime.Serialization;



    [Serializable]
    public class SocketUtilException : Exception
    {

        public SocketUtilException()
        {
        }

        public SocketUtilException(string message)
            : base(message)
        {
        }

        public SocketUtilException(string message, Exception inner)
            : base(message, inner)
        {
        }

        protected SocketUtilException(
            SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
        }
    }
    public static class SocketUtil
    {
        private const int WSADESCRIPTION_LEN = 256;

        private const int WSASYSSTATUS_LEN = 128;

        [StructLayout(LayoutKind.Sequential)]
        public struct WSAData
        {
            public short wVersion;
            public short wHighVersion;

            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = WSADESCRIPTION_LEN+1)]
            public string szDescription;

            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = WSASYSSTATUS_LEN+1)]
            public string wSystemStatus;
            [Obsolete("Ignored when wVersionRequested >= 2.0")]
            public ushort wMaxSockets;
            [Obsolete("Ignored when wVersionRequested >= 2.0")]
            public ushort wMaxUdpDg;
            public IntPtr dwVendorInfo;
        }


        [StructLayoutAttribute(LayoutKind.Sequential)]
        public struct servent
        {
            public string s_name;
            public IntPtr s_aliases;
            public short s_port;
            public string s_proto;
        }

        private static ushort MakeWord ( byte low, byte high)
        {

            return  (ushort)((ushort)(high << 8) | low);
        }

        [DllImport("ws2_32.dll", CharSet = CharSet.Auto, ExactSpelling = true, SetLastError = true)]
        private static extern int WSAStartup(ushort wVersionRequested, ref WSAData wsaData);
        [DllImport("ws2_32.dll", CharSet = CharSet.Auto, ExactSpelling = true, SetLastError = true)]
        private static extern int WSACleanup();
        [DllImport("ws2_32.dll",  SetLastError = true, CharSet = CharSet.Ansi)]
        private static extern IntPtr getservbyname(string name, string proto);
        [DllImport("ws2_32.dll", SetLastError = true, CharSet = CharSet.Ansi)]
        private static extern IntPtr getservbyport(short port, string proto);

        public static string GetServiceByPort(short port, string protocol)
        {

            var wsaData = new WSAData();
            if (WSAStartup(MakeWord(2, 2), ref wsaData) != 0)
            {
                throw new SocketUtilException("WSAStartup",
                    new SocketException(Marshal.GetLastWin32Error()));

            }
            try
            {
                var netport = Convert.ToInt16(IPAddress.HostToNetworkOrder(port));
                var result = getservbyport(netport, protocol);
                if (IntPtr.Zero == result)
                {
                    throw new SocketUtilException(
                        string.Format("Could not resolve service for port {0}", port),
                        new SocketException(Marshal.GetLastWin32Error()));
                }
                var srvent = (servent)Marshal.PtrToStructure(result, typeof(servent));
                return srvent.s_name;;
            }
            finally
            {
                WSACleanup();
            }
        }


        public static short GetServiceByName(string service, string protocol)
        {

            var wsaData = new WSAData();
            if(WSAStartup(MakeWord(2,2), ref wsaData) != 0)
            {
                throw new SocketUtilException("WSAStartup", 
                    new SocketException(Marshal.GetLastWin32Error()));

            }
            try
            {
                var result = getservbyname(service, protocol);
                if (IntPtr.Zero == result)
                {
                    throw new SocketUtilException(
                        string.Format("Could not resolve port for service {0}", service), 
                        new SocketException(Marshal.GetLastWin32Error()));
                }
                var srvent = (servent)Marshal.PtrToStructure(result, typeof(servent));
                return Convert.ToInt16(IPAddress.NetworkToHostOrder(srvent.s_port));
            }
            finally
            {
                WSACleanup();
            }



        }
    }
    class Program
    {


        static void Main(string[] args)
        {

            try
            {


                var port = SocketUtil.GetServiceByName("http", "tcp");
                Console.WriteLine("http runs on port {0}", port);

                Console.WriteLine("Reverse call:{0}", SocketUtil.GetServiceByPort(port, "tcp"));



            }
            catch(SocketUtilException exception)
            {
                Console.WriteLine(exception.Message);
                if(exception.InnerException != null)
                {
                    Console.WriteLine(exception.InnerException.Message);
                }
            }


        }
    }

【讨论】:

  • 这看起来不错,但因为它在我的联盟中已经很远了,所以我会接受 Mohammad 的回答。
【解决方案3】:

在 Windows 上有一个“服务”文件,它位于 System32\Drivers\Etc\ 文件夹中。我编写了以下代码来解析它。您可以使用它来查找有关您想要的任何端口的信息:

class Program
{
    static void Main(string[] args)
    {
        var services = ReadServicesFile();

        // For example, I want to find information about port 443 of TCP service
        var port443Info = services.FirstOrDefault(s => s.Port == 443 && s.Type.Equals("tcp"));

        if (port443Info != null)
        {
            Console.WriteLine("TCP Port = {0}, Service name = {1}", port443Info.Port, port443Info.Name);
        }

    }

    static List<ServiceInfo> ReadServicesFile()
    {
        var sysFolder = Environment.GetFolderPath(Environment.SpecialFolder.System);
        if (!sysFolder.EndsWith("\\"))
            sysFolder += "\\";

        var svcFileName = sysFolder + "drivers\\etc\\services";

        var lines = File.ReadAllLines(svcFileName);

        var result = new List<ServiceInfo>();
        foreach (var line in lines)
        {
            if (string.IsNullOrEmpty(line) || line.StartsWith("#"))
                continue;

            var info = new ServiceInfo();

            var index = 0;

            // Name
            info.Name = line.Substring(index, 16).Trim();
            index += 16;

            // Port number and type
            var temp = line.Substring(index, 9).Trim();
            var tempSplitted = temp.Split('/');

            info.Port = ushort.Parse(tempSplitted[0]);
            info.Type = tempSplitted[1].ToLower();

            result.Add(info);
        }

        return result;
    }
}

您还需要以下类声明:

class ServiceInfo
{
    public ushort Port { get; set; }
    public string Name { get; set; }
    public string Type { get; set; }
}

【讨论】:

  • 我还意识到 Windows 7 附带的“服务”文件比 Windows XP 版本更完整。因此,您可能希望将该文件与端口扫描程序一起使用并解析该文件而不是 Windows 版本。
  • 这是一个很好的答案,我会尝试将此文件复制到我的 Linux 机器上并使用它,我会接受这个答案,但这更像是一个 HACK,而不是 Linux 的真正选择,我在我的问题中添加了更多信息,所以我不会让人们白白努力,对不起。
  • @Ba7a7chy 由于常用端口是由 IANA 注册的,因此在 Windows 和 Linux 上应该没有区别。因此,您可以在每个操作系统上使用相同的文件。为什么你认为这是一个 hack?
  • 因为描述的文件只能在windows中找到,我错了吗? “在 Windows 上,有一个位于 System32\Drivers\Etc\ 文件夹中的“服务”文件”
  • 我相信 /etc/services 会是你想要的(当然这取决于 linux 在主机上的设置方式)。
猜你喜欢
  • 1970-01-01
  • 2018-09-01
  • 2011-03-24
  • 2010-11-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-28
  • 2015-01-12
  • 1970-01-01
相关资源
最近更新 更多