Add project files.

This commit is contained in:
Tommy Parnell
2016-05-20 23:44:28 -04:00
parent 096a4113a8
commit 33017ca9a3
32 changed files with 1568 additions and 0 deletions

39
Shodan.Net.sln Normal file
View File

@@ -0,0 +1,39 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25123.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{060AE71C-AE05-4F14-8970-84A1AE49A562}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{18A95621-2E1B-48F0-9D38-D7B3513F31D3}"
ProjectSection(SolutionItems) = preProject
global.json = global.json
EndProjectSection
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Shodan.Net", "src\Shodan.Net\Shodan.Net.xproj", "{2AC1566F-7C77-499E-B9AE-1F029BD7F7E8}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Shodan.Net.UnitTests", "src\Shodan.Net.UnitTests\Shodan.Net.UnitTests.xproj", "{F83A8130-97B5-4AA2-810C-1D16A88799BD}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2AC1566F-7C77-499E-B9AE-1F029BD7F7E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2AC1566F-7C77-499E-B9AE-1F029BD7F7E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2AC1566F-7C77-499E-B9AE-1F029BD7F7E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2AC1566F-7C77-499E-B9AE-1F029BD7F7E8}.Release|Any CPU.Build.0 = Release|Any CPU
{F83A8130-97B5-4AA2-810C-1D16A88799BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F83A8130-97B5-4AA2-810C-1D16A88799BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F83A8130-97B5-4AA2-810C-1D16A88799BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F83A8130-97B5-4AA2-810C-1D16A88799BD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{2AC1566F-7C77-499E-B9AE-1F029BD7F7E8} = {060AE71C-AE05-4F14-8970-84A1AE49A562}
{F83A8130-97B5-4AA2-810C-1D16A88799BD} = {060AE71C-AE05-4F14-8970-84A1AE49A562}
EndGlobalSection
EndGlobal

6
global.json Normal file
View File

@@ -0,0 +1,6 @@
{
"projects": [ "src", "test" ],
"sdk": {
"version": "1.0.0-preview1-002702"
}
}

View File

@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
namespace Shodan.Net.UnitTests
{
// This project can output the Class library as a NuGet Package.
// To enable this option, right-click on the project and select the Properties menu item. In the Build tab select "Produce outputs on build".
public class Class1
{
[Fact]
public async Task privateGetsPorts()
{
var client = new ShodanClient("9F0mxmNSaHbe0mYmefwoCZrChT2h0KzC");
var ports = await client.GetPortsAsync();
}
}
}

View File

@@ -0,0 +1,19 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Shodan.Net.UnitTests")]
[assembly: AssemblyTrademark("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("f83a8130-97b5-4aa2-810c-1d16a88799bd")]

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>f83a8130-97b5-4aa2-810c-1d16a88799bd</ProjectGuid>
<RootNamespace>Shodan.Net.UnitTests</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@@ -0,0 +1,25 @@
{
"version": "1.0.0-*",
"testRunner": "xunit",
"dependencies": {
"NETStandard.Library": "1.5.0-rc2-24027",
"xunit": "2.1.0",
"dotnet-test-xunit": "1.0.0-rc2-build10015",
"Shodan.Net": "1.0.0-*"
},
"frameworks": {
"netcoreapp1.0": {
"dependencies": {
"Microsoft.NETCore.App": {
"type": "platform",
"version": "1.0.0-rc2-3002702"
}
},
"imports": [
"dnxcore50",
"portable-net45+win8"
]
}
}
}

16
src/Shodan.Net/Class1.cs Normal file
View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Shodan.Net
{
// This project can output the Class library as a NuGet Package.
// To enable this option, right-click on the project and select the Properties menu item. In the Build tab select "Produce outputs on build".
public class Class1
{
public Class1()
{
}
}
}

View File

@@ -0,0 +1,124 @@
using Shodan.Net.Models;
using Shodan.Net.Models.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Shodan.Net
{
internal interface IShodanAsyncClient
{
/// <summary>
/// Returns all services that have been found on the given host IP.
/// </summary>
/// <param name="Ip">Host IP address</param>
/// <param name="history">True if all historical banners should be returned (default: False) </param>
/// <param name="minify">True to only return the list of ports and the general host information, no banners. (default: False) </param>
/// <returns></returns>
Task<Host> GetHostAsync(string Ip, bool history = false, bool minify = false);
/// <summary>
/// This method returns a list of port numbers that the crawlers are looking for.
/// </summary>
/// <returns></returns>
Task<List<int>> GetPortsAsync();
/// <summary>
/// This method returns an object containing all the protocols that can be used when launching an Internet scan.
/// </summary>
/// <returns></returns>
Task<Dictionary<string, string>> GetProtocolsAsync();
/// <summary>
/// Use this method to request Shodan to crawl a network
/// <strong>Requirements:</strong> This method uses API scan credits: 1 IP consumes 1 scan credit. You must have a paid API plan (either one-time payment or subscription) in order to use this method
/// </summary>
/// <returns></returns>
Task<ScanResult> RequstScanAsync(string ips);
/// <summary>
///
/// </summary>
/// <param name="port"> The port that Shodan should crawl the Internet for. </param>
/// <param name="protocol">The name of the protocol that should be used to interrogate the port. See /shodan/protocols for a list of supported protocols. </param>
/// <returns></returns>
Task<ScanPortResult> RequestInternetPortScanAsync(int port, string protocol);
/// <summary>
/// Check the progress of a previously submitted scan request
/// </summary>
/// <param name="id">the unique scan ID that was returned by /shodan/scan</param>
/// <returns></returns>
Task<ScanStatus> GetScanStatusAsync(string id);
/// <summary>
/// This method returns an object containing all the services that the Shodan crawlers look at. It can also be used as a quick and practical way to resolve a port number to the name of a service
/// </summary>
/// <returns></returns>
Task<Dictionary<string, string>> GetServicesAsync();
/// <summary>
/// Use this method to obtain a list of search queries that users have saved in Shodan.
/// </summary>
/// <param name="page"> Page number to iterate over results; each page contains 10 items </param>
/// <param name="options"> Sort the list based on a property. Possible values are: votes, timestamp </param>
/// <param name="order">Whether to sort the list in ascending or descending order. Possible values are: asc, desc </param>
/// <returns></returns>
Task<SearchQueries> GetQueriesAsync(int? page = null, SortOptions? options = null, OrderOption? order = null);
/// <summary>
/// Use this method to search the directory of search queries that users have saved in Shodan.
/// </summary>
/// <param name="query"> What to search for in the directory of saved search queries. </param>
/// <param name="page">Page number to iterate over results; each page contains 10 items </param>
/// <returns></returns>
Task<SearchQueries> SearchQueriesAsync(string query, int? page = null);
/// <summary>
/// Use this method to obtain a list of popular tags for the saved search queries in Shodan.
/// </summary>
/// <param name="size">The number of tags to return </param>
/// <returns></returns>
Task<TagResult> GetTagsAsync(int size = 10);
/// <summary>
/// Returns information about the Shodan account linked to this API key.
/// </summary>
/// <returns></returns>
Task<Profile> GetProfileAsync();
/// <summary>
/// Look up the IP address for the provided list of hostnames.
/// </summary>
/// <param name="hostnames">Comma-separated list of hostnames; example "google.com,bing.com" </param>
/// <returns></returns>
Task<Dictionary<string, string>> DnsLookupAsync(string hostnames);
/// <summary>
/// Look up the hostnames that have been defined for the given list of IP addresses
/// </summary>
/// <param name="ips">Comma-separated list of IP addresses; example "74.125.227.230,204.79.197.200"</param>
/// <returns></returns>
Task<Dictionary<string, List<string>>> ReverseLookupAsync(string ips);
/// <summary>
/// Get your current IP address as seen from the Internet.
/// </summary>
/// <returns></returns>
Task<string> GetMyIpAsync();
/// <summary>
/// Returns information about the API plan belonging to the given API key.
/// </summary>
/// <returns></returns>
Task<ApiStatus> GetApiStatusAsync();
/// <summary>
/// Calculates a honeypot probability score rangin from 0 (not a honeypot) to 1.0 (is a honeypot).
/// </summary>
/// <param name="ip"></param>
/// <returns></returns>
Task<double> Experimental_GetHoneyPotScoreAsync(string ip);
}
}

View File

@@ -0,0 +1,124 @@
using Shodan.Net.Models;
using Shodan.Net.Models.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Shodan.Net
{
public interface IShodanClient
{
/// <summary>
/// Returns all services that have been found on the given host IP.
/// </summary>
/// <param name="Ip">Host IP address</param>
/// <param name="history">True if all historical banners should be returned (default: False) </param>
/// <param name="minify">True to only return the list of ports and the general host information, no banners. (default: False) </param>
/// <returns></returns>
Host GetHost(string Ip, bool history = false, bool minify = false);
/// <summary>
/// This method returns a list of port numbers that the crawlers are looking for.
/// </summary>
/// <returns></returns>
List<int> GetPorts();
/// <summary>
/// This method returns an object containing all the protocols that can be used when launching an Internet scan.
/// </summary>
/// <returns></returns>
Dictionary<string, string> GetProtocols();
/// <summary>
/// Use this method to request Shodan to crawl a network
/// <strong>Requirements:</strong> This method uses API scan credits: 1 IP consumes 1 scan credit. You must have a paid API plan (either one-time payment or subscription) in order to use this method
/// </summary>
/// <returns></returns>
ScanResult RequstScan(string ips);
/// <summary>
///
/// </summary>
/// <param name="port"> The port that Shodan should crawl the Internet for. </param>
/// <param name="protocol">The name of the protocol that should be used to interrogate the port. See /shodan/protocols for a list of supported protocols. </param>
/// <returns></returns>
ScanPortResult RequestPortScan(int port, string protocol);
/// <summary>
/// Check the progress of a previously submitted scan request
/// </summary>
/// <param name="id">the unique scan ID that was returned by /shodan/scan</param>
/// <returns></returns>
ScanStatus GetScanStatus(string id);
/// <summary>
/// This method returns an object containing all the services that the Shodan crawlers look at. It can also be used as a quick and practical way to resolve a port number to the name of a service
/// </summary>
/// <returns></returns>
Dictionary<string, string> GetServices();
/// <summary>
/// Use this method to obtain a list of search queries that users have saved in Shodan.
/// </summary>
/// <param name="page"> Page number to iterate over results; each page contains 10 items </param>
/// <param name="options"> Sort the list based on a property. Possible values are: votes, timestamp </param>
/// <param name="order">Whether to sort the list in ascending or descending order. Possible values are: asc, desc </param>
/// <returns></returns>
SearchQueries GetQueries(int? page = null, SortOptions? options = null, OrderOption? order = null);
/// <summary>
/// Use this method to search the directory of search queries that users have saved in Shodan.
/// </summary>
/// <param name="query"> What to search for in the directory of saved search queries. </param>
/// <param name="page">Page number to iterate over results; each page contains 10 items </param>
/// <returns></returns>
SearchQueries GetQuerySearches(string query, int? page = null);
/// <summary>
/// Use this method to obtain a list of popular tags for the saved search queries in Shodan.
/// </summary>
/// <param name="size">The number of tags to return </param>
/// <returns></returns>
TagResult GetTags(int size = 10);
/// <summary>
/// Returns information about the Shodan account linked to this API key.
/// </summary>
/// <returns></returns>
Profile GetProfile();
/// <summary>
/// Look up the IP address for the provided list of hostnames.
/// </summary>
/// <param name="hostnames">Comma-separated list of hostnames; example "google.com,bing.com" </param>
/// <returns></returns>
Dictionary<string, string> DnsLookup(string hostnames);
/// <summary>
/// Look up the hostnames that have been defined for the given list of IP addresses
/// </summary>
/// <param name="ips">Comma-separated list of IP addresses; example "74.125.227.230,204.79.197.200"</param>
/// <returns></returns>
Dictionary<string, List<string>> ReverseLookup(string ips);
/// <summary>
/// Get your current IP address as seen from the Internet.
/// </summary>
/// <returns></returns>
string GetMyIp();
/// <summary>
/// Returns information about the API plan belonging to the given API key.
/// </summary>
/// <returns></returns>
ApiStatus GetApiStatus();
/// <summary>
/// Calculates a honeypot probability score rangin from 0 (not a honeypot) to 1.0 (is a honeypot).
/// </summary>
/// <param name="ip"></param>
/// <returns></returns>
double Experimental_GetHoneyPotScore(string ip);
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Shodan.Net.Models
{
[DataContract]
public class ApiStatus
{
[DataMember(Name = "query_credits")]
public int QueryCredits { get; set; }
[DataMember(Name = "scan_credits")]
public int ScanCredits { get; set; }
[DataMember(Name = "telnet")]
public bool Telnet { get; set; }
[DataMember(Name = "plan")]
public string Plan { get; set; }
[DataMember(Name = "https")]
public bool Https { get; set; }
[DataMember(Name = "unlocked")]
public bool Unlocked { get; set; }
}
}

View File

@@ -0,0 +1,139 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Shodan.Net.Models
{
[DataContract]
public class Banner
{
/// <summary>
/// The timestamp for when the banner was fetched from the device in the UTC timezone.
/// </summary>
[DataMember(Name = "timestamp")]
public DateTime Timestamp { get; set; }
/// <summary>
/// Either "udp" or "tcp" to indicate which IP transport protocol was used to fetch the information
/// </summary>
[DataMember(Name = "transport")]
public string Transport { get; set; }
/// <summary>
/// An array of strings containing all of the hostnames that have been assigned to the IP address for this device.
/// </summary>
[DataMember(Name = "hostnames")]
public IList<string> Hostnames { get; set; }
/// <summary>
/// The name of the organization that is assigned the IP space for this device.
/// </summary>
[DataMember(Name = "org")]
public string Org { get; set; }
[DataMember(Name = "guid")]
public string Guid { get; set; }
/// <summary>
/// Contains the banner information for the service.
/// </summary>
[DataMember(Name = "data")]
public string Data { get; set; }
/// <summary>
/// The port number that the service is operating on
/// </summary>
[DataMember(Name = "port")]
public int Port { get; set; }
/// <summary>
/// The ISP that is providing the organization with the IP space for this device. Consider this the "parent" of the organization in terms of IP ownership.
/// </summary>
[DataMember(Name = "isp")]
public string Isp { get; set; }
/// <summary>
/// The autonomous system number (ex. "AS4837"
/// </summary>
[DataMember(Name = "asn")]
public string Asn { get; set; }
[DataMember(Name = "location")]
public Location Location { get; set; }
/// <summary>
/// The IP address of the host as an integer
/// </summary>
[DataMember(Name = "ip")]
public int? Ip { get; set; }
/// <summary>
/// The IPv6 address of the host as a string. If this is present then the "ip" and "ip_str" fields wont be.
/// </summary>
[DataMember(Name = "ip")]
public string Ipv6 { get; set; }
/// <summary>
/// ] An array of strings containing the top-level domains for the hostnames of the device. This is a utility property in case you want to filter by TLD instead of subdomain. It is smart enough to handle global TLDs with several dots in the domain (ex. "co.uk")
/// </summary>
[DataMember(Name = "domains")]
public IList<string> Domains { get; set; }
/// <summary>
/// The IP address of the host as a string
/// </summary>
[DataMember(Name = "ip_str")]
public string IpStr { get; set; }
/// <summary>
/// The operating system that powers the device.
/// </summary>
[DataMember(Name = "os")]
public object Os { get; set; }
/// <summary>
/// Contains experimental and supplemental data for the service. This can include the SSL certificate, robots.txt and other raw information that hasn't yet been formalized into the Banner Specification.
/// </summary>
[DataMember(Name = "opts", IsRequired = false)]
public dynamic Opts { get; set; }
#region Optional Properties
/// <summary>
/// The number of minutes that the device has been online.
/// </summary>
[DataMember(Name = "uptime", IsRequired = false)]
public int? Uptime { get; set; }
[DataMember(Name = "link", IsRequired = false)]
public string Link { get; set; }
[DataMember(Name = "title", IsRequired = false)]
public string Title { get; set; }
[DataMember(Name = "html", IsRequired = false)]
public string Html { get; set; }
[DataMember(Name = "product", IsRequired = false)]
public string Product { get; set; }
[DataMember(Name = "version", IsRequired = false)]
public string Version { get; set; }
[DataMember(Name = "devicetype", IsRequired = false)]
public string DeviceType { get; set; }
[DataMember(Name = "info", IsRequired = false)]
public string Info { get; set; }
[DataMember(Name = "cpe", IsRequired = false)]
public string Cpe { get; set; }
[DataMember(Name = "ssl", IsRequired = false)]
public SslProperties Ssl { get; set; }
#endregion Optional Properties
}
}

View File

@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Shodan.Net.Models
{
[DataContract]
public class Host
{
[DataMember(Name = "region_code")]
public string RegionCode { get; set; }
[DataMember(Name = "ip")]
public string Ip { get; set; }
[DataMember(Name = "area_code")]
public string AreaCode { get; set; }
[DataMember(Name = "country_names")]
public string CountryName { get; set; }
[DataMember(Name = "hostnames")]
public List<string> Hostnames { get; set; }
[DataMember(Name = "postal_code")]
public string PostalCode { get; set; }
[DataMember(Name = "dma_code")]
public string DmaCode { get; set; }
[DataMember(Name = "country_code")]
public string CountryCode { get; set; }
[DataMember(Name = "data")]
public List<Banner> Data { get; set; }
}
}

View File

@@ -0,0 +1,78 @@
using System.Runtime.Serialization;
namespace Shodan.Net.Models
{
[DataContract]
public class Location
{
/// <summary>
/// An object containing all of the location information for the device.
/// </summary>
public Location()
{
}
/// <summary>
/// The 3-letter country code for the device location.
/// </summary>
[DataMember(Name = "country_code3")]
public string CountryCode3 { get; set; }
/// <summary>
/// The name of the city where the device is located.
/// </summary>
[DataMember(Name = "city")]
public string City { get; set; }
/// <summary>
/// The postal code for the device's location.
/// </summary>
[DataMember(Name = "postal_code")]
public string PostalCode { get; set; }
/// <summary>
/// The longitude for the geolocation of the device.
/// </summary>
[DataMember(Name = "longitude")]
public double Longitude { get; set; }
/// <summary>
/// The 2-letter country code for the device location.
/// </summary>
[DataMember(Name = "country_code")]
public string CountryCode { get; set; }
/// <summary>
/// The latitude for the geolocation of the device
/// </summary>
[DataMember(Name = "latitude")]
public double Latitude { get; set; }
/// <summary>
/// The name of the country where the device is located.
/// </summary>
[DataMember(Name = "country_name")]
public string CountryName { get; set; }
/// <summary>
/// The area code for the device's location. Only available for the US.
/// </summary>
[DataMember(Name = "area_code")]
public int AreaCode { get; set; }
/// <summary>
/// The designated market area code for the area where the device is located. Only available for the US.
/// </summary>
[DataMember(Name = "dma_code")]
public int? DmaCode { get; set; }
/// <summary>
/// The name of the region where the device is located.
/// </summary>
[DataMember(Name = "region_code")]
public string RegionCode { get; set; }
}
}

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Shodan.Net.Models.Options
{
public enum OrderOption
{
asc,
desc
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Shodan.Net.Models.Options
{
public enum RequestType
{
GET = 0,
PUT = 1,
POST = 2,
DELETE = 4
}
}

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Shodan.Net.Models.Options
{
public enum SortOptions
{
votes,
timestamp
}
}

View File

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Shodan.Net.Models
{
[DataContract]
public class Profile
{
[DataMember(Name = "member")]
public bool Member { get; set; }
[DataMember(Name = "credits")]
public int Credits { get; set; }
[DataMember(Name = "display_name")]
public string DisplayName { get; set; }
[DataMember(Name = "created")]
public DateTime Created { get; set; }
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Shodan.Net.Models
{
[DataContract]
public class ScanPortResult
{
[DataMember(Name = "id")]
public string Id { get; set; }
}
}

View File

@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Shodan.Net.Models
{
[DataContract]
public class ScanResult
{
[DataMember(Name = "id")]
public string Id { get; set; }
[DataMember(Name = "count")]
public int Count { get; set; }
[DataMember(Name = "credits_left")]
public int CreditsLeft { get; set; }
}
}

View File

@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Shodan.Net.Models
{
[DataContract]
public class ScanStatus
{
[DataMember(Name = "id")]
public string Id { get; set; }
[DataMember(Name = "count")]
public int Count { get; set; }
[DataMember(Name = "status")]
public StatusEnum? Status { get; set; }
}
}

View File

@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Shodan.Net.Models
{
[DataContract]
public class Match
{
[DataMember(Name = "votes")]
public int Votes { get; set; }
[DataMember(Name = "description")]
public string Description { get; set; }
[DataMember(Name = "title")]
public string Title { get; set; }
[DataMember(Name = "timestamp")]
public DateTime Timestamp { get; set; }
[DataMember(Name = "tags")]
public IList<string> Tags { get; set; }
[DataMember(Name = "query")]
public string Query { get; set; }
}
[DataContract]
public class SearchQueries
{
[DataMember(Name = "total")]
public int Total { get; set; }
[DataMember(Name = "matches")]
public IList<Match> Matches { get; set; }
}
}

View File

@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Shodan.Net.Models
{
[DataContract]
public class SslProperties
{
/// <summary>
/// The parsed certificate properties that includes information such as when it was issued, the SSL extensions, the issuer, subject etc.
/// </summary>
[DataMember(Name = "cert")]
public dynamic Cert { get; set; }
/// <summary>
/// Preferred cipher for the SSL connection
/// </summary>
[DataMember(Name = "ciper")]
public dynamic Ciper { get; set; }
/// <summary>
/// An array of certificates, where each string is a PEM-encoded SSL certificate. This includes the user SSL certificate up to its root certificate.
/// </summary>
[DataMember(Name = "chain")]
public IList<dynamic> Chain { get; set; }
/// <summary>
/// The Diffie-Hellman parameters if available: "prime", "public_key", "bits", "generator" and an optional "fingerprint" if we know which program generated these parameters.
/// </summary>
[DataMember(Name = "dhparams")]
public dynamic DhParams { get; set; }
/// <summary>
/// A list of SSL versions that are supported by the server. If a version isnt supported the value is prefixed with a "-". Example: ["TLSv1", "-SSLv2"] means that the server supports TLSv1 but doesnt support SSLv2.
/// </summary>
[DataMember(Name = "versions")]
public IList<string> Versions { get; set; }
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Shodan.Net.Models
{
public enum StatusEnum
{
SUBMITTING,
QUEUE,
PROCESSING,
DONE
}
}

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Shodan.Net.Models
{
[DataContract]
public class TagResult
{
[DataMember(Name = "total")]
public int Total { get; set; }
[DataMember(Name = "matches")]
public IList<Match> Matches { get; set; }
[DataContract]
public class Match
{
[DataMember(Name = "value")]
public string Value { get; set; }
[DataMember(Name = "count")]
public int Count { get; set; }
}
}
}

View File

@@ -0,0 +1,19 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Shodan.Net")]
[assembly: AssemblyTrademark("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("2ac1566f-7c77-499e-b9ae-1f029bd7f7e8")]

View File

@@ -0,0 +1,205 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Shodan.Net
{
public class QueryGenerator
{
internal Dictionary<string, string> queryData { get; set; }
private HashSet<string> CalledMethods = new HashSet<string>();
public QueryGenerator Before(DateTime time)
{
queryData.Add("before", time.ToString("dd/MM/yyyy"));
return this;
}
/// <summary>
/// Only show results that were collected after the given date
/// </summary>
/// <param name="time"></param>
/// <returns></returns>
public QueryGenerator After(DateTime time)
{
queryData.Add("after", time.ToString("dd/MM/yyyy"));
return this;
}
/// <summary>
/// The Autonomous System Number that identifies the network the device is on.
/// </summary>
/// <param name="asn"></param>
/// <returns></returns>
public QueryGenerator WithAsn(string asn)
{
queryData.Add("asn", asn);
return this;
}
public QueryGenerator InCity(string city)
{
queryData.Add("city", city);
return this;
}
public QueryGenerator InCountry(string country)
{
queryData.Add("country", country);
return this;
}
/// <summary>
/// If "true" only show results that have a screenshot available.
/// </summary>
/// <param name="hasScreenshot"></param>
/// <returns></returns>
public QueryGenerator HasScreenshot(bool hasScreenshot = true)
{
queryData.Add("has_screenshot", hasScreenshot.ToString());
return this;
}
public QueryGenerator WithHostname(string hostname)
{
queryData.Add("hostname", hostname);
return this;
}
public QueryGenerator WithHtml(string html)
{
queryData.Add("html", html);
return this;
}
public QueryGenerator WithIsp(string isp)
{
queryData.Add("isp", isp);
return this;
}
public QueryGenerator WithNet(string net)
{
queryData.Add("net", net);
return this;
}
public QueryGenerator WithOrg(string org)
{
queryData.Add("org", org);
return this;
}
public QueryGenerator WithOs(string os)
{
queryData.Add("os", os);
return this;
}
public QueryGenerator Withport(string port)
{
queryData.Add("port", port);
return this;
}
public QueryGenerator With_postal(string postal)
{
queryData.Add("postal", postal);
return this;
}
public QueryGenerator With_product(string product)
{
queryData.Add("product", product);
return this;
}
public QueryGenerator With_state(string state)
{
queryData.Add("state", state);
return this;
}
public QueryGenerator With_title(string title)
{
queryData.Add("title", title);
return this;
}
public QueryGenerator With_version(string version)
{
queryData.Add("version", version);
return this;
}
public QueryGenerator With_Bitcoinip(string bitcoinip)
{
queryData.Add("bitcoin.ip", bitcoinip);
return this;
}
public QueryGenerator With_Bitcoinip_count(string bitcoinip_count)
{
queryData.Add("bitcoin.ip_count", bitcoinip_count);
return this;
}
public QueryGenerator WithBitcoinport(string bitcoinport)
{
queryData.Add("bitcoin.port", bitcoinport);
return this;
}
public QueryGenerator WithBitcoinversion(string bitcoinversion)
{
queryData.Add("bitcoin.version", bitcoinversion);
return this;
}
public QueryGenerator WithNtpip(string ntpip)
{
queryData.Add("ntp.ip", ntpip);
return this;
}
public QueryGenerator WithNtpip_count(string ntpip_count)
{
queryData.Add("ntp.ip_count", ntpip_count);
return this;
}
public QueryGenerator WithNtpmore(string ntpmore)
{
queryData.Add("ntp.more", ntpmore);
return this;
}
public QueryGenerator WithNtpport(string ntpport)
{
queryData.Add("ntp.port", ntpport);
return this;
}
public SearchQuery Generate(string searchText)
{
var sb = new StringBuilder(searchText);
sb.Append(" ");
foreach(var item in queryData)
{
sb.Append($"{item.Key}:{item.Value}");
}
return new SearchQuery(sb.ToString());
}
private void EnsureMethodNotCalled(string methodName)
{
if(CalledMethods.Contains(methodName))
{
throw new ShodanException($"{methodName} cannot be called twice");
}
CalledMethods.Add(methodName);
}
}
}

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Shodan.Net
{
public class SearchQuery
{
internal SearchQuery(string builtQuery)
{
this.Query = builtQuery;
}
internal string Query { get; set; }
}
}

View File

@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Shodan.Net
{
public class SearchQueryGenerator
{
public QueryGenerator WithQuery() => new QueryGenerator();
}
}

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>2ac1566f-7c77-499e-b9ae-1f029bd7f7e8</ProjectGuid>
<RootNamespace>Shodan.Net</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@@ -0,0 +1,326 @@
using Newtonsoft.Json;
using Shodan.Net.Models;
using Shodan.Net.Models.Options;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
namespace Shodan.Net
{
public class ShodanClient : IShodanAsyncClient
{
private readonly string apikey;
private const string BasePath = "https://api.shodan.io";
//todo error handle!!!
//todo:
/*
/shodan/host/count
/shodan/host/search
/shodan/host/search/tokens
*/
public ShodanClient(string apikey)
{
if(string.IsNullOrWhiteSpace(apikey))
{
throw new ArgumentNullException(nameof(apikey));
}
this.apikey = apikey;
}
/// <summary>
/// Look up the IP address for the provided list of hostnames.
/// </summary>
/// <param name="hostnames">Comma-separated list of hostnames; example "google.com,bing.com" </param>
/// <returns></returns>
public Task<Dictionary<string, string>> DnsLookupAsync(string hostnames)
{
if(string.IsNullOrWhiteSpace(hostnames))
{
throw new ArgumentNullException(hostnames);
}
var url = new Uri($"{BasePath}/dns/resolve?hostnames={hostnames}&key={this.apikey}");
return MakeRequestAsync<Dictionary<string, string>>(url);
}
/// <summary>
/// Calculates a honeypot probability score ranging from 0 (not a honeypot) to 1.0 (is a honeypot).
/// </summary>
/// <param name="ip"></param>
/// <returns></returns>
public async Task<double> Experimental_GetHoneyPotScoreAsync(string ip)
{
if(string.IsNullOrWhiteSpace(ip))
{
throw new ArgumentNullException(nameof(ip));
}
var url = new Uri($"{BasePath}/labs/honeyscore/{ip}?key={apikey}");
var result = await MakeRequestAsync<string>(url);
double resultParsed;
if(!double.TryParse(result, out resultParsed))
{
throw new ShodanException($"honeypot score returned with {result} failed to parse to double");
}
return resultParsed;
}
/// <summary>
/// Returns information about the API plan belonging to the given API key.
/// </summary>
/// <returns></returns>
public Task<ApiStatus> GetApiStatusAsync()
{
var url = new Uri($"{BasePath}/api-info?key={apikey}");
return MakeRequestAsync<ApiStatus>(url);
}
/// <summary>
/// Returns all services that have been found on the given host IP.
/// </summary>
/// <param name="Ip">Host IP address</param>
/// <param name="history">True if all historical banners should be returned (default: False) </param>
/// <param name="minify">True to only return the list of ports and the general host information, no banners. (default: False) </param>
/// <returns></returns>
public Task<Host> GetHostAsync(string Ip, bool history = false, bool minify = false)
{
if(string.IsNullOrWhiteSpace(Ip))
{
throw new ArgumentNullException(nameof(Ip));
}
var builder = new UriBuilder($"{BasePath}/shodan/host/{Ip}")
{
Query = $"key={this.apikey}&history={history.ToString()}&minify={minify.ToString()}"
};
return MakeRequestAsync<Host>(builder.Uri);
}
/// <summary>
/// Get your current IP address as seen from the Internet.
/// </summary>
/// <returns></returns>
public Task<string> GetMyIpAsync()
{
var url = new Uri($"{BasePath}/tools/myip?key={this.apikey}");
return MakeRequestAsync<string>(url);
}
/// <summary>
/// This method returns a list of port numbers that the crawlers are looking for.
/// </summary>
/// <returns></returns>
public Task<List<int>> GetPortsAsync()
{
var builder = new Uri($"{BasePath}/shodan/ports?key={this.apikey}");
return MakeRequestAsync<List<int>>(builder);
}
private async static Task<T> MakeRequestAsync<T>(Uri url, HttpContent content = null, RequestType requstType = RequestType.GET)
where T : class
{
if(requstType != RequestType.GET && content == null)
{
throw new ShodanException($"Request type {requstType} requires content");
}
if(requstType == RequestType.DELETE || requstType == RequestType.PUT)
{
throw new NotImplementedException("Put and Delete requests have not been implemented properly");
}
using(var client = new HttpClient())
{
HttpResponseMessage connection = null;
if(requstType == RequestType.GET)
{
connection = await client.GetAsync(url);
}
else if(requstType == RequestType.POST)
{
connection = await client.PostAsync(url, content);
}
var statusCode = (int)connection.StatusCode;
if(statusCode != 200 && statusCode != 201 && statusCode == 202)
{
//todo error handle
return null;
}
var readResult = await connection.Content.ReadAsStringAsync();
if(typeof(T) == typeof(string))
{
return readResult as T;
}
return JsonConvert.DeserializeObject<T>(readResult);
}
}
/// <summary>
/// Returns information about the Shodan account linked to this API key.
/// </summary>
/// <returns></returns>
public Task<Profile> GetProfileAsync()
{
var url = new Uri($"{BasePath}/account/profile?key={apikey}");
return MakeRequestAsync<Profile>(url);
}
/// <summary>
/// This method returns an object containing all the protocols that can be used when launching an Internet scan.
/// </summary>
/// <returns></returns>
public Task<Dictionary<string, string>> GetProtocolsAsync()
{
var url = new Uri($"{BasePath}/shodan/protocols?key={this.apikey}");
return MakeRequestAsync<Dictionary<string, string>>(url);
}
/// <summary>
/// Use this method to obtain a list of search queries that users have saved in Shodan.
/// </summary>
/// <param name="page"> Page number to iterate over results; each page contains 10 items </param>
/// <param name="sort"> Sort the list based on a property. Possible values are: votes, timestamp </param>
/// <param name="order">Whether to sort the list in ascending or descending order. Possible values are: asc, desc </param>
/// <returns></returns>
public Task<SearchQueries> GetQueriesAsync(int? page = null, SortOptions? sort = null, OrderOption? order = null)
{
var url = new UriBuilder($"{BasePath}/shodan/query")
{
Query = $"key={apikey}"
};
if(sort.HasValue)
{
var sortName = Enum.GetName(typeof(SortOptions), sort.Value);
url.Query = $"{url.Query}&sort={sortName}";
}
if(order.HasValue)
{
var orderName = Enum.GetName(typeof(OrderOption), order.Value);
url.Query = $"{url.Query}&order={orderName}";
}
return MakeRequestAsync<SearchQueries>(url.Uri);
}
/// <summary>
/// Use this method to search the directory of search queries that users have saved in Shodan.
/// </summary>
/// <param name="query"> What to search for in the directory of saved search queries. </param>
/// <param name="page">Page number to iterate over results; each page contains 10 items </param>
/// <returns></returns>
public Task<SearchQueries> SearchQueriesAsync(string query, int? page = null)
{
if(string.IsNullOrWhiteSpace(query))
{
throw new ArgumentNullException(query);
}
var url = new UriBuilder($"{BasePath}/shodan/query/search")
{
Query = $"key={apikey}&query={query}"
};
if(page != null)
{
url.Query = $"{url.Query}&page={page}";
}
return MakeRequestAsync<SearchQueries>(url.Uri);
}
/// <summary>
/// Check the progress of a previously submitted scan request
/// </summary>
/// <param name="id">the unique scan ID that was returned by <see cref="RequstScanAsync(string)"/></param>
/// <returns></returns>
public Task<ScanStatus> GetScanStatusAsync(string id)
{
if(string.IsNullOrWhiteSpace(id))
{
throw new ArgumentNullException(nameof(id));
}
var url = new Uri($"{BasePath}/shodan/scan/{id}");
return MakeRequestAsync<ScanStatus>(url);
}
/// <summary>
/// This method returns an object containing all the services that the Shodan crawlers look at. It can also be used as a quick and practical way to resolve a port number to the name of a service
/// </summary>
/// <returns></returns>
public Task<Dictionary<string, string>> GetServicesAsync()
{
var url = new Uri($"{BasePath}/shodan/services?key={this.apikey}");
return MakeRequestAsync<Dictionary<string, string>>(url);
}
/// <summary>
/// Use this method to obtain a list of popular tags for the saved search queries in Shodan.
/// </summary>
/// <param name="size">The number of tags to return </param>
/// <returns></returns>
public Task<TagResult> GetTagsAsync(int size = 10)
{
var url = new UriBuilder($"{BasePath}/shodan/query/tags")
{
Query = $"key={apikey}&size={size}"
};
return MakeRequestAsync<TagResult>(url.Uri);
}
/// <summary>
/// Use this method to request Shodan to crawl the Internet for a specific port.
/// This method is restricted to security researchers and companies with a Shodan Data license. To apply for access to this method as a researcher, please email jmath@shodan.io with information about your project. Access is restricted to prevent abuse.
/// </summary>
/// <param name="port">The port that Shodan should crawl the Internet for. </param>
/// <param name="protocol">The name of the protocol that should be used to interrogate the port. See <see cref="GetProtocolsAsync"/> for a list of supported protocols. </param>
/// <returns></returns>
public Task<ScanPortResult> RequestInternetPortScanAsync(int port, string protocol)
{
var url = new Uri($"{BasePath}/shodan/scan/internet?key={this.apikey}");
using(var data = new FormUrlEncodedContent(new List<KeyValuePair<string, string>>() {
new KeyValuePair<string, string>("port", port.ToString()),
new KeyValuePair<string, string>("protocol", protocol)
}))
{
return MakeRequestAsync<ScanPortResult>(url, data, RequestType.POST);
}
}
/// <summary>
/// Use this method to request Shodan to crawl a network
/// <strong>Requirements:</strong> This method uses API scan credits: 1 IP consumes 1 scan credit. You must have a paid API plan (either one-time payment or subscription) in order to use this method
/// </summary>
/// <param name="ips"></param>
/// <returns></returns>
public Task<ScanResult> RequstScanAsync(string ips)
{
if(string.IsNullOrWhiteSpace(ips))
{
throw new ArgumentNullException(nameof(ips));
}
if(!ips.Split(',').Any())
{
throw new ArgumentOutOfRangeException($"{ips} must have one valid record");
}
var url = new Uri($"{BasePath}/shodan/scan?key={this.apikey}");
using(var data = new FormUrlEncodedContent(new KeyValuePair<string, string>[] { new KeyValuePair<string, string>("ips", ips) }))
{
return MakeRequestAsync<ScanResult>(url, data, RequestType.POST);
}
}
/// <summary>
/// Look up the hostnames that have been defined for the given list of IP addresses
/// </summary>
/// <param name="ips">Comma-separated list of IP addresses; example "74.125.227.230,204.79.197.200"</param>
/// <returns></returns>
public Task<Dictionary<string, List<string>>> ReverseLookupAsync(string ips)
{
if(string.IsNullOrWhiteSpace(ips))
{
throw new ArgumentNullException(ips);
}
var url = new Uri($"{BasePath}/dns/reverse?ips={ips}&key={this.apikey}");
return MakeRequestAsync<Dictionary<string, List<string>>>(url);
}
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Shodan.Net
{
public class ShodanException : Exception
{
public ShodanException()
{
}
public ShodanException(string message) : base(message)
{
}
public ShodanException(string message, Exception innerException) : base(message, innerException)
{
}
}
}

View File

@@ -0,0 +1,17 @@
{
"version": "1.0.0-*",
"dependencies": {
"NETStandard.Library": "1.5.0-rc2-24027",
"System.Runtime.Serialization.Primitives": "4.1.1-rc2-24027",
"System.Dynamic.Runtime": "4.0.11-rc2-24027",
"Newtonsoft.Json": "8.0.3",
"System.Diagnostics.TraceSource": "4.0.0-rc2-24027"
},
"frameworks": {
"netstandard1.5": {
"imports": "dnxcore50"
}
}
}