Showdan.Net
c# client for the shodan api
ShodanClient.cs
1 using Newtonsoft.Json;
2 using Shodan.Net.Models;
4 using System;
5 using System.Collections.Generic;
6 using System.Diagnostics;
7 using System.Linq;
8 using System.Net;
9 using System.Net.Http;
10 using System.Threading.Tasks;
11 
12 namespace Shodan.Net
13 {
17  public class ShodanClient : IShodanAsyncClient, IDisposable
18  {
19  private readonly string apikey;
20  private const string BasePath = "https://api.shodan.io";
21  private IRequstHandler RequestHandler = new RequestHandler();
22 
23  //todo error handle!!!
24 
25  public ShodanClient(string apikey)
26  {
27  if(string.IsNullOrWhiteSpace(apikey))
28  {
29  throw new ArgumentNullException(nameof(apikey));
30  }
31  this.apikey = apikey;
32  }
33 
34  internal ShodanClient(string apikey, IRequstHandler requestHandler)
35  : this(apikey)
36  {
37  RequestHandler = requestHandler;
38  }
39 
45  public Task<Dictionary<string, string>> DnsLookupAsync(string hostnames)
46  {
47  if(string.IsNullOrWhiteSpace(hostnames))
48  {
49  throw new ArgumentNullException(hostnames);
50  }
51  var url = new Uri($"{BasePath}/dns/resolve?hostnames={hostnames}&key={this.apikey}");
52  return RequestHandler.MakeRequestAsync<Dictionary<string, string>>(url);
53  }
54 
66  public Task<SearchHostResults> SearchHosts(Action<QueryGenerator> query, Action<FacetGenerator> facet = null, int page = 1, bool minify = true)
67  {
68  if(query == null)
69  {
70  throw new ArgumentNullException(nameof(query));
71  }
72  var queryGenerator = new QueryGenerator();
73  query.Invoke(queryGenerator);
74  var queryResult = queryGenerator.Generate();
75  var url = new UriBuilder($"{BasePath}/shodan/host/search")
76  {
77  Query = $"key={apikey}&query={queryResult}&minify={minify.ToString()}"
78  };
79  if(facet != null)
80  {
81  var facetGenerator = new FacetGenerator();
82  facet.Invoke(facetGenerator);
83  url.Query = $"{url.Query}&facets={facetGenerator.GenerateFacets()}";
84  }
85  if(page > 1)
86  {
87  url.Query = $"{url.Query}&page={page}";
88  }
89  return RequestHandler.MakeRequestAsync<SearchHostResults>(url.Uri);
90  }
91 
98  public Task<SearchHostResults> SearchHostsCount(Action<QueryGenerator> query, Action<FacetGenerator> facet = null)
99  {
100  if(query == null)
101  {
102  throw new ArgumentNullException(nameof(query));
103  }
104  var queryGenObj = new QueryGenerator();
105  query.Invoke(queryGenObj);
106 
107  var url = new UriBuilder($"{BasePath}/shodan/host/count")
108  {
109  Query = $"key={apikey}&query={queryGenObj.Generate()}"
110  };
111  if(facet != null)
112  {
113  var facetGenObj = new FacetGenerator();
114  facet.Invoke(facetGenObj);
115 
116  url.Query = $"{url.Query}&facets={facetGenObj.GenerateFacets()}";
117  }
118 
119  return RequestHandler.MakeRequestAsync<SearchHostResults>(url.Uri);
120  }
121 
122  public Task<SearchTokens> SearchTokens(Action<QueryGenerator> query)
123  {
124  if(query == null)
125  {
126  throw new ArgumentNullException(nameof(query));
127  }
128  var queryObj = new QueryGenerator();
129  query.Invoke(queryObj);
130  var url = new Uri($"{BasePath}/shodan/host/search/tokens?key={apikey}&query={queryObj.Generate()}");
131  return RequestHandler.MakeRequestAsync<SearchTokens>(url);
132  }
133 
139  public async Task<double> Experimental_GetHoneyPotScoreAsync(string ip)
140  {
141  if(string.IsNullOrWhiteSpace(ip))
142  {
143  throw new ArgumentNullException(nameof(ip));
144  }
145  var url = new Uri($"{BasePath}/labs/honeyscore/{ip}?key={apikey}");
146  var result = await RequestHandler.MakeRequestAsync<string>(url);
147  double resultParsed;
148  if(!double.TryParse(result, out resultParsed))
149  {
150  throw new ShodanException($"honeypot score returned with {result} failed to parse to double");
151  }
152  return resultParsed;
153  }
154 
159  public Task<ApiStatus> GetApiStatusAsync()
160  {
161  var url = new Uri($"{BasePath}/api-info?key={apikey}");
162  return RequestHandler.MakeRequestAsync<ApiStatus>(url);
163  }
164 
172  public Task<Host> GetHostAsync(string Ip, bool history = false, bool minify = false)
173  {
174  if(string.IsNullOrWhiteSpace(Ip))
175  {
176  throw new ArgumentNullException(nameof(Ip));
177  }
178  var builder = new UriBuilder($"{BasePath}/shodan/host/{Ip}")
179  {
180  Query = $"key={this.apikey}&history={history.ToString()}&minify={minify.ToString()}"
181  };
182 
183  return RequestHandler.MakeRequestAsync<Host>(builder.Uri);
184  }
185 
190  public Task<string> GetMyIpAsync()
191  {
192  var url = new Uri($"{BasePath}/tools/myip?key={this.apikey}");
193  return RequestHandler.MakeRequestAsync<string>(url);
194  }
195 
200  public Task<List<int>> GetPortsAsync()
201  {
202  var builder = new Uri($"{BasePath}/shodan/ports?key={this.apikey}");
203  return RequestHandler.MakeRequestAsync<List<int>>(builder);
204  }
205 
210  public Task<Profile> GetProfileAsync()
211  {
212  var url = new Uri($"{BasePath}/account/profile?key={apikey}");
213  return RequestHandler.MakeRequestAsync<Profile>(url);
214  }
215 
220  public Task<Dictionary<string, string>> GetProtocolsAsync()
221  {
222  var url = new Uri($"{BasePath}/shodan/protocols?key={this.apikey}");
223  return RequestHandler.MakeRequestAsync<Dictionary<string, string>>(url);
224  }
225 
233  public Task<SearchQueries> GetQueriesAsync(int? page = null, SortOptions? sort = null, OrderOption? order = null)
234  {
235  var url = new UriBuilder($"{BasePath}/shodan/query")
236  {
237  Query = $"key={apikey}"
238  };
239  if(sort.HasValue)
240  {
241  var sortName = Enum.GetName(typeof(SortOptions), sort.Value);
242  url.Query = $"{url.Query}&sort={sortName}";
243  }
244  if(order.HasValue)
245  {
246  var orderName = Enum.GetName(typeof(OrderOption), order.Value);
247  url.Query = $"{url.Query}&order={orderName}";
248  }
249  return RequestHandler.MakeRequestAsync<SearchQueries>(url.Uri);
250  }
251 
258  public Task<SearchQueries> SearchQueriesAsync(string query, int? page = null)
259  {
260  if(string.IsNullOrWhiteSpace(query))
261  {
262  throw new ArgumentNullException(query);
263  }
264  var url = new UriBuilder($"{BasePath}/shodan/query/search")
265  {
266  Query = $"key={apikey}&query={query}"
267  };
268  if(page != null)
269  {
270  url.Query = $"{url.Query}&page={page}";
271  }
272  return RequestHandler.MakeRequestAsync<SearchQueries>(url.Uri);
273  }
274 
280  public Task<ScanStatus> GetScanStatusAsync(string id)
281  {
282  if(string.IsNullOrWhiteSpace(id))
283  {
284  throw new ArgumentNullException(nameof(id));
285  }
286  var url = new Uri($"{BasePath}/shodan/scan/{id}");
287  return RequestHandler.MakeRequestAsync<ScanStatus>(url);
288  }
289 
294  public Task<Dictionary<string, string>> GetServicesAsync()
295  {
296  var url = new Uri($"{BasePath}/shodan/services?key={this.apikey}");
297  return RequestHandler.MakeRequestAsync<Dictionary<string, string>>(url);
298  }
299 
305  public Task<TagResult> GetTagsAsync(int size = 10)
306  {
307  var url = new UriBuilder($"{BasePath}/shodan/query/tags")
308  {
309  Query = $"key={apikey}&size={size}"
310  };
311  return RequestHandler.MakeRequestAsync<TagResult>(url.Uri);
312  }
313 
321  public Task<ScanPortResult> RequestInternetPortScanAsync(int port, string protocol)
322  {
323  var url = new Uri($"{BasePath}/shodan/scan/internet?key={this.apikey}");
324  using(var data = new FormUrlEncodedContent(new List<KeyValuePair<string, string>>() {
325  new KeyValuePair<string, string>("port", port.ToString()),
326  new KeyValuePair<string, string>("protocol", protocol)
327  }))
328  {
329  return RequestHandler.MakeRequestAsync<ScanPortResult>(url, data, RequestType.POST);
330  }
331  }
332 
339  public Task<ScanResult> RequstScanAsync(string ips)
340  {
341  if(string.IsNullOrWhiteSpace(ips))
342  {
343  throw new ArgumentNullException(nameof(ips));
344  }
345  if(!ips.Split(',').Any())
346  {
347  throw new ArgumentOutOfRangeException($"{ips} must have one valid record");
348  }
349  var url = new Uri($"{BasePath}/shodan/scan?key={this.apikey}");
350  using(var data = new FormUrlEncodedContent(new KeyValuePair<string, string>[] { new KeyValuePair<string, string>("ips", ips) }))
351  {
352  return RequestHandler.MakeRequestAsync<ScanResult>(url, data, RequestType.POST);
353  }
354  }
355 
361  public Task<Dictionary<string, List<string>>> ReverseLookupAsync(string ips)
362  {
363  if(string.IsNullOrWhiteSpace(ips))
364  {
365  throw new ArgumentNullException(ips);
366  }
367  var url = new Uri($"{BasePath}/dns/reverse?ips={ips}&key={this.apikey}");
368  return RequestHandler.MakeRequestAsync<Dictionary<string, List<string>>>(url);
369  }
370 
371  #region IDisposable Support
372 
373  private bool disposedValue = false; // To detect redundant calls
374 
375  protected virtual void Dispose(bool disposing)
376  {
377  if(!disposedValue)
378  {
379  if(disposing)
380  {
381  RequestHandler.Dispose();
382  }
383 
384  disposedValue = true;
385  }
386  }
387 
388  // This code added to correctly implement the disposable pattern.
389  public void Dispose()
390  {
391  // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
392  Dispose(true);
393  // TODO: uncomment the following line if the finalizer is overridden above.
394  // GC.SuppressFinalize(this);
395  }
396 
397  #endregion IDisposable Support
398  }
399 }
Main mechanism to talk to the shodan api. This is what you should use to interact with the api ...
Definition: ShodanClient.cs:17
Task< Host > GetHostAsync(string Ip, bool history=false, bool minify=false)
Returns all services that have been found on the given host IP.
Task< ScanPortResult > RequestInternetPortScanAsync(int port, string protocol)
Use this method to request Shodan to crawl the Internet for a specific port. This method is restricte...
Result of ShodanClient.GetQueriesAsync(int?, SortOptions?, OrderOption?) and ShodanClient.SearchQueriesAsync(string, int?)
Task< Dictionary< string, string > > GetServicesAsync()
This method returns an object containing all the services that the Shodan crawlers look at...
result of ShodanClient.GetTagsAsync(int)
Definition: TagResult.cs:13
Task< SearchHostResults > SearchHosts(Action< QueryGenerator > query, Action< FacetGenerator > facet=null, int page=1, bool minify=true)
Search Shodan using the same query syntax as the website and use facets to get summary information fo...
Definition: ShodanClient.cs:66
Task< Dictionary< string, string > > GetProtocolsAsync()
This method returns an object containing all the protocols that can be used when launching an Interne...
Task< string > GetMyIpAsync()
Get your current IP address as seen from the Internet.
Task< Dictionary< string, List< string > > > ReverseLookupAsync(string ips)
Look up the hostnames that have been defined for the given list of IP addresses
OrderOption
Represents an order of either ascending or descending
Definition: OrderOption.cs:11
Represents data about your profile
Definition: Profile.cs:13
sane wrapper of http, and simple abstraction layer for unit testing
Task< ScanResult > RequstScanAsync(string ips)
Use this method to request Shodan to crawl a network Requirements: This method uses API scan credits:...
sane wrapper of http, and simple abstraction layer for unit testing
Returns information about the API plan belonging to the given API key.
Definition: ApiStatus.cs:13
Represents return data for querying hosts
Definition: Host.cs:13
result of ShodanClient.RequestInternetPortScanAsync(int, string)
Task< ScanStatus > GetScanStatusAsync(string id)
Check the progress of a previously submitted scan request
Task< SearchQueries > GetQueriesAsync(int?page=null, SortOptions?sort=null, OrderOption?order=null)
Use this method to obtain a list of search queries that users have saved in Shodan.
Task< SearchQueries > SearchQueriesAsync(string query, int?page=null)
Use this method to search the directory of search queries that users have saved in Shodan...
Task< ApiStatus > GetApiStatusAsync()
Returns information about the API plan belonging to the given API key.
SortOptions
Represents an option to sort
Definition: SortOptions.cs:11
Result of ShodanClient.GetScanStatusAsync(string)
Definition: ScanStatus.cs:14
result of ShodanClient.RequstScanAsync(string)
Definition: ScanResult.cs:13
async Task< double > Experimental_GetHoneyPotScoreAsync(string ip)
Calculates a honeypot probability score ranging from 0 (not a honeypot) to 1.0 (is a honeypot)...
Task< List< int > > GetPortsAsync()
This method returns a list of port numbers that the crawlers are looking for.
Task< TagResult > GetTagsAsync(int size=10)
Use this method to obtain a list of popular tags for the saved search queries in Shodan.
Task< Dictionary< string, string > > DnsLookupAsync(string hostnames)
Look up the IP address for the provided list of hostnames.
Definition: ShodanClient.cs:45
result of ShodanClient.SearchHosts(SearchQuery, FacetQuery, int, bool) and ShodanClient.SearchHostsCount(SearchQuery, FacetQuery)
Task< SearchHostResults > SearchHostsCount(Action< QueryGenerator > query, Action< FacetGenerator > facet=null)
This method behaves identical to SearchHosts(SearchQuery, FacetQuery, int, bool)" with the only diffe...
Definition: ShodanClient.cs:98
Task< Profile > GetProfileAsync()
Returns information about the Shodan account linked to this API key.