7c849b7fef0c26c73b5d72e10d3d6d779cc52a42
[toolkit.git] / samples / dnsguard / src / main / java / org / sdnhub / dnsguard / DnsGuard.java
1 /*
2  * Copyright (c) 2014 SDN Hub.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  *
8  * author: Luis Chiang
9  */
10
11 package org.sdnhub.dnsguard;
12
13 import java.io.BufferedReader;
14 import java.io.File;
15 import java.io.FileInputStream;
16 import java.io.FileNotFoundException;
17 import java.io.FileOutputStream;
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.io.InputStreamReader;
21 import java.io.OutputStream;
22 import java.io.StringWriter;
23 import java.net.URL;
24 import java.util.ArrayList;
25 import java.util.Enumeration;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Properties;
29 import java.util.Set;
30
31 import net.dougharris.dns.RFC1035;
32
33 import org.opendaylight.controller.hosttracker.IfHostListener;
34 import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
35 import org.opendaylight.controller.sal.action.Action;
36 import org.opendaylight.controller.sal.action.Controller;
37 import org.opendaylight.controller.sal.core.Node;
38 import org.opendaylight.controller.sal.core.NodeConnector;
39 import org.opendaylight.controller.sal.core.Property;
40 import org.opendaylight.controller.sal.core.UpdateType;
41 import org.opendaylight.controller.sal.flowprogrammer.Flow;
42 import org.opendaylight.controller.sal.flowprogrammer.IFlowProgrammerService;
43 import org.opendaylight.controller.sal.inventory.IListenInventoryUpdates;
44 import org.opendaylight.controller.sal.match.Match;
45 import org.opendaylight.controller.sal.match.MatchField;
46 import org.opendaylight.controller.sal.match.MatchType;
47 import org.opendaylight.controller.sal.packet.Ethernet;
48 import org.opendaylight.controller.sal.packet.IDataPacketService;
49 import org.opendaylight.controller.sal.packet.IListenDataPacket;
50 import org.opendaylight.controller.sal.packet.IPv4;
51 import org.opendaylight.controller.sal.packet.Packet;
52 import org.opendaylight.controller.sal.packet.PacketResult;
53 import org.opendaylight.controller.sal.packet.RawPacket;
54 import org.opendaylight.controller.sal.packet.UDP;
55 import org.opendaylight.controller.sal.utils.EtherTypes;
56 import org.opendaylight.controller.sal.utils.NetUtils;
57 import org.opendaylight.controller.sal.utils.ServiceHelper;
58 import org.opendaylight.controller.sal.utils.Status;
59 import org.opendaylight.controller.switchmanager.ISwitchManager;
60 import org.osgi.framework.BundleContext;
61 import org.osgi.framework.FrameworkUtil;
62 import org.sdnhub.dnsguard.renders.DnsRecordReply;
63 import org.sdnhub.dnsguard.renders.DnsUsage;
64 import org.sdnhub.dnsguard.renders.Violator;
65 //import org.sdnhub.learningswitch.ILearningSwitch;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
68
69 import org.hsqldb.Server;
70
71 public class DnsGuard implements IListenDataPacket, IListenInventoryUpdates,
72                 IfHostListener, IDnsGuard {
73         
74         private Server hsqlServer;
75         private DnsGuardPersistence database;
76         
77         protected static final Logger log = LoggerFactory.getLogger(DnsGuard.class);
78         private String dbconfig_filename = "database.config";
79         
80         private IDataPacketService dataPacketService = null;
81
82         void setDataPacketService(IDataPacketService s) {
83                 this.dataPacketService = s;
84         }
85
86         void unsetDataPacketService(IDataPacketService s) {
87                 if (this.dataPacketService == s) {
88                         this.dataPacketService = null;
89                 }
90         }
91
92         private ISwitchManager swManager = null;
93
94         void setSwitchManager(ISwitchManager s) {
95                 log.info("set ISwitchManager");
96                 this.swManager = s;
97         }
98
99         void unsetSwitchManager(ISwitchManager s) {
100                 if (this.swManager == s) {
101                         this.swManager = null;
102                 }
103         }
104
105         private IFlowProgrammerService programmer = null;
106
107         public void setFlowProgrammerService(IFlowProgrammerService s) {
108                 this.programmer = s;
109         }
110
111         public void unsetFlowProgrammerService(IFlowProgrammerService s) {
112                 if (this.programmer == s) {
113                         this.programmer = null;
114                 }
115         }
116
117         @Override
118         public PacketResult receiveDataPacket(RawPacket inPkt) {
119
120                 // log.info("packet arrived");
121
122                 Packet etherpack = this.dataPacketService.decodeDataPacket(inPkt);
123
124                 if (etherpack instanceof Ethernet) {
125
126                         Packet ippack = etherpack.getPayload();
127
128                         if (ippack instanceof IPv4) {
129
130                                 Packet udppackt = ippack.getPayload();
131
132                                 if (udppackt instanceof UDP) {
133
134                                         // & 0xFFFF
135                                         short sport = ((UDP) udppackt).getSourcePort();
136                                         short dport = ((UDP) udppackt).getDestinationPort();
137
138                                         if (sport == 53 || dport == 53) {
139
140                                                 int sip = ((IPv4) ippack).getSourceAddress();
141                                                 int dip = ((IPv4) ippack).getDestinationAddress();
142
143                                                 byte[] dnspacket = ((UDP) udppackt).getRawPayload();
144
145                                                 if (dnspacket != null) {
146
147                                                         RFC1035 dns_msg = new RFC1035(dnspacket);
148
149                                                         try {
150                                                                 dns_msg.parse();
151
152                                                                 if (dns_msg.qr == false) {
153
154                                                                         log.info(
155                                                                                         "DNS REPLY len: {}  from: {} to: {} dport: {}",
156                                                                                         dnspacket.length, NetUtils
157                                                                                                         .getInetAddress(sip)
158                                                                                                         .toString(), NetUtils
159                                                                                                         .getInetAddress(dip)
160                                                                                                         .toString(), dport);
161
162                                                                         DnsReply reply = new DnsReply(dns_msg);
163
164                                                                         if (dns_msg.getCountAnswer() > 0) {
165                                                                                 reply.setPacketHeaders(NetUtils
166                                                                                                 .getInetAddress(sip).toString()
167                                                                                                 .replace("/", ""), NetUtils
168                                                                                                 .getInetAddress(dip).toString()
169                                                                                                 .replace("/", ""), sport, dport);
170                                                                                 database.save(reply);
171                                                                         }
172                                                                 }
173
174                                                         } catch (Exception ex) {
175
176                                                                 log.error(ex.getLocalizedMessage());
177                                                         }
178                                                 }
179                                         }
180                                 }
181                         }
182                 }
183
184                 return PacketResult.KEEP_PROCESSING;
185         }
186
187         void init() {
188
189 //              ILearningSwitch simple = (ILearningSwitch) ServiceHelper.getInstance(
190 //                              ILearningSwitch.class, "default", this);
191 //              if (simple == null) {
192 //                      log.error("Not able to find the learningswitch");
193 //              } else {
194 //                      log.info("learningswitch found, loaded before dnsguard OK");
195 //              }
196
197                 // read info from config file
198                 
199         }
200
201         void start(){
202                 
203 //                 hsqlServer = new Server();
204 //
205 //         // HSQLDB prints out a lot of informations when
206 //         // starting and closing, which we don't need now.
207 //         // Normally you should point the setLogWriter
208 //         // to some Writer object that could store the logs.
209 //         hsqlServer.setLogWriter(null);
210 //         hsqlServer.setSilent(true);
211 //
212 //         // The actual database will be named 'xdb' and its
213 //         // settings and data will be stored in files
214 //         // testdb.properties and testdb.script
215 //         hsqlServer.setDatabaseName(0, "dnsspy");
216 //         hsqlServer.setDatabasePath(0, "file:dbfile");
217 //
218 //         // Start the database!
219 //         hsqlServer.start();
220          
221                  connectToDb();
222         }
223         
224         void stop(){
225                 if (hsqlServer != null) {
226             hsqlServer.stop();
227         }
228         }
229         
230         private String getDatabaseSql(){
231                 
232                 BundleContext bundleContext = FrameworkUtil.getBundle( this.getClass()).getBundleContext();
233
234                 URL urlfile = bundleContext.getBundle().getResource("/database.sql"); 
235                 
236                 InputStream input;
237                 
238                 //TODO: LC FIX
239                 String sqlCreation = "";
240                 
241                 try{
242                         
243                         input = urlfile.openConnection().getInputStream();
244                         
245                         BufferedReader br = new BufferedReader(new InputStreamReader(input));
246                         String line;
247                         
248                         while ((line = br.readLine()) != null) {
249                                 sqlCreation = sqlCreation + line;
250                         }
251                         
252                 } catch (IOException e) {
253                         // TODO Auto-generated catch block
254                         e.printStackTrace();
255                 }
256                 
257                 return sqlCreation;
258                 
259         }
260         private void connectToDb(){
261           
262                 String sqlCreation = getDatabaseSql(); 
263                 Properties prop = getStartupConfig(dbconfig_filename);
264                 
265                 // connect to db
266                 database = new DnsGuardPersistence(prop.getProperty("dbserver"),
267                                 Integer.valueOf(prop.getProperty("dbport")),
268                                 prop.getProperty("dbname"), prop.getProperty("dbuser"),
269                                 prop.getProperty("dbpasswd"), Integer.valueOf(prop
270                                                 .getProperty("internal_buffer_init_size")),
271                                 Integer.valueOf(prop.getProperty("internal_buffer_max_size")), sqlCreation);
272                  
273                 
274                 boolean dbconx = database.Connect();
275
276                 log.info("Database connected: {} ", dbconx);
277         }
278
279         /*
280          * This method read from a file shipped with the bundle... 
281          * This method is deprecated, and still in code to have a reference how to read resources
282          */
283 //      public Properties getStartupConfig(String filename) {
284 //
285 //              // http://www.mkyong.com/java/java-properties-file-examples/
286 //
287 //              Properties prop = new Properties();
288 //              InputStream input = null;
289 //
290 //              try {
291 //
292 //                      BundleContext bundleContext = FrameworkUtil.getBundle(
293 //                                      this.getClass()).getBundleContext();
294 //                       
295 //                      URL urlfile = bundleContext.getBundle().getResource(
296 //                                      "/database.config");
297 //
298 //                      // input = new FileInputStream(
299 //                      // bundleContext.getBundle().getResource("database.config").to );
300 //
301 //                      input = urlfile.openConnection().getInputStream();
302 //
303 //                      // load a properties file
304 //                      prop.load(input);
305 //
306 //                      input.close();
307 //                       
308 //                      Enumeration<?> e = prop.propertyNames();
309 //                      while (e.hasMoreElements()) {
310 //                              String key = (String) e.nextElement();
311 //                              String value = prop.getProperty(key);
312 //                              log.info(key + ": " + value);
313 //                      }
314 //                      
315 //                              //LC: This section is for using a file in the local bundle storage (seems only available at running time)
316 //                              File foutput = bundleContext.getDataFile("dns-guard-save.txt");
317 //                              OutputStream  fostream = new FileOutputStream(foutput);
318 //                              
319 //                              Properties newprop = new Properties();
320 //                              newprop.put("newval", "luis");
321 //                              
322 //                              newprop.store(fostream, "");
323 //                              
324 //                              fostream.close();
325 //                      
326 //                      return prop;
327 //
328 //              } catch (IOException ex) {
329 //                      ex.printStackTrace();
330 //              } finally {
331 //                      if (input != null) {
332 //                              try {
333 //                                      input.close();
334 //                              } catch (IOException e) {
335 //                                      e.printStackTrace();
336 //                              }
337 //                      }
338 //              }
339 //
340 //              return null;
341 //
342 //      }
343
344         public Properties getStartupConfig(String filename) {
345
346                 // http://www.mkyong.com/java/java-properties-file-examples/
347
348                 Properties prop = new Properties();
349                 InputStream input = null;
350
351                 try {
352
353                         BundleContext bundleContext = FrameworkUtil.getBundle(
354                                         this.getClass()).getBundleContext();
355                          
356                         File configfile = bundleContext.getDataFile( dbconfig_filename );
357                          
358                         input = new FileInputStream(configfile);
359
360                         // load a properties file
361                         prop.load(input);
362
363                         input.close();
364                         
365                         Enumeration<?> e = prop.propertyNames();
366                         while (e.hasMoreElements()) {
367                                 String key = (String) e.nextElement();
368                                 String value = prop.getProperty(key);
369                                 log.info(key + ": " + value);
370                         }
371                         
372                         return prop;
373
374                 } catch (IOException ex) {
375                         ex.printStackTrace();
376                 } finally {
377                         if (input != null) {
378                                 try {
379                                         input.close();
380                                 } catch (IOException e) {
381                                         e.printStackTrace();
382                                 }
383                         }
384                 }
385
386                 return prop;
387
388         }
389         
390         @Override
391         public String echo(String in) {
392                 return in + " from class";
393         }
394
395         @Override
396         public List<String> lazyresolv(String appIp) {
397
398                 // select distinct request FROM dnsspy.bulkreply where data =
399                 // '61.211.161.1'
400                 if (database.Connect()) {
401                         return database.lazyresolv(appIp);
402                 }
403
404                 return new ArrayList<String>();
405         }
406
407         @Override
408         public List<String> appsbyip(String sourceIp) {
409
410                 // select distinct request FROM dnsspy.bulkreply where data =
411                 // '61.211.161.1'
412                 if (database.Connect()) {
413                         return database.appsbyip(sourceIp);
414                 }
415
416                 return new ArrayList<String>();
417         }
418
419         @Override
420         public void updateNode(Node node, UpdateType type, Set<Property> props) {
421                 // log.info("updateNode, NodeType {} Update {} Prop {}",
422                 // node.getID().toString(), type.toString(), props.toString());
423
424                 if (type == UpdateType.ADDED) {
425
426                         for (Iterator<Property> it = props.iterator(); it.hasNext();) {
427
428                                 Property pval = it.next();
429
430                                 // log.info("updateNode {} - {}", pval.getName().toString(),
431                                 // pval.getStringValue());
432
433                                 if (pval.getName() == "macAddress") {
434
435                                         log.info("sw is up");
436
437                                         // forward dns traffic to controller: TP_DST, (short)53
438                                         Match match = new Match();
439                                         match.setField(new MatchField(MatchType.DL_TYPE,
440                                                         EtherTypes.IPv4.shortValue()));
441                                         match.setField(new MatchField(MatchType.NW_PROTO, (byte) 17));
442                                         match.setField(new MatchField(MatchType.TP_DST, (short) 53));
443
444                                         List<Action> actions = new ArrayList<Action>();
445                                         actions.add(new Controller());
446
447                                         Flow flow = new Flow(match, actions);
448                                         flow.setIdleTimeout((short) 0);
449                                         flow.setHardTimeout((short) 0);
450                                         flow.setPriority((short) 32769);
451
452                                         // Modify the flow on the network node
453                                         Status status = programmer.addFlow(node, flow);
454
455                                         if (!status.isSuccess()) {
456                                                 log.warn(
457                                                                 "SDN Plugin failed to program the flow: {}. The failure is: {}",
458                                                                 flow, status.getDescription());
459                                         }
460
461                                         // forward dns traffic to controller: TP_SRC, (short)53
462                                         // match = new Match();
463                                         // match.setField( new MatchField(MatchType.DL_TYPE,
464                                         // EtherTypes.IPv4.shortValue()));
465                                         // match.setField( new MatchField(MatchType.NW_PROTO,
466                                         // (byte)17) );
467                                         // match.setField( new MatchField(MatchType.TP_SRC,
468                                         // (short)53) );
469                                         //
470                                         // actions = new ArrayList<Action>();
471                                         // actions.add(new Controller());
472                                         //
473                                         // flow = new Flow(match, actions);
474                                         // flow.setIdleTimeout((short) 0);
475                                         // flow.setHardTimeout((short) 0);
476                                         // flow.setPriority( (short) 32769);
477                                         //
478                                         // // Modify the flow on the network node
479                                         // status = programmer.addFlow(node, flow);
480                                         //
481                                         // if (!status.isSuccess()) {
482                                         // log.warn("SDN Plugin failed to program the flow: {}. The failure is: {}",
483                                         // flow, status.getDescription());
484                                         // }
485
486                                         // if(this.swManager != null){
487                                         // Set<NodeConnector> nconx =
488                                         // this.swManager.getNodeConnectors(node);
489                                         // log.info("got nconx");
490                                         // }
491                                 }
492
493                         }
494
495                 }
496
497         }
498
499         @Override
500         public void updateNodeConnector(NodeConnector nodeConnector,
501                         UpdateType type, Set<Property> props) {
502
503         }
504
505         @Override
506         public void hostListener(HostNodeConnector host) {
507                 log.info("new host {}", host.getNetworkAddressAsString());
508         }
509
510         @Override
511         public List<Violator> getViolators() {
512
513                 // select distinct request FROM dnsspy.bulkreply where data =
514                 // '61.211.161.1'
515                 if (database.Connect()) {
516                         return database.getViolators();
517                 }
518
519                 return new ArrayList<>();
520         }
521
522         @Override
523         public String setLocalDnsServer(String local_dns) {
524
525                 // select distinct request FROM dnsspy.bulkreply where data =
526                 // '61.211.161.1'
527                 if (database.Connect()) {
528                         return database.setLocalDnsServer(local_dns);
529                 }
530
531                 return new String("Error updating data");
532         }
533
534         @Override
535         public String getLocalDnsServer() {
536
537                 // select distinct request FROM dnsspy.bulkreply where data =
538                 // '61.211.161.1'
539                 if (database.Connect()) {
540                         return database.getLocalDnsServer();
541                 }
542
543                 return new String("Error getting data");
544         }
545
546         @Override
547         public List<DnsUsage> getExternalDnsUsage(int top) {
548                 // select distinct request FROM dnsspy.bulkreply where data =
549                 // '61.211.161.1'
550                 if (database.Connect()) {
551
552                         return database.getExternalDnsUsage(top);
553                 }
554
555                 return new ArrayList<>();
556         }
557
558         @Override
559         public List<DnsRecordReply>getDatabaseDnsRecords(int limit, int offset) {
560                 // TODO Auto-generated method stub
561                 if (database.Connect()) {
562
563                         return database.getDatabaseDnsRecords(limit, offset);
564                 }
565
566                 return new ArrayList<>();
567         }
568         
569         @Override
570         public Boolean isConnected(){
571                 
572                 if(database != null){
573                         return database.isConnected();
574                 }
575                 
576                 return false;
577         }
578         
579         @Override
580         public void savePropsAndConnect(Properties props){
581                 
582                 BundleContext bundleContext = FrameworkUtil.getBundle(
583                                 this.getClass()).getBundleContext();
584                 
585                 File foutput = bundleContext.getDataFile( dbconfig_filename );
586                 OutputStream fostream;
587                 
588                 try {
589                         fostream = new FileOutputStream(foutput);
590                         
591                          
592                         props.store(fostream, "");
593                         fostream.close();
594                         
595                 } catch ( IOException e) {
596                         // TODO Auto-generated catch block
597                         e.printStackTrace();
598                 }
599  
600                 connectToDb();
601                 
602         }
603         
604 }