basic api done
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -21,6 +21,7 @@
|
|||||||
bld/
|
bld/
|
||||||
[Bb]in/
|
[Bb]in/
|
||||||
[Oo]bj/
|
[Oo]bj/
|
||||||
|
output/
|
||||||
|
|
||||||
# Visual Studio 2015 cache/options directory
|
# Visual Studio 2015 cache/options directory
|
||||||
.vs/
|
.vs/
|
||||||
|
|||||||
45
Readme.md
Normal file
45
Readme.md
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
Visit the official Shodan API documentation at:
|
||||||
|
|
||||||
|
[https://developer.shodan.io](https://developer.shodan.io)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
`install-package Shodan.Net`
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
You need to have an Api key. Get your [api key here](http://www.shodanhq.com/api_doc).
|
||||||
|
|
||||||
|
|
||||||
|
Create a shodan client. Note that ShodanClient inerhits from IDisposable, so you should wrap it in a using, or make sure it will be disposed. Shodan client is thread safe, so you should be able to keep 1 object around for many requests.
|
||||||
|
|
||||||
|
`var client = new Shodan.Net.ShodanClient("myapiKey");`
|
||||||
|
|
||||||
|
Now just query away.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
|
||||||
|
await client.GetPortsAsync();
|
||||||
|
await client.GetHostAsync("172.1.1.0");
|
||||||
|
await client.GetMyIpAsync();
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Searching
|
||||||
|
|
||||||
|
Searching shodan requires you to build up queries, and facets to make it easier we have used a generator pattern to produce queries.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
|
||||||
|
await client.SearchHosts(
|
||||||
|
query: a => a.Withcity("boston")
|
||||||
|
.Withcountry("usa")
|
||||||
|
.Before(DateTime.Now.AddDays(-5)),
|
||||||
|
facet: b => b.WithAsn()
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
@@ -7,7 +7,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{060AE71C-AE0
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{18A95621-2E1B-48F0-9D38-D7B3513F31D3}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{18A95621-2E1B-48F0-9D38-D7B3513F31D3}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
.gitignore = .gitignore
|
||||||
global.json = global.json
|
global.json = global.json
|
||||||
|
Readme.md = Readme.md
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Shodan.Net", "src\Shodan.Net\Shodan.Net.xproj", "{2AC1566F-7C77-499E-B9AE-1F029BD7F7E8}"
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Shodan.Net", "src\Shodan.Net\Shodan.Net.xproj", "{2AC1566F-7C77-499E-B9AE-1F029BD7F7E8}"
|
||||||
|
|||||||
2425
doxy.config
Normal file
2425
doxy.config
Normal file
File diff suppressed because it is too large
Load Diff
@@ -14,8 +14,14 @@ namespace Shodan.Net.UnitTests
|
|||||||
public async Task privateGetsPorts()
|
public async Task privateGetsPorts()
|
||||||
|
|
||||||
{
|
{
|
||||||
var client = new ShodanClient("9F0mxmNSaHbe0mYmefwoCZrChT2h0KzC");
|
var client = new ShodanClient("");
|
||||||
var ports = await client.GetPortsAsync();
|
var ports = await client.SearchHosts(
|
||||||
|
query: a => a.Withcity("boston")
|
||||||
|
.Withcountry("usa")
|
||||||
|
.Before(DateTime.Now.AddDays(-5)),
|
||||||
|
facet: b => b.WithAsn()
|
||||||
|
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
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()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,124 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,9 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Shodan.Net.Models
|
namespace Shodan.Net.Models
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Returns information about the API plan belonging to the given API key.
|
||||||
|
/// </summary>
|
||||||
[DataContract]
|
[DataContract]
|
||||||
public class ApiStatus
|
public class ApiStatus
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Shodan.Net.Models
|
namespace Shodan.Net.Models
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents basic return data
|
||||||
|
/// </summary>
|
||||||
[DataContract]
|
[DataContract]
|
||||||
public class Banner
|
public class Banner
|
||||||
{
|
{
|
||||||
|
|||||||
21
src/Shodan.Net/Models/Facet.cs
Normal file
21
src/Shodan.Net/Models/Facet.cs
Normal 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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents return facet data
|
||||||
|
/// </summary>
|
||||||
|
[DataContract]
|
||||||
|
public class Facet
|
||||||
|
{
|
||||||
|
[DataMember(Name = "count")]
|
||||||
|
public int? Count { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "value")]
|
||||||
|
public string Value { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,9 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Shodan.Net.Models
|
namespace Shodan.Net.Models
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents return data for querying hosts
|
||||||
|
/// </summary>
|
||||||
[DataContract]
|
[DataContract]
|
||||||
public class Host
|
public class Host
|
||||||
{
|
{
|
||||||
@@ -35,5 +38,23 @@ namespace Shodan.Net.Models
|
|||||||
|
|
||||||
[DataMember(Name = "data")]
|
[DataMember(Name = "data")]
|
||||||
public List<Banner> Data { get; set; }
|
public List<Banner> Data { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "city")]
|
||||||
|
public string City { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "longitude")]
|
||||||
|
public double Longitude { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "country_code3")]
|
||||||
|
public string CountryCode3 { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "latitude")]
|
||||||
|
public double Latitude { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "os")]
|
||||||
|
public string Os { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "ports")]
|
||||||
|
public IList<int> Ports { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
namespace Shodan.Net.Models
|
namespace Shodan.Net.Models
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Basic location data returned by shodan
|
||||||
|
/// </summary>
|
||||||
[DataContract]
|
[DataContract]
|
||||||
public class Location
|
public class Location
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,8 +3,11 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Shodan.Net.Models.Options
|
namespace Shodan.Net
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an order of either ascending or descending
|
||||||
|
/// </summary>
|
||||||
public enum OrderOption
|
public enum OrderOption
|
||||||
{
|
{
|
||||||
asc,
|
asc,
|
||||||
|
|||||||
@@ -3,8 +3,11 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Shodan.Net.Models.Options
|
namespace Shodan.Net
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an option to sort
|
||||||
|
/// </summary>
|
||||||
public enum SortOptions
|
public enum SortOptions
|
||||||
{
|
{
|
||||||
votes,
|
votes,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Shodan.Net.Models
|
namespace Shodan.Net.Models.Options
|
||||||
{
|
{
|
||||||
public enum StatusEnum
|
public enum StatusEnum
|
||||||
{
|
{
|
||||||
@@ -6,6 +6,9 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Shodan.Net.Models
|
namespace Shodan.Net.Models
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents data about your profile
|
||||||
|
/// </summary>
|
||||||
[DataContract]
|
[DataContract]
|
||||||
public class Profile
|
public class Profile
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Shodan.Net.Models
|
namespace Shodan.Net.Models
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// result of <see cref="ShodanClient.RequestInternetPortScanAsync(int, string)"/>
|
||||||
|
/// </summary>
|
||||||
[DataContract]
|
[DataContract]
|
||||||
public class ScanPortResult
|
public class ScanPortResult
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Shodan.Net.Models
|
namespace Shodan.Net.Models
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// result of <see cref="ShodanClient.RequstScanAsync(string)"/>
|
||||||
|
/// </summary>
|
||||||
[DataContract]
|
[DataContract]
|
||||||
public class ScanResult
|
public class ScanResult
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using Shodan.Net.Models.Options;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
@@ -6,6 +7,9 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Shodan.Net.Models
|
namespace Shodan.Net.Models
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Result of <see cref="ShodanClient.GetScanStatusAsync(string)"/>
|
||||||
|
/// </summary>
|
||||||
[DataContract]
|
[DataContract]
|
||||||
public class ScanStatus
|
public class ScanStatus
|
||||||
{
|
{
|
||||||
|
|||||||
24
src/Shodan.Net/Models/SearchHostResults.cs
Normal file
24
src/Shodan.Net/Models/SearchHostResults.cs
Normal 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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// result of <see cref="ShodanClient.SearchHosts(SearchQuery, FacetQuery, int, bool)"/> and <see cref="ShodanClient.SearchHostsCount(SearchQuery, FacetQuery)"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataContract]
|
||||||
|
public class SearchHostResults
|
||||||
|
{
|
||||||
|
[DataMember(Name = "matches")]
|
||||||
|
public List<Banner> Matches { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "facets")]
|
||||||
|
public Dictionary<string, Facet> Facets { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "total")]
|
||||||
|
public int? Total { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,6 +28,9 @@ namespace Shodan.Net.Models
|
|||||||
public string Query { get; set; }
|
public string Query { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Result of <see cref="ShodanClient.GetQueriesAsync(int?, SortOptions?, OrderOption?)"/> and <seealso cref="ShodanClient.SearchQueriesAsync(string, int?)"/>
|
||||||
|
/// </summary>
|
||||||
[DataContract]
|
[DataContract]
|
||||||
public class SearchQueries
|
public class SearchQueries
|
||||||
{
|
{
|
||||||
|
|||||||
31
src/Shodan.Net/Models/SearchTokens.cs
Normal file
31
src/Shodan.Net/Models/SearchTokens.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Shodan.Net.Models
|
||||||
|
{
|
||||||
|
[DataContract]
|
||||||
|
public class Attributes
|
||||||
|
{
|
||||||
|
[DataMember(Name = "ports")]
|
||||||
|
public IList<int> Ports { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataContract]
|
||||||
|
public class SearchTokens
|
||||||
|
{
|
||||||
|
[DataMember(Name = "attributes")]
|
||||||
|
public Attributes Attributes { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "errors")]
|
||||||
|
public IList<dynamic> Errors { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "string")]
|
||||||
|
public string String { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "filters")]
|
||||||
|
public IList<dynamic> Filters { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,9 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Shodan.Net.Models
|
namespace Shodan.Net.Models
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// result of <see cref="ShodanClient.GetTagsAsync(int)"/>
|
||||||
|
/// </summary>
|
||||||
[DataContract]
|
[DataContract]
|
||||||
public class TagResult
|
public class TagResult
|
||||||
{
|
{
|
||||||
|
|||||||
18
src/Shodan.Net/Net/IRequstHandler.cs
Normal file
18
src/Shodan.Net/Net/IRequstHandler.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using Shodan.Net.Models.Options;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Shodan.Net
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// sane wrapper of http, and simple abstraction layer for unit testing
|
||||||
|
/// </summary>
|
||||||
|
public interface IRequstHandler : IDisposable
|
||||||
|
{
|
||||||
|
Task<T> MakeRequestAsync<T>(Uri url, HttpContent content = null, RequestType requstType = RequestType.GET)
|
||||||
|
where T : class;
|
||||||
|
}
|
||||||
|
}
|
||||||
85
src/Shodan.Net/Net/RequestHandler.cs
Normal file
85
src/Shodan.Net/Net/RequestHandler.cs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using Shodan.Net.Models.Options;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Shodan.Net
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// sane wrapper of http, and simple abstraction layer for unit testing
|
||||||
|
/// </summary>
|
||||||
|
public class RequestHandler : IRequstHandler
|
||||||
|
{
|
||||||
|
private HttpClient client { get; set; }
|
||||||
|
|
||||||
|
public async 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");
|
||||||
|
}
|
||||||
|
HttpResponseMessage connection = null;
|
||||||
|
if(requstType == RequestType.GET)
|
||||||
|
{
|
||||||
|
connection = await client.GetAsync(url);
|
||||||
|
}
|
||||||
|
else if(requstType == RequestType.POST)
|
||||||
|
{
|
||||||
|
connection = await client.PostAsync(url, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
//todo error handle based on user input
|
||||||
|
connection.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
//var statusCode = (int)connection.StatusCode;
|
||||||
|
//if(statusCode != 200 && statusCode != 201 && statusCode == 202)
|
||||||
|
//{
|
||||||
|
// System.Diagnostics.Trace.TraceError("Error calling shodan API, Status code is not 200" + JsonConvert.SerializeObject(connection.s))
|
||||||
|
// //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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IDisposable Support
|
||||||
|
|
||||||
|
private bool disposedValue = false; // To detect redundant calls
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if(!disposedValue)
|
||||||
|
{
|
||||||
|
if(disposing)
|
||||||
|
{
|
||||||
|
client.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
disposedValue = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This code added to correctly implement the disposable pattern.
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
|
||||||
|
Dispose(true);
|
||||||
|
// TODO: uncomment the following line if the finalizer is overridden above.
|
||||||
|
// GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion IDisposable Support
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,11 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
/*! \mainpage Shodan.Net
|
||||||
|
* Shodan.net is a simple c# implementation of the shodan api
|
||||||
|
* Shodan.net is missing the streams api, but it will come soon. Most of the action occurs in ShodanClient.cs
|
||||||
|
*/
|
||||||
|
|
||||||
// General Information about an assembly is controlled through the following
|
// General Information about an assembly is controlled through the following
|
||||||
// set of attributes. Change these attribute values to modify the information
|
// set of attributes. Change these attribute values to modify the information
|
||||||
// associated with an assembly.
|
// associated with an assembly.
|
||||||
|
|||||||
215
src/Shodan.Net/Search/FacetGenerator.cs
Normal file
215
src/Shodan.Net/Search/FacetGenerator.cs
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Shodan.Net
|
||||||
|
{
|
||||||
|
public class FacetGenerator
|
||||||
|
{
|
||||||
|
private Dictionary<string, int?> queryData = new Dictionary<string, int?>();
|
||||||
|
|
||||||
|
internal FacetGenerator()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
internal string GenerateFacets()
|
||||||
|
{
|
||||||
|
var data = queryData.Select(a => a.Value.HasValue ? $"{a.Key}:{a.Value}" : a.Key);
|
||||||
|
return string.Join(",", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithAsn(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("asn", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithCity(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("city", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithCountry(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("country", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithDevice(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("device", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithDomain(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("domain", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithGeocluster(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("geocluster", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator HasScreenshot(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("has_screenshot", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithIsp(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("isp", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithLink(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("link", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithOrg(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("org", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithOs(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("os", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithPort(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("port", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithPostal(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("postal", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithState(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("state", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithTimestamp_day(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("timestamp_day", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithTimestamp_month(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("timestamp_month", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithTimestamp_year(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("timestamp_year", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithUptime(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("uptime", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithVersion(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("version", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithBitcoin_ip(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("bitcoin.ip", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithBitcoin_ip_count(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("bitcoin.ip_count", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithBitcoin_port(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("bitcoin.port", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithBitcoin_user_agent(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("bitcoin.user_agent", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithBitcoin_version(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("bitcoin.version", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithNtp_ip(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("ntp.ip", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithNtp_ip_count(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("ntp.ip_count", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithNtp_more(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("ntp.more", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithNtp_port(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("ntp.port", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithSsh_cipher(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("ssh.cipher", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithSsh_fingerprint(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("ssh.fingerprint", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithSsh_mac(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("ssh.mac", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacetGenerator WithSsh_type(int? count = null)
|
||||||
|
{
|
||||||
|
queryData.Add("ssh.type", count);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,11 @@ namespace Shodan.Net
|
|||||||
{
|
{
|
||||||
internal Dictionary<string, string> queryData { get; set; }
|
internal Dictionary<string, string> queryData { get; set; }
|
||||||
private HashSet<string> CalledMethods = new HashSet<string>();
|
private HashSet<string> CalledMethods = new HashSet<string>();
|
||||||
|
private string searchText = string.Empty;
|
||||||
|
|
||||||
|
internal QueryGenerator()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public QueryGenerator Before(DateTime time)
|
public QueryGenerator Before(DateTime time)
|
||||||
{
|
{
|
||||||
@@ -39,13 +44,13 @@ namespace Shodan.Net
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public QueryGenerator InCity(string city)
|
public QueryGenerator Withcity(string city)
|
||||||
{
|
{
|
||||||
queryData.Add("city", city);
|
queryData.Add("city", city);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public QueryGenerator InCountry(string country)
|
public QueryGenerator Withcountry(string country)
|
||||||
{
|
{
|
||||||
queryData.Add("country", country);
|
queryData.Add("country", country);
|
||||||
return this;
|
return this;
|
||||||
@@ -182,24 +187,28 @@ namespace Shodan.Net
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SearchQuery Generate(string searchText)
|
public QueryGenerator WithText(string searchText)
|
||||||
|
{
|
||||||
|
if(!string.IsNullOrWhiteSpace(this.searchText))
|
||||||
|
{
|
||||||
|
throw new ShodanException("Method cannot be called twice");
|
||||||
|
}
|
||||||
|
this.searchText = searchText;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal string Generate()
|
||||||
{
|
{
|
||||||
var sb = new StringBuilder(searchText);
|
var sb = new StringBuilder(searchText);
|
||||||
sb.Append(" ");
|
if(!string.IsNullOrWhiteSpace(searchText))
|
||||||
|
{
|
||||||
|
sb.Append(" ");
|
||||||
|
}
|
||||||
foreach(var item in queryData)
|
foreach(var item in queryData)
|
||||||
{
|
{
|
||||||
sb.Append($"{item.Key}:{item.Value}");
|
sb.Append($"{item.Key}:{item.Value}");
|
||||||
}
|
}
|
||||||
return new SearchQuery(sb.ToString());
|
return sb.ToString();
|
||||||
}
|
|
||||||
|
|
||||||
private void EnsureMethodNotCalled(string methodName)
|
|
||||||
{
|
|
||||||
if(CalledMethods.Contains(methodName))
|
|
||||||
{
|
|
||||||
throw new ShodanException($"{methodName} cannot be called twice");
|
|
||||||
}
|
|
||||||
CalledMethods.Add(methodName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
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; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Shodan.Net
|
|
||||||
{
|
|
||||||
public class SearchQueryGenerator
|
|
||||||
{
|
|
||||||
public QueryGenerator WithQuery() => new QueryGenerator();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,18 +11,16 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Shodan.Net
|
namespace Shodan.Net
|
||||||
{
|
{
|
||||||
public class ShodanClient : IShodanAsyncClient
|
/// <summary>
|
||||||
|
/// Main mechanism to talk to the shodan api. This is what you should use to interact with the api
|
||||||
|
/// </summary>
|
||||||
|
public class ShodanClient : IShodanAsyncClient, IDisposable
|
||||||
{
|
{
|
||||||
private readonly string apikey;
|
private readonly string apikey;
|
||||||
private const string BasePath = "https://api.shodan.io";
|
private const string BasePath = "https://api.shodan.io";
|
||||||
|
private IRequstHandler RequestHandler = new RequestHandler();
|
||||||
|
|
||||||
//todo error handle!!!
|
//todo error handle!!!
|
||||||
//todo:
|
|
||||||
/*
|
|
||||||
/shodan/host/count
|
|
||||||
/shodan/host/search
|
|
||||||
/shodan/host/search/tokens
|
|
||||||
*/
|
|
||||||
|
|
||||||
public ShodanClient(string apikey)
|
public ShodanClient(string apikey)
|
||||||
{
|
{
|
||||||
@@ -33,6 +31,12 @@ namespace Shodan.Net
|
|||||||
this.apikey = apikey;
|
this.apikey = apikey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal ShodanClient(string apikey, IRequstHandler requestHandler)
|
||||||
|
: this(apikey)
|
||||||
|
{
|
||||||
|
RequestHandler = requestHandler;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Look up the IP address for the provided list of hostnames.
|
/// Look up the IP address for the provided list of hostnames.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -45,7 +49,86 @@ namespace Shodan.Net
|
|||||||
throw new ArgumentNullException(hostnames);
|
throw new ArgumentNullException(hostnames);
|
||||||
}
|
}
|
||||||
var url = new Uri($"{BasePath}/dns/resolve?hostnames={hostnames}&key={this.apikey}");
|
var url = new Uri($"{BasePath}/dns/resolve?hostnames={hostnames}&key={this.apikey}");
|
||||||
return MakeRequestAsync<Dictionary<string, string>>(url);
|
return RequestHandler.MakeRequestAsync<Dictionary<string, string>>(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Search Shodan using the same query syntax as the website and use facets to get summary information for different properties.
|
||||||
|
/// This method may use API query credits depending on usage. If any of the following criteria are met, your account will be deducated 1 query credit:
|
||||||
|
/// 1. The search query contains a filter.
|
||||||
|
/// 2. Accessing results past the 1st page using the "page". For every 100 results past the 1st page 1 query credit is deducted.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">lambda to generate a query. Shodan search query. The provided string is used to search the database of banners in Shodan, with the additional option to provide filters inside the search query using a "filter:value" format.</param>
|
||||||
|
/// <param name="facet">static class to define facets.</param>
|
||||||
|
/// <param name="page">The page number to page through results 100 at a time (default: 1) </param>
|
||||||
|
/// <param name="minify">True or False; whether or not to truncate some of the larger fields (default: True) </param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public Task<SearchHostResults> SearchHosts(Action<QueryGenerator> query, Action<FacetGenerator> facet = null, int page = 1, bool minify = true)
|
||||||
|
{
|
||||||
|
if(query == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(query));
|
||||||
|
}
|
||||||
|
var queryGenerator = new QueryGenerator();
|
||||||
|
query.Invoke(queryGenerator);
|
||||||
|
var queryResult = queryGenerator.Generate();
|
||||||
|
var url = new UriBuilder($"{BasePath}/shodan/host/search")
|
||||||
|
{
|
||||||
|
Query = $"key={apikey}&query={queryResult}&minify={minify.ToString()}"
|
||||||
|
};
|
||||||
|
if(facet != null)
|
||||||
|
{
|
||||||
|
var facetGenerator = new FacetGenerator();
|
||||||
|
facet.Invoke(facetGenerator);
|
||||||
|
url.Query = $"{url.Query}&facets={facetGenerator.GenerateFacets()}";
|
||||||
|
}
|
||||||
|
if(page > 1)
|
||||||
|
{
|
||||||
|
url.Query = $"{url.Query}&page={page}";
|
||||||
|
}
|
||||||
|
return RequestHandler.MakeRequestAsync<SearchHostResults>(url.Uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This method behaves identical to <see cref="SearchHosts(SearchQuery, FacetQuery, int, bool)"/>" with the only difference that this method does not return any host results, it only returns the total number of results that matched the query and any facet information that was requested. As a result this method does not consume query credits.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query"></param>
|
||||||
|
/// <param name="facet"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public Task<SearchHostResults> SearchHostsCount(Action<QueryGenerator> query, Action<FacetGenerator> facet = null)
|
||||||
|
{
|
||||||
|
if(query == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(query));
|
||||||
|
}
|
||||||
|
var queryGenObj = new QueryGenerator();
|
||||||
|
query.Invoke(queryGenObj);
|
||||||
|
|
||||||
|
var url = new UriBuilder($"{BasePath}/shodan/host/count")
|
||||||
|
{
|
||||||
|
Query = $"key={apikey}&query={queryGenObj.Generate()}"
|
||||||
|
};
|
||||||
|
if(facet != null)
|
||||||
|
{
|
||||||
|
var facetGenObj = new FacetGenerator();
|
||||||
|
facet.Invoke(facetGenObj);
|
||||||
|
|
||||||
|
url.Query = $"{url.Query}&facets={facetGenObj.GenerateFacets()}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return RequestHandler.MakeRequestAsync<SearchHostResults>(url.Uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<SearchTokens> SearchTokens(Action<QueryGenerator> query)
|
||||||
|
{
|
||||||
|
if(query == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(query));
|
||||||
|
}
|
||||||
|
var queryObj = new QueryGenerator();
|
||||||
|
query.Invoke(queryObj);
|
||||||
|
var url = new Uri($"{BasePath}/shodan/host/search/tokens?key={apikey}&query={queryObj.Generate()}");
|
||||||
|
return RequestHandler.MakeRequestAsync<SearchTokens>(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -60,7 +143,7 @@ namespace Shodan.Net
|
|||||||
throw new ArgumentNullException(nameof(ip));
|
throw new ArgumentNullException(nameof(ip));
|
||||||
}
|
}
|
||||||
var url = new Uri($"{BasePath}/labs/honeyscore/{ip}?key={apikey}");
|
var url = new Uri($"{BasePath}/labs/honeyscore/{ip}?key={apikey}");
|
||||||
var result = await MakeRequestAsync<string>(url);
|
var result = await RequestHandler.MakeRequestAsync<string>(url);
|
||||||
double resultParsed;
|
double resultParsed;
|
||||||
if(!double.TryParse(result, out resultParsed))
|
if(!double.TryParse(result, out resultParsed))
|
||||||
{
|
{
|
||||||
@@ -76,7 +159,7 @@ namespace Shodan.Net
|
|||||||
public Task<ApiStatus> GetApiStatusAsync()
|
public Task<ApiStatus> GetApiStatusAsync()
|
||||||
{
|
{
|
||||||
var url = new Uri($"{BasePath}/api-info?key={apikey}");
|
var url = new Uri($"{BasePath}/api-info?key={apikey}");
|
||||||
return MakeRequestAsync<ApiStatus>(url);
|
return RequestHandler.MakeRequestAsync<ApiStatus>(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -97,7 +180,7 @@ namespace Shodan.Net
|
|||||||
Query = $"key={this.apikey}&history={history.ToString()}&minify={minify.ToString()}"
|
Query = $"key={this.apikey}&history={history.ToString()}&minify={minify.ToString()}"
|
||||||
};
|
};
|
||||||
|
|
||||||
return MakeRequestAsync<Host>(builder.Uri);
|
return RequestHandler.MakeRequestAsync<Host>(builder.Uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -107,7 +190,7 @@ namespace Shodan.Net
|
|||||||
public Task<string> GetMyIpAsync()
|
public Task<string> GetMyIpAsync()
|
||||||
{
|
{
|
||||||
var url = new Uri($"{BasePath}/tools/myip?key={this.apikey}");
|
var url = new Uri($"{BasePath}/tools/myip?key={this.apikey}");
|
||||||
return MakeRequestAsync<string>(url);
|
return RequestHandler.MakeRequestAsync<string>(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -117,45 +200,7 @@ namespace Shodan.Net
|
|||||||
public Task<List<int>> GetPortsAsync()
|
public Task<List<int>> GetPortsAsync()
|
||||||
{
|
{
|
||||||
var builder = new Uri($"{BasePath}/shodan/ports?key={this.apikey}");
|
var builder = new Uri($"{BasePath}/shodan/ports?key={this.apikey}");
|
||||||
return MakeRequestAsync<List<int>>(builder);
|
return RequestHandler.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>
|
/// <summary>
|
||||||
@@ -165,7 +210,7 @@ namespace Shodan.Net
|
|||||||
public Task<Profile> GetProfileAsync()
|
public Task<Profile> GetProfileAsync()
|
||||||
{
|
{
|
||||||
var url = new Uri($"{BasePath}/account/profile?key={apikey}");
|
var url = new Uri($"{BasePath}/account/profile?key={apikey}");
|
||||||
return MakeRequestAsync<Profile>(url);
|
return RequestHandler.MakeRequestAsync<Profile>(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -175,7 +220,7 @@ namespace Shodan.Net
|
|||||||
public Task<Dictionary<string, string>> GetProtocolsAsync()
|
public Task<Dictionary<string, string>> GetProtocolsAsync()
|
||||||
{
|
{
|
||||||
var url = new Uri($"{BasePath}/shodan/protocols?key={this.apikey}");
|
var url = new Uri($"{BasePath}/shodan/protocols?key={this.apikey}");
|
||||||
return MakeRequestAsync<Dictionary<string, string>>(url);
|
return RequestHandler.MakeRequestAsync<Dictionary<string, string>>(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -201,7 +246,7 @@ namespace Shodan.Net
|
|||||||
var orderName = Enum.GetName(typeof(OrderOption), order.Value);
|
var orderName = Enum.GetName(typeof(OrderOption), order.Value);
|
||||||
url.Query = $"{url.Query}&order={orderName}";
|
url.Query = $"{url.Query}&order={orderName}";
|
||||||
}
|
}
|
||||||
return MakeRequestAsync<SearchQueries>(url.Uri);
|
return RequestHandler.MakeRequestAsync<SearchQueries>(url.Uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -224,7 +269,7 @@ namespace Shodan.Net
|
|||||||
{
|
{
|
||||||
url.Query = $"{url.Query}&page={page}";
|
url.Query = $"{url.Query}&page={page}";
|
||||||
}
|
}
|
||||||
return MakeRequestAsync<SearchQueries>(url.Uri);
|
return RequestHandler.MakeRequestAsync<SearchQueries>(url.Uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -239,7 +284,7 @@ namespace Shodan.Net
|
|||||||
throw new ArgumentNullException(nameof(id));
|
throw new ArgumentNullException(nameof(id));
|
||||||
}
|
}
|
||||||
var url = new Uri($"{BasePath}/shodan/scan/{id}");
|
var url = new Uri($"{BasePath}/shodan/scan/{id}");
|
||||||
return MakeRequestAsync<ScanStatus>(url);
|
return RequestHandler.MakeRequestAsync<ScanStatus>(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -249,7 +294,7 @@ namespace Shodan.Net
|
|||||||
public Task<Dictionary<string, string>> GetServicesAsync()
|
public Task<Dictionary<string, string>> GetServicesAsync()
|
||||||
{
|
{
|
||||||
var url = new Uri($"{BasePath}/shodan/services?key={this.apikey}");
|
var url = new Uri($"{BasePath}/shodan/services?key={this.apikey}");
|
||||||
return MakeRequestAsync<Dictionary<string, string>>(url);
|
return RequestHandler.MakeRequestAsync<Dictionary<string, string>>(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -263,7 +308,7 @@ namespace Shodan.Net
|
|||||||
{
|
{
|
||||||
Query = $"key={apikey}&size={size}"
|
Query = $"key={apikey}&size={size}"
|
||||||
};
|
};
|
||||||
return MakeRequestAsync<TagResult>(url.Uri);
|
return RequestHandler.MakeRequestAsync<TagResult>(url.Uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -281,7 +326,7 @@ namespace Shodan.Net
|
|||||||
new KeyValuePair<string, string>("protocol", protocol)
|
new KeyValuePair<string, string>("protocol", protocol)
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
return MakeRequestAsync<ScanPortResult>(url, data, RequestType.POST);
|
return RequestHandler.MakeRequestAsync<ScanPortResult>(url, data, RequestType.POST);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,7 +349,7 @@ namespace Shodan.Net
|
|||||||
var url = new Uri($"{BasePath}/shodan/scan?key={this.apikey}");
|
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) }))
|
using(var data = new FormUrlEncodedContent(new KeyValuePair<string, string>[] { new KeyValuePair<string, string>("ips", ips) }))
|
||||||
{
|
{
|
||||||
return MakeRequestAsync<ScanResult>(url, data, RequestType.POST);
|
return RequestHandler.MakeRequestAsync<ScanResult>(url, data, RequestType.POST);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,7 +365,35 @@ namespace Shodan.Net
|
|||||||
throw new ArgumentNullException(ips);
|
throw new ArgumentNullException(ips);
|
||||||
}
|
}
|
||||||
var url = new Uri($"{BasePath}/dns/reverse?ips={ips}&key={this.apikey}");
|
var url = new Uri($"{BasePath}/dns/reverse?ips={ips}&key={this.apikey}");
|
||||||
return MakeRequestAsync<Dictionary<string, List<string>>>(url);
|
return RequestHandler.MakeRequestAsync<Dictionary<string, List<string>>>(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region IDisposable Support
|
||||||
|
|
||||||
|
private bool disposedValue = false; // To detect redundant calls
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if(!disposedValue)
|
||||||
|
{
|
||||||
|
if(disposing)
|
||||||
|
{
|
||||||
|
RequestHandler.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
disposedValue = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This code added to correctly implement the disposable pattern.
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
|
||||||
|
Dispose(true);
|
||||||
|
// TODO: uncomment the following line if the finalizer is overridden above.
|
||||||
|
// GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion IDisposable Support
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Shodan.Net
|
namespace Shodan.Net
|
||||||
|
|||||||
@@ -1,17 +1,27 @@
|
|||||||
{
|
{
|
||||||
"version": "1.0.0-*",
|
"version": "1.0.0-*",
|
||||||
|
"authors": [ "Tommy Parnell" ],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"NETStandard.Library": "1.5.0-rc2-24027",
|
"NETStandard.Library": "1.5.0-rc2-24027",
|
||||||
"System.Runtime.Serialization.Primitives": "4.1.1-rc2-24027",
|
"System.Runtime.Serialization.Primitives": "4.1.1-rc2-24027",
|
||||||
"System.Dynamic.Runtime": "4.0.11-rc2-24027",
|
"System.Dynamic.Runtime": "4.0.11-rc2-24027",
|
||||||
"Newtonsoft.Json": "8.0.3",
|
"Newtonsoft.Json": "8.0.3"
|
||||||
"System.Diagnostics.TraceSource": "4.0.0-rc2-24027"
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
"netstandard1.5": {
|
"netstandard1.5": {
|
||||||
"imports": "dnxcore50"
|
"imports": "dnxcore50"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"buildOptions": {
|
||||||
|
"xmlDoc": false
|
||||||
|
},
|
||||||
|
"packOptions": {
|
||||||
|
"owners": [ "Tommy Parnell" ],
|
||||||
|
"summary": "Wrapper over the shodan.io api",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/tparnell8/Shodan.Net"
|
||||||
|
},
|
||||||
|
"tags": [ "shodan.io" ]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user