Browse Source

Initial import

master
Chris Smith 14 years ago
commit
738ff22bda
45 changed files with 4187 additions and 0 deletions
  1. 3
    0
      .gitignore
  2. 74
    0
      build.xml
  3. 805
    0
      nbproject/build-impl.xml
  4. 8
    0
      nbproject/genfiles.properties
  5. 60
    0
      nbproject/project.properties
  6. 16
    0
      nbproject/project.xml
  7. 26
    0
      src/com/md87/nat/CommsAgent.java
  8. 217
    0
      src/com/md87/nat/NatTraverser.java
  9. 36
    0
      src/com/md87/nat/NatType.java
  10. 105
    0
      src/com/md87/nat/test/SimpleDemo.java
  11. 81
    0
      src/de/javawi/jstun/attribute/ChangeRequest.java
  12. 30
    0
      src/de/javawi/jstun/attribute/ChangedAddress.java
  13. 42
    0
      src/de/javawi/jstun/attribute/Dummy.java
  14. 104
    0
      src/de/javawi/jstun/attribute/ErrorCode.java
  15. 29
    0
      src/de/javawi/jstun/attribute/MappedAddress.java
  16. 107
    0
      src/de/javawi/jstun/attribute/MappedResponseChangedSourceAddressReflectedFrom.java
  17. 115
    0
      src/de/javawi/jstun/attribute/MessageAttribute.java
  18. 20
    0
      src/de/javawi/jstun/attribute/MessageAttributeException.java
  19. 28
    0
      src/de/javawi/jstun/attribute/MessageAttributeInterface.java
  20. 20
    0
      src/de/javawi/jstun/attribute/MessageAttributeParsingException.java
  21. 27
    0
      src/de/javawi/jstun/attribute/MessageIntegrity.java
  22. 64
    0
      src/de/javawi/jstun/attribute/Password.java
  23. 31
    0
      src/de/javawi/jstun/attribute/ReflectedFrom.java
  24. 29
    0
      src/de/javawi/jstun/attribute/ResponseAddress.java
  25. 29
    0
      src/de/javawi/jstun/attribute/SourceAddress.java
  26. 82
    0
      src/de/javawi/jstun/attribute/UnknownAttribute.java
  27. 18
    0
      src/de/javawi/jstun/attribute/UnknownMessageAttributeException.java
  28. 64
    0
      src/de/javawi/jstun/attribute/Username.java
  29. 180
    0
      src/de/javawi/jstun/header/MessageHeader.java
  30. 20
    0
      src/de/javawi/jstun/header/MessageHeaderException.java
  31. 22
    0
      src/de/javawi/jstun/header/MessageHeaderInterface.java
  32. 20
    0
      src/de/javawi/jstun/header/MessageHeaderParsingException.java
  33. 192
    0
      src/de/javawi/jstun/test/BindingLifetimeTest.java
  34. 153
    0
      src/de/javawi/jstun/test/DiscoveryInfo.java
  35. 347
    0
      src/de/javawi/jstun/test/DiscoveryTest.java
  36. 73
    0
      src/de/javawi/jstun/test/demo/.#DiscoveryTestDemo.java.1.3
  37. 73
    0
      src/de/javawi/jstun/test/demo/.#DiscoveryTestDemo.java.1.7
  38. 47
    0
      src/de/javawi/jstun/test/demo/BindingLifetimeTestDemo.java
  39. 75
    0
      src/de/javawi/jstun/test/demo/DiscoveryTestDemo.java
  40. 263
    0
      src/de/javawi/jstun/test/demo/StunServer.java
  41. 115
    0
      src/de/javawi/jstun/test/demo/ice/Candidate.java
  42. 146
    0
      src/de/javawi/jstun/test/demo/ice/ICENegociator.java
  43. 102
    0
      src/de/javawi/jstun/util/Address.java
  44. 68
    0
      src/de/javawi/jstun/util/Utility.java
  45. 21
    0
      src/de/javawi/jstun/util/UtilityException.java

+ 3
- 0
.gitignore View File

@@ -0,0 +1,3 @@
1
+/nbproject/private/
2
+/build
3
+/dist

+ 74
- 0
build.xml View File

@@ -0,0 +1,74 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<!-- You may freely edit this file. See commented blocks below for -->
3
+<!-- some examples of how to customize the build. -->
4
+<!-- (If you delete it and reopen the project it will be recreated.) -->
5
+<!-- By default, only the Clean and Build commands use this build script. -->
6
+<!-- Commands such as Run, Debug, and Test only use this build script if -->
7
+<!-- the Compile on Save feature is turned off for the project. -->
8
+<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
9
+<!-- in the project's Project Properties dialog box.-->
10
+<project name="NatTraversal" default="default" basedir=".">
11
+    <description>Builds, tests, and runs the project NatTraversal.</description>
12
+    <import file="nbproject/build-impl.xml"/>
13
+    <!--
14
+
15
+    There exist several targets which are by default empty and which can be 
16
+    used for execution of your tasks. These targets are usually executed 
17
+    before and after some main targets. They are: 
18
+
19
+      -pre-init:                 called before initialization of project properties
20
+      -post-init:                called after initialization of project properties
21
+      -pre-compile:              called before javac compilation
22
+      -post-compile:             called after javac compilation
23
+      -pre-compile-single:       called before javac compilation of single file
24
+      -post-compile-single:      called after javac compilation of single file
25
+      -pre-compile-test:         called before javac compilation of JUnit tests
26
+      -post-compile-test:        called after javac compilation of JUnit tests
27
+      -pre-compile-test-single:  called before javac compilation of single JUnit test
28
+      -post-compile-test-single: called after javac compilation of single JUunit test
29
+      -pre-jar:                  called before JAR building
30
+      -post-jar:                 called after JAR building
31
+      -post-clean:               called after cleaning build products
32
+
33
+    (Targets beginning with '-' are not intended to be called on their own.)
34
+
35
+    Example of inserting an obfuscator after compilation could look like this:
36
+
37
+        <target name="-post-compile">
38
+            <obfuscate>
39
+                <fileset dir="${build.classes.dir}"/>
40
+            </obfuscate>
41
+        </target>
42
+
43
+    For list of available properties check the imported 
44
+    nbproject/build-impl.xml file. 
45
+
46
+
47
+    Another way to customize the build is by overriding existing main targets.
48
+    The targets of interest are: 
49
+
50
+      -init-macrodef-javac:     defines macro for javac compilation
51
+      -init-macrodef-junit:     defines macro for junit execution
52
+      -init-macrodef-debug:     defines macro for class debugging
53
+      -init-macrodef-java:      defines macro for class execution
54
+      -do-jar-with-manifest:    JAR building (if you are using a manifest)
55
+      -do-jar-without-manifest: JAR building (if you are not using a manifest)
56
+      run:                      execution of project 
57
+      -javadoc-build:           Javadoc generation
58
+      test-report:              JUnit report generation
59
+
60
+    An example of overriding the target for project execution could look like this:
61
+
62
+        <target name="run" depends="NatTraversal-impl.jar">
63
+            <exec dir="bin" executable="launcher.exe">
64
+                <arg file="${dist.jar}"/>
65
+            </exec>
66
+        </target>
67
+
68
+    Notice that the overridden target depends on the jar target and not only on 
69
+    the compile target as the regular run target does. Again, for a list of available 
70
+    properties which you can use, check the target you are overriding in the
71
+    nbproject/build-impl.xml file. 
72
+
73
+    -->
74
+</project>

+ 805
- 0
nbproject/build-impl.xml View File

@@ -0,0 +1,805 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<!--
3
+*** GENERATED FROM project.xml - DO NOT EDIT  ***
4
+***         EDIT ../build.xml INSTEAD         ***
5
+
6
+For the purpose of easier reading the script
7
+is divided into following sections:
8
+
9
+  - initialization
10
+  - compilation
11
+  - jar
12
+  - execution
13
+  - debugging
14
+  - javadoc
15
+  - junit compilation
16
+  - junit execution
17
+  - junit debugging
18
+  - applet
19
+  - cleanup
20
+
21
+        -->
22
+<project xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" basedir=".." default="default" name="NatTraversal-impl">
23
+    <fail message="Please build using Ant 1.7.1 or higher.">
24
+        <condition>
25
+            <not>
26
+                <antversion atleast="1.7.1"/>
27
+            </not>
28
+        </condition>
29
+    </fail>
30
+    <target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>
31
+    <!-- 
32
+                ======================
33
+                INITIALIZATION SECTION 
34
+                ======================
35
+            -->
36
+    <target name="-pre-init">
37
+        <!-- Empty placeholder for easier customization. -->
38
+        <!-- You can override this target in the ../build.xml file. -->
39
+    </target>
40
+    <target depends="-pre-init" name="-init-private">
41
+        <property file="nbproject/private/config.properties"/>
42
+        <property file="nbproject/private/configs/${config}.properties"/>
43
+        <property file="nbproject/private/private.properties"/>
44
+    </target>
45
+    <target depends="-pre-init,-init-private" name="-init-user">
46
+        <property file="${user.properties.file}"/>
47
+        <!-- The two properties below are usually overridden -->
48
+        <!-- by the active platform. Just a fallback. -->
49
+        <property name="default.javac.source" value="1.4"/>
50
+        <property name="default.javac.target" value="1.4"/>
51
+    </target>
52
+    <target depends="-pre-init,-init-private,-init-user" name="-init-project">
53
+        <property file="nbproject/configs/${config}.properties"/>
54
+        <property file="nbproject/project.properties"/>
55
+    </target>
56
+    <target depends="-pre-init,-init-private,-init-user,-init-project,-init-macrodef-property" name="-do-init">
57
+        <available file="${manifest.file}" property="manifest.available"/>
58
+        <condition property="main.class.available">
59
+            <and>
60
+                <isset property="main.class"/>
61
+                <not>
62
+                    <equals arg1="${main.class}" arg2="" trim="true"/>
63
+                </not>
64
+            </and>
65
+        </condition>
66
+        <condition property="manifest.available+main.class">
67
+            <and>
68
+                <isset property="manifest.available"/>
69
+                <isset property="main.class.available"/>
70
+            </and>
71
+        </condition>
72
+        <condition property="do.mkdist">
73
+            <and>
74
+                <isset property="libs.CopyLibs.classpath"/>
75
+                <not>
76
+                    <istrue value="${mkdist.disabled}"/>
77
+                </not>
78
+            </and>
79
+        </condition>
80
+        <condition property="manifest.available+main.class+mkdist.available">
81
+            <and>
82
+                <istrue value="${manifest.available+main.class}"/>
83
+                <isset property="do.mkdist"/>
84
+            </and>
85
+        </condition>
86
+        <condition property="manifest.available+mkdist.available">
87
+            <and>
88
+                <istrue value="${manifest.available}"/>
89
+                <isset property="do.mkdist"/>
90
+            </and>
91
+        </condition>
92
+        <condition property="manifest.available-mkdist.available">
93
+            <or>
94
+                <istrue value="${manifest.available}"/>
95
+                <isset property="do.mkdist"/>
96
+            </or>
97
+        </condition>
98
+        <condition property="manifest.available+main.class-mkdist.available">
99
+            <or>
100
+                <istrue value="${manifest.available+main.class}"/>
101
+                <isset property="do.mkdist"/>
102
+            </or>
103
+        </condition>
104
+        <condition property="have.tests">
105
+            <or>
106
+                <available file="${test.src.dir}"/>
107
+            </or>
108
+        </condition>
109
+        <condition property="have.sources">
110
+            <or>
111
+                <available file="${src.dir}"/>
112
+            </or>
113
+        </condition>
114
+        <condition property="netbeans.home+have.tests">
115
+            <and>
116
+                <isset property="netbeans.home"/>
117
+                <isset property="have.tests"/>
118
+            </and>
119
+        </condition>
120
+        <condition property="no.javadoc.preview">
121
+            <and>
122
+                <isset property="javadoc.preview"/>
123
+                <isfalse value="${javadoc.preview}"/>
124
+            </and>
125
+        </condition>
126
+        <property name="run.jvmargs" value=""/>
127
+        <property name="javac.compilerargs" value=""/>
128
+        <property name="work.dir" value="${basedir}"/>
129
+        <condition property="no.deps">
130
+            <and>
131
+                <istrue value="${no.dependencies}"/>
132
+            </and>
133
+        </condition>
134
+        <property name="javac.debug" value="true"/>
135
+        <property name="javadoc.preview" value="true"/>
136
+        <property name="application.args" value=""/>
137
+        <property name="source.encoding" value="${file.encoding}"/>
138
+        <property name="runtime.encoding" value="${source.encoding}"/>
139
+        <condition property="javadoc.encoding.used" value="${javadoc.encoding}">
140
+            <and>
141
+                <isset property="javadoc.encoding"/>
142
+                <not>
143
+                    <equals arg1="${javadoc.encoding}" arg2=""/>
144
+                </not>
145
+            </and>
146
+        </condition>
147
+        <property name="javadoc.encoding.used" value="${source.encoding}"/>
148
+        <property name="includes" value="**"/>
149
+        <property name="excludes" value=""/>
150
+        <property name="do.depend" value="false"/>
151
+        <condition property="do.depend.true">
152
+            <istrue value="${do.depend}"/>
153
+        </condition>
154
+        <path id="endorsed.classpath.path" path="${endorsed.classpath}"/>
155
+        <condition else="" property="endorsed.classpath.cmd.line.arg" value="-Xbootclasspath/p:'${toString:endorsed.classpath.path}'">
156
+            <length length="0" string="${endorsed.classpath}" when="greater"/>
157
+        </condition>
158
+        <property name="javac.fork" value="false"/>
159
+    </target>
160
+    <target name="-post-init">
161
+        <!-- Empty placeholder for easier customization. -->
162
+        <!-- You can override this target in the ../build.xml file. -->
163
+    </target>
164
+    <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init" name="-init-check">
165
+        <fail unless="src.dir">Must set src.dir</fail>
166
+        <fail unless="test.src.dir">Must set test.src.dir</fail>
167
+        <fail unless="build.dir">Must set build.dir</fail>
168
+        <fail unless="dist.dir">Must set dist.dir</fail>
169
+        <fail unless="build.classes.dir">Must set build.classes.dir</fail>
170
+        <fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail>
171
+        <fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail>
172
+        <fail unless="build.test.results.dir">Must set build.test.results.dir</fail>
173
+        <fail unless="build.classes.excludes">Must set build.classes.excludes</fail>
174
+        <fail unless="dist.jar">Must set dist.jar</fail>
175
+    </target>
176
+    <target name="-init-macrodef-property">
177
+        <macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1">
178
+            <attribute name="name"/>
179
+            <attribute name="value"/>
180
+            <sequential>
181
+                <property name="@{name}" value="${@{value}}"/>
182
+            </sequential>
183
+        </macrodef>
184
+    </target>
185
+    <target name="-init-macrodef-javac">
186
+        <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
187
+            <attribute default="${src.dir}" name="srcdir"/>
188
+            <attribute default="${build.classes.dir}" name="destdir"/>
189
+            <attribute default="${javac.classpath}" name="classpath"/>
190
+            <attribute default="${includes}" name="includes"/>
191
+            <attribute default="${excludes}" name="excludes"/>
192
+            <attribute default="${javac.debug}" name="debug"/>
193
+            <attribute default="${empty.dir}" name="sourcepath"/>
194
+            <attribute default="${empty.dir}" name="gensrcdir"/>
195
+            <element name="customize" optional="true"/>
196
+            <sequential>
197
+                <property location="${build.dir}/empty" name="empty.dir"/>
198
+                <mkdir dir="${empty.dir}"/>
199
+                <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" fork="${javac.fork}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}" tempdir="${java.io.tmpdir}">
200
+                    <src>
201
+                        <dirset dir="@{gensrcdir}" erroronmissingdir="false">
202
+                            <include name="*"/>
203
+                        </dirset>
204
+                    </src>
205
+                    <classpath>
206
+                        <path path="@{classpath}"/>
207
+                    </classpath>
208
+                    <compilerarg line="${endorsed.classpath.cmd.line.arg}"/>
209
+                    <compilerarg line="${javac.compilerargs}"/>
210
+                    <customize/>
211
+                </javac>
212
+            </sequential>
213
+        </macrodef>
214
+        <macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-project/3">
215
+            <attribute default="${src.dir}" name="srcdir"/>
216
+            <attribute default="${build.classes.dir}" name="destdir"/>
217
+            <attribute default="${javac.classpath}" name="classpath"/>
218
+            <sequential>
219
+                <depend cache="${build.dir}/depcache" destdir="@{destdir}" excludes="${excludes}" includes="${includes}" srcdir="@{srcdir}">
220
+                    <classpath>
221
+                        <path path="@{classpath}"/>
222
+                    </classpath>
223
+                </depend>
224
+            </sequential>
225
+        </macrodef>
226
+        <macrodef name="force-recompile" uri="http://www.netbeans.org/ns/j2se-project/3">
227
+            <attribute default="${build.classes.dir}" name="destdir"/>
228
+            <sequential>
229
+                <fail unless="javac.includes">Must set javac.includes</fail>
230
+                <pathconvert pathsep="," property="javac.includes.binary">
231
+                    <path>
232
+                        <filelist dir="@{destdir}" files="${javac.includes}"/>
233
+                    </path>
234
+                    <globmapper from="*.java" to="*.class"/>
235
+                </pathconvert>
236
+                <delete>
237
+                    <files includes="${javac.includes.binary}"/>
238
+                </delete>
239
+            </sequential>
240
+        </macrodef>
241
+    </target>
242
+    <target name="-init-macrodef-junit">
243
+        <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
244
+            <attribute default="${includes}" name="includes"/>
245
+            <attribute default="${excludes}" name="excludes"/>
246
+            <attribute default="**" name="testincludes"/>
247
+            <sequential>
248
+                <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" showoutput="true" tempdir="${build.dir}">
249
+                    <batchtest todir="${build.test.results.dir}">
250
+                        <fileset dir="${test.src.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
251
+                            <filename name="@{testincludes}"/>
252
+                        </fileset>
253
+                    </batchtest>
254
+                    <classpath>
255
+                        <path path="${run.test.classpath}"/>
256
+                    </classpath>
257
+                    <syspropertyset>
258
+                        <propertyref prefix="test-sys-prop."/>
259
+                        <mapper from="test-sys-prop.*" to="*" type="glob"/>
260
+                    </syspropertyset>
261
+                    <formatter type="brief" usefile="false"/>
262
+                    <formatter type="xml"/>
263
+                    <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
264
+                    <jvmarg line="${run.jvmargs}"/>
265
+                </junit>
266
+            </sequential>
267
+        </macrodef>
268
+    </target>
269
+    <target depends="-init-debug-args" name="-init-macrodef-nbjpda">
270
+        <macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
271
+            <attribute default="${main.class}" name="name"/>
272
+            <attribute default="${debug.classpath}" name="classpath"/>
273
+            <attribute default="" name="stopclassname"/>
274
+            <sequential>
275
+                <nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="${debug-transport}">
276
+                    <classpath>
277
+                        <path path="@{classpath}"/>
278
+                    </classpath>
279
+                </nbjpdastart>
280
+            </sequential>
281
+        </macrodef>
282
+        <macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1">
283
+            <attribute default="${build.classes.dir}" name="dir"/>
284
+            <sequential>
285
+                <nbjpdareload>
286
+                    <fileset dir="@{dir}" includes="${fix.classes}">
287
+                        <include name="${fix.includes}*.class"/>
288
+                    </fileset>
289
+                </nbjpdareload>
290
+            </sequential>
291
+        </macrodef>
292
+    </target>
293
+    <target name="-init-debug-args">
294
+        <property name="version-output" value="java version &quot;${ant.java.version}"/>
295
+        <condition property="have-jdk-older-than-1.4">
296
+            <or>
297
+                <contains string="${version-output}" substring="java version &quot;1.0"/>
298
+                <contains string="${version-output}" substring="java version &quot;1.1"/>
299
+                <contains string="${version-output}" substring="java version &quot;1.2"/>
300
+                <contains string="${version-output}" substring="java version &quot;1.3"/>
301
+            </or>
302
+        </condition>
303
+        <condition else="-Xdebug" property="debug-args-line" value="-Xdebug -Xnoagent -Djava.compiler=none">
304
+            <istrue value="${have-jdk-older-than-1.4}"/>
305
+        </condition>
306
+        <condition else="dt_socket" property="debug-transport-by-os" value="dt_shmem">
307
+            <os family="windows"/>
308
+        </condition>
309
+        <condition else="${debug-transport-by-os}" property="debug-transport" value="${debug.transport}">
310
+            <isset property="debug.transport"/>
311
+        </condition>
312
+    </target>
313
+    <target depends="-init-debug-args" name="-init-macrodef-debug">
314
+        <macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
315
+            <attribute default="${main.class}" name="classname"/>
316
+            <attribute default="${debug.classpath}" name="classpath"/>
317
+            <element name="customize" optional="true"/>
318
+            <sequential>
319
+                <java classname="@{classname}" dir="${work.dir}" fork="true">
320
+                    <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
321
+                    <jvmarg line="${debug-args-line}"/>
322
+                    <jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
323
+                    <jvmarg value="-Dfile.encoding=${runtime.encoding}"/>
324
+                    <redirector errorencoding="${runtime.encoding}" inputencoding="${runtime.encoding}" outputencoding="${runtime.encoding}"/>
325
+                    <jvmarg line="${run.jvmargs}"/>
326
+                    <classpath>
327
+                        <path path="@{classpath}"/>
328
+                    </classpath>
329
+                    <syspropertyset>
330
+                        <propertyref prefix="run-sys-prop."/>
331
+                        <mapper from="run-sys-prop.*" to="*" type="glob"/>
332
+                    </syspropertyset>
333
+                    <customize/>
334
+                </java>
335
+            </sequential>
336
+        </macrodef>
337
+    </target>
338
+    <target name="-init-macrodef-java">
339
+        <macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
340
+            <attribute default="${main.class}" name="classname"/>
341
+            <attribute default="${run.classpath}" name="classpath"/>
342
+            <element name="customize" optional="true"/>
343
+            <sequential>
344
+                <java classname="@{classname}" dir="${work.dir}" fork="true">
345
+                    <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
346
+                    <jvmarg value="-Dfile.encoding=${runtime.encoding}"/>
347
+                    <redirector errorencoding="${runtime.encoding}" inputencoding="${runtime.encoding}" outputencoding="${runtime.encoding}"/>
348
+                    <jvmarg line="${run.jvmargs}"/>
349
+                    <classpath>
350
+                        <path path="@{classpath}"/>
351
+                    </classpath>
352
+                    <syspropertyset>
353
+                        <propertyref prefix="run-sys-prop."/>
354
+                        <mapper from="run-sys-prop.*" to="*" type="glob"/>
355
+                    </syspropertyset>
356
+                    <customize/>
357
+                </java>
358
+            </sequential>
359
+        </macrodef>
360
+    </target>
361
+    <target name="-init-presetdef-jar">
362
+        <presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1">
363
+            <jar compress="${jar.compress}" jarfile="${dist.jar}">
364
+                <j2seproject1:fileset dir="${build.classes.dir}"/>
365
+            </jar>
366
+        </presetdef>
367
+    </target>
368
+    <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-junit,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar" name="init"/>
369
+    <!--
370
+                ===================
371
+                COMPILATION SECTION
372
+                ===================
373
+            -->
374
+    <target name="-deps-jar-init" unless="built-jar.properties">
375
+        <property location="${build.dir}/built-jar.properties" name="built-jar.properties"/>
376
+        <delete file="${built-jar.properties}" quiet="true"/>
377
+    </target>
378
+    <target if="already.built.jar.${basedir}" name="-warn-already-built-jar">
379
+        <echo level="warn" message="Cycle detected: NatTraversal was already built"/>
380
+    </target>
381
+    <target depends="init,-deps-jar-init" name="deps-jar" unless="no.deps">
382
+        <mkdir dir="${build.dir}"/>
383
+        <touch file="${built-jar.properties}" verbose="false"/>
384
+        <property file="${built-jar.properties}" prefix="already.built.jar."/>
385
+        <antcall target="-warn-already-built-jar"/>
386
+        <propertyfile file="${built-jar.properties}">
387
+            <entry key="${basedir}" value=""/>
388
+        </propertyfile>
389
+    </target>
390
+    <target depends="init,-check-automatic-build,-clean-after-automatic-build" name="-verify-automatic-build"/>
391
+    <target depends="init" name="-check-automatic-build">
392
+        <available file="${build.classes.dir}/.netbeans_automatic_build" property="netbeans.automatic.build"/>
393
+    </target>
394
+    <target depends="init" if="netbeans.automatic.build" name="-clean-after-automatic-build">
395
+        <antcall target="clean"/>
396
+    </target>
397
+    <target depends="init,deps-jar" name="-pre-pre-compile">
398
+        <mkdir dir="${build.classes.dir}"/>
399
+    </target>
400
+    <target name="-pre-compile">
401
+        <!-- Empty placeholder for easier customization. -->
402
+        <!-- You can override this target in the ../build.xml file. -->
403
+    </target>
404
+    <target if="do.depend.true" name="-compile-depend">
405
+        <pathconvert property="build.generated.subdirs">
406
+            <dirset dir="${build.generated.sources.dir}" erroronmissingdir="false">
407
+                <include name="*"/>
408
+            </dirset>
409
+        </pathconvert>
410
+        <j2seproject3:depend srcdir="${src.dir}:${build.generated.subdirs}"/>
411
+    </target>
412
+    <target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-compile-depend" if="have.sources" name="-do-compile">
413
+        <j2seproject3:javac gensrcdir="${build.generated.sources.dir}"/>
414
+        <copy todir="${build.classes.dir}">
415
+            <fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
416
+        </copy>
417
+    </target>
418
+    <target name="-post-compile">
419
+        <!-- Empty placeholder for easier customization. -->
420
+        <!-- You can override this target in the ../build.xml file. -->
421
+    </target>
422
+    <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
423
+    <target name="-pre-compile-single">
424
+        <!-- Empty placeholder for easier customization. -->
425
+        <!-- You can override this target in the ../build.xml file. -->
426
+    </target>
427
+    <target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single">
428
+        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
429
+        <j2seproject3:force-recompile/>
430
+        <j2seproject3:javac excludes="" gensrcdir="${build.generated.sources.dir}" includes="${javac.includes}" sourcepath="${src.dir}"/>
431
+    </target>
432
+    <target name="-post-compile-single">
433
+        <!-- Empty placeholder for easier customization. -->
434
+        <!-- You can override this target in the ../build.xml file. -->
435
+    </target>
436
+    <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
437
+    <!--
438
+                ====================
439
+                JAR BUILDING SECTION
440
+                ====================
441
+            -->
442
+    <target depends="init" name="-pre-pre-jar">
443
+        <dirname file="${dist.jar}" property="dist.jar.dir"/>
444
+        <mkdir dir="${dist.jar.dir}"/>
445
+    </target>
446
+    <target name="-pre-jar">
447
+        <!-- Empty placeholder for easier customization. -->
448
+        <!-- You can override this target in the ../build.xml file. -->
449
+    </target>
450
+    <target depends="init,compile,-pre-pre-jar,-pre-jar" name="-do-jar-without-manifest" unless="manifest.available-mkdist.available">
451
+        <j2seproject1:jar/>
452
+    </target>
453
+    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available" name="-do-jar-with-manifest" unless="manifest.available+main.class-mkdist.available">
454
+        <j2seproject1:jar manifest="${manifest.file}"/>
455
+    </target>
456
+    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class" name="-do-jar-with-mainclass" unless="manifest.available+main.class+mkdist.available">
457
+        <j2seproject1:jar manifest="${manifest.file}">
458
+            <j2seproject1:manifest>
459
+                <j2seproject1:attribute name="Main-Class" value="${main.class}"/>
460
+            </j2seproject1:manifest>
461
+        </j2seproject1:jar>
462
+        <echo>To run this application from the command line without Ant, try:</echo>
463
+        <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
464
+        <property location="${dist.jar}" name="dist.jar.resolved"/>
465
+        <pathconvert property="run.classpath.with.dist.jar">
466
+            <path path="${run.classpath}"/>
467
+            <map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
468
+        </pathconvert>
469
+        <echo>java -cp "${run.classpath.with.dist.jar}" ${main.class}</echo>
470
+    </target>
471
+    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class+mkdist.available" name="-do-jar-with-libraries">
472
+        <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
473
+        <pathconvert property="run.classpath.without.build.classes.dir">
474
+            <path path="${run.classpath}"/>
475
+            <map from="${build.classes.dir.resolved}" to=""/>
476
+        </pathconvert>
477
+        <pathconvert pathsep=" " property="jar.classpath">
478
+            <path path="${run.classpath.without.build.classes.dir}"/>
479
+            <chainedmapper>
480
+                <flattenmapper/>
481
+                <globmapper from="*" to="lib/*"/>
482
+            </chainedmapper>
483
+        </pathconvert>
484
+        <taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
485
+        <copylibs compress="${jar.compress}" jarfile="${dist.jar}" manifest="${manifest.file}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
486
+            <fileset dir="${build.classes.dir}"/>
487
+            <manifest>
488
+                <attribute name="Main-Class" value="${main.class}"/>
489
+                <attribute name="Class-Path" value="${jar.classpath}"/>
490
+            </manifest>
491
+        </copylibs>
492
+        <echo>To run this application from the command line without Ant, try:</echo>
493
+        <property location="${dist.jar}" name="dist.jar.resolved"/>
494
+        <echo>java -jar "${dist.jar.resolved}"</echo>
495
+    </target>
496
+    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+mkdist.available" name="-do-jar-with-libraries-without-mainclass" unless="main.class.available">
497
+        <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
498
+        <pathconvert property="run.classpath.without.build.classes.dir">
499
+            <path path="${run.classpath}"/>
500
+            <map from="${build.classes.dir.resolved}" to=""/>
501
+        </pathconvert>
502
+        <pathconvert pathsep=" " property="jar.classpath">
503
+            <path path="${run.classpath.without.build.classes.dir}"/>
504
+            <chainedmapper>
505
+                <flattenmapper/>
506
+                <globmapper from="*" to="lib/*"/>
507
+            </chainedmapper>
508
+        </pathconvert>
509
+        <taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
510
+        <copylibs compress="${jar.compress}" jarfile="${dist.jar}" manifest="${manifest.file}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
511
+            <fileset dir="${build.classes.dir}"/>
512
+            <manifest>
513
+                <attribute name="Class-Path" value="${jar.classpath}"/>
514
+            </manifest>
515
+        </copylibs>
516
+    </target>
517
+    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="do.mkdist" name="-do-jar-with-libraries-without-manifest" unless="manifest.available">
518
+        <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
519
+        <pathconvert property="run.classpath.without.build.classes.dir">
520
+            <path path="${run.classpath}"/>
521
+            <map from="${build.classes.dir.resolved}" to=""/>
522
+        </pathconvert>
523
+        <pathconvert pathsep=" " property="jar.classpath">
524
+            <path path="${run.classpath.without.build.classes.dir}"/>
525
+            <chainedmapper>
526
+                <flattenmapper/>
527
+                <globmapper from="*" to="lib/*"/>
528
+            </chainedmapper>
529
+        </pathconvert>
530
+        <taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
531
+        <copylibs compress="${jar.compress}" jarfile="${dist.jar}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
532
+            <fileset dir="${build.classes.dir}"/>
533
+            <manifest>
534
+                <attribute name="Class-Path" value="${jar.classpath}"/>
535
+            </manifest>
536
+        </copylibs>
537
+    </target>
538
+    <target name="-post-jar">
539
+        <!-- Empty placeholder for easier customization. -->
540
+        <!-- You can override this target in the ../build.xml file. -->
541
+    </target>
542
+    <target depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-do-jar-with-libraries-without-mainclass,-do-jar-with-libraries-without-manifest,-post-jar" description="Build JAR." name="jar"/>
543
+    <!--
544
+                =================
545
+                EXECUTION SECTION
546
+                =================
547
+            -->
548
+    <target depends="init,compile" description="Run a main class." name="run">
549
+        <j2seproject1:java>
550
+            <customize>
551
+                <arg line="${application.args}"/>
552
+            </customize>
553
+        </j2seproject1:java>
554
+    </target>
555
+    <target name="-do-not-recompile">
556
+        <property name="javac.includes.binary" value=""/>
557
+    </target>
558
+    <target depends="init,compile-single" name="run-single">
559
+        <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
560
+        <j2seproject1:java classname="${run.class}"/>
561
+    </target>
562
+    <target depends="init,compile-test-single" name="run-test-with-main">
563
+        <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
564
+        <j2seproject1:java classname="${run.class}" classpath="${run.test.classpath}"/>
565
+    </target>
566
+    <!--
567
+                =================
568
+                DEBUGGING SECTION
569
+                =================
570
+            -->
571
+    <target depends="init" if="netbeans.home" name="-debug-start-debugger">
572
+        <j2seproject1:nbjpdastart name="${debug.class}"/>
573
+    </target>
574
+    <target depends="init" if="netbeans.home" name="-debug-start-debugger-main-test">
575
+        <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${debug.class}"/>
576
+    </target>
577
+    <target depends="init,compile" name="-debug-start-debuggee">
578
+        <j2seproject3:debug>
579
+            <customize>
580
+                <arg line="${application.args}"/>
581
+            </customize>
582
+        </j2seproject3:debug>
583
+    </target>
584
+    <target depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE." if="netbeans.home" name="debug"/>
585
+    <target depends="init" if="netbeans.home" name="-debug-start-debugger-stepinto">
586
+        <j2seproject1:nbjpdastart stopclassname="${main.class}"/>
587
+    </target>
588
+    <target depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee" if="netbeans.home" name="debug-stepinto"/>
589
+    <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-single">
590
+        <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
591
+        <j2seproject3:debug classname="${debug.class}"/>
592
+    </target>
593
+    <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/>
594
+    <target depends="init,compile-test-single" if="netbeans.home" name="-debug-start-debuggee-main-test">
595
+        <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
596
+        <j2seproject3:debug classname="${debug.class}" classpath="${debug.test.classpath}"/>
597
+    </target>
598
+    <target depends="init,compile-test-single,-debug-start-debugger-main-test,-debug-start-debuggee-main-test" if="netbeans.home" name="debug-test-with-main"/>
599
+    <target depends="init" name="-pre-debug-fix">
600
+        <fail unless="fix.includes">Must set fix.includes</fail>
601
+        <property name="javac.includes" value="${fix.includes}.java"/>
602
+    </target>
603
+    <target depends="init,-pre-debug-fix,compile-single" if="netbeans.home" name="-do-debug-fix">
604
+        <j2seproject1:nbjpdareload/>
605
+    </target>
606
+    <target depends="init,-pre-debug-fix,-do-debug-fix" if="netbeans.home" name="debug-fix"/>
607
+    <!--
608
+                ===============
609
+                JAVADOC SECTION
610
+                ===============
611
+            -->
612
+    <target depends="init" name="-javadoc-build">
613
+        <mkdir dir="${dist.javadoc.dir}"/>
614
+        <javadoc additionalparam="${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}">
615
+            <classpath>
616
+                <path path="${javac.classpath}"/>
617
+            </classpath>
618
+            <fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}">
619
+                <filename name="**/*.java"/>
620
+            </fileset>
621
+            <fileset dir="${build.generated.sources.dir}" erroronmissingdir="false">
622
+                <include name="**/*.java"/>
623
+            </fileset>
624
+        </javadoc>
625
+    </target>
626
+    <target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview">
627
+        <nbbrowse file="${dist.javadoc.dir}/index.html"/>
628
+    </target>
629
+    <target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/>
630
+    <!--
631
+                =========================
632
+                JUNIT COMPILATION SECTION
633
+                =========================
634
+            -->
635
+    <target depends="init,compile" if="have.tests" name="-pre-pre-compile-test">
636
+        <mkdir dir="${build.test.classes.dir}"/>
637
+    </target>
638
+    <target name="-pre-compile-test">
639
+        <!-- Empty placeholder for easier customization. -->
640
+        <!-- You can override this target in the ../build.xml file. -->
641
+    </target>
642
+    <target if="do.depend.true" name="-compile-test-depend">
643
+        <j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
644
+    </target>
645
+    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test">
646
+        <j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
647
+        <copy todir="${build.test.classes.dir}">
648
+            <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
649
+        </copy>
650
+    </target>
651
+    <target name="-post-compile-test">
652
+        <!-- Empty placeholder for easier customization. -->
653
+        <!-- You can override this target in the ../build.xml file. -->
654
+    </target>
655
+    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test" name="compile-test"/>
656
+    <target name="-pre-compile-test-single">
657
+        <!-- Empty placeholder for easier customization. -->
658
+        <!-- You can override this target in the ../build.xml file. -->
659
+    </target>
660
+    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single">
661
+        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
662
+        <j2seproject3:force-recompile destdir="${build.test.classes.dir}"/>
663
+        <j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}" sourcepath="${test.src.dir}" srcdir="${test.src.dir}"/>
664
+        <copy todir="${build.test.classes.dir}">
665
+            <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
666
+        </copy>
667
+    </target>
668
+    <target name="-post-compile-test-single">
669
+        <!-- Empty placeholder for easier customization. -->
670
+        <!-- You can override this target in the ../build.xml file. -->
671
+    </target>
672
+    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/>
673
+    <!--
674
+                =======================
675
+                JUNIT EXECUTION SECTION
676
+                =======================
677
+            -->
678
+    <target depends="init" if="have.tests" name="-pre-test-run">
679
+        <mkdir dir="${build.test.results.dir}"/>
680
+    </target>
681
+    <target depends="init,compile-test,-pre-test-run" if="have.tests" name="-do-test-run">
682
+        <j2seproject3:junit testincludes="**/*Test.java"/>
683
+    </target>
684
+    <target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
685
+        <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
686
+    </target>
687
+    <target depends="init" if="have.tests" name="test-report"/>
688
+    <target depends="init" if="netbeans.home+have.tests" name="-test-browse"/>
689
+    <target depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests." name="test"/>
690
+    <target depends="init" if="have.tests" name="-pre-test-run-single">
691
+        <mkdir dir="${build.test.results.dir}"/>
692
+    </target>
693
+    <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single">
694
+        <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
695
+        <j2seproject3:junit excludes="" includes="${test.includes}"/>
696
+    </target>
697
+    <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
698
+        <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
699
+    </target>
700
+    <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/>
701
+    <!--
702
+                =======================
703
+                JUNIT DEBUGGING SECTION
704
+                =======================
705
+            -->
706
+    <target depends="init,compile-test" if="have.tests" name="-debug-start-debuggee-test">
707
+        <fail unless="test.class">Must select one file in the IDE or set test.class</fail>
708
+        <property location="${build.test.results.dir}/TEST-${test.class}.xml" name="test.report.file"/>
709
+        <delete file="${test.report.file}"/>
710
+        <mkdir dir="${build.test.results.dir}"/>
711
+        <j2seproject3:debug classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner" classpath="${ant.home}/lib/ant.jar:${ant.home}/lib/ant-junit.jar:${debug.test.classpath}">
712
+            <customize>
713
+                <syspropertyset>
714
+                    <propertyref prefix="test-sys-prop."/>
715
+                    <mapper from="test-sys-prop.*" to="*" type="glob"/>
716
+                </syspropertyset>
717
+                <arg value="${test.class}"/>
718
+                <arg value="showoutput=true"/>
719
+                <arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.BriefJUnitResultFormatter"/>
720
+                <arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,${test.report.file}"/>
721
+            </customize>
722
+        </j2seproject3:debug>
723
+    </target>
724
+    <target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test">
725
+        <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${test.class}"/>
726
+    </target>
727
+    <target depends="init,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/>
728
+    <target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test">
729
+        <j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/>
730
+    </target>
731
+    <target depends="init,-pre-debug-fix,-do-debug-fix-test" if="netbeans.home" name="debug-fix-test"/>
732
+    <!--
733
+                =========================
734
+                APPLET EXECUTION SECTION
735
+                =========================
736
+            -->
737
+    <target depends="init,compile-single" name="run-applet">
738
+        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
739
+        <j2seproject1:java classname="sun.applet.AppletViewer">
740
+            <customize>
741
+                <arg value="${applet.url}"/>
742
+            </customize>
743
+        </j2seproject1:java>
744
+    </target>
745
+    <!--
746
+                =========================
747
+                APPLET DEBUGGING  SECTION
748
+                =========================
749
+            -->
750
+    <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-applet">
751
+        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
752
+        <j2seproject3:debug classname="sun.applet.AppletViewer">
753
+            <customize>
754
+                <arg value="${applet.url}"/>
755
+            </customize>
756
+        </j2seproject3:debug>
757
+    </target>
758
+    <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet" if="netbeans.home" name="debug-applet"/>
759
+    <!--
760
+                ===============
761
+                CLEANUP SECTION
762
+                ===============
763
+            -->
764
+    <target name="-deps-clean-init" unless="built-clean.properties">
765
+        <property location="${build.dir}/built-clean.properties" name="built-clean.properties"/>
766
+        <delete file="${built-clean.properties}" quiet="true"/>
767
+    </target>
768
+    <target if="already.built.clean.${basedir}" name="-warn-already-built-clean">
769
+        <echo level="warn" message="Cycle detected: NatTraversal was already built"/>
770
+    </target>
771
+    <target depends="init,-deps-clean-init" name="deps-clean" unless="no.deps">
772
+        <mkdir dir="${build.dir}"/>
773
+        <touch file="${built-clean.properties}" verbose="false"/>
774
+        <property file="${built-clean.properties}" prefix="already.built.clean."/>
775
+        <antcall target="-warn-already-built-clean"/>
776
+        <propertyfile file="${built-clean.properties}">
777
+            <entry key="${basedir}" value=""/>
778
+        </propertyfile>
779
+    </target>
780
+    <target depends="init" name="-do-clean">
781
+        <delete dir="${build.dir}"/>
782
+        <delete dir="${dist.dir}" followsymlinks="false" includeemptydirs="true"/>
783
+    </target>
784
+    <target name="-post-clean">
785
+        <!-- Empty placeholder for easier customization. -->
786
+        <!-- You can override this target in the ../build.xml file. -->
787
+    </target>
788
+    <target depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products." name="clean"/>
789
+    <target name="-check-call-dep">
790
+        <property file="${call.built.properties}" prefix="already.built."/>
791
+        <condition property="should.call.dep">
792
+            <not>
793
+                <isset property="already.built.${call.subproject}"/>
794
+            </not>
795
+        </condition>
796
+    </target>
797
+    <target depends="-check-call-dep" if="should.call.dep" name="-maybe-call-dep">
798
+        <ant antfile="${call.script}" inheritall="false" target="${call.target}">
799
+            <propertyset>
800
+                <propertyref prefix="transfer."/>
801
+                <mapper from="transfer.*" to="*" type="glob"/>
802
+            </propertyset>
803
+        </ant>
804
+    </target>
805
+</project>

+ 8
- 0
nbproject/genfiles.properties View File

@@ -0,0 +1,8 @@
1
+build.xml.data.CRC32=b47783a3
2
+build.xml.script.CRC32=e04acb88
3
+build.xml.stylesheet.CRC32=958a1d3e@1.32.1.45
4
+# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
5
+# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
6
+nbproject/build-impl.xml.data.CRC32=b47783a3
7
+nbproject/build-impl.xml.script.CRC32=5144d085
8
+nbproject/build-impl.xml.stylesheet.CRC32=576378a2@1.32.1.45

+ 60
- 0
nbproject/project.properties View File

@@ -0,0 +1,60 @@
1
+build.classes.dir=${build.dir}/classes
2
+build.classes.excludes=**/*.java,**/*.form
3
+# This directory is removed when the project is cleaned:
4
+build.dir=build
5
+build.generated.dir=${build.dir}/generated
6
+build.generated.sources.dir=${build.dir}/generated-sources
7
+# Only compile against the classpath explicitly listed here:
8
+build.sysclasspath=ignore
9
+build.test.classes.dir=${build.dir}/test/classes
10
+build.test.results.dir=${build.dir}/test/results
11
+debug.classpath=\
12
+    ${run.classpath}
13
+debug.test.classpath=\
14
+    ${run.test.classpath}
15
+# This directory is removed when the project is cleaned:
16
+dist.dir=dist
17
+dist.jar=${dist.dir}/NatTraversal.jar
18
+dist.javadoc.dir=${dist.dir}/javadoc
19
+excludes=
20
+includes=**
21
+jar.compress=false
22
+javac.classpath=
23
+# Space-separated list of extra javac options
24
+javac.compilerargs=
25
+javac.deprecation=false
26
+javac.source=1.5
27
+javac.target=1.5
28
+javac.test.classpath=\
29
+    ${javac.classpath}:\
30
+    ${build.classes.dir}:\
31
+    ${libs.junit.classpath}:\
32
+    ${libs.junit_4.classpath}
33
+javadoc.additionalparam=
34
+javadoc.author=false
35
+javadoc.encoding=${source.encoding}
36
+javadoc.noindex=false
37
+javadoc.nonavbar=false
38
+javadoc.notree=false
39
+javadoc.private=false
40
+javadoc.splitindex=true
41
+javadoc.use=true
42
+javadoc.version=false
43
+javadoc.windowtitle=
44
+main.class=com.md87.nat.test.SimpleDemo
45
+manifest.file=manifest.mf
46
+meta.inf.dir=${src.dir}/META-INF
47
+platform.active=default_platform
48
+run.classpath=\
49
+    ${javac.classpath}:\
50
+    ${build.classes.dir}
51
+# Space-separated list of JVM arguments used when running the project
52
+# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
53
+# or test-sys-prop.name=value to set system properties for unit tests):
54
+run.jvmargs=
55
+run.test.classpath=\
56
+    ${javac.test.classpath}:\
57
+    ${build.test.classes.dir}
58
+source.encoding=UTF-8
59
+src.dir=src
60
+test.src.dir=test

+ 16
- 0
nbproject/project.xml View File

@@ -0,0 +1,16 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<project xmlns="http://www.netbeans.org/ns/project/1">
3
+    <type>org.netbeans.modules.java.j2seproject</type>
4
+    <configuration>
5
+        <data xmlns="http://www.netbeans.org/ns/j2se-project/3">
6
+            <name>NatTraversal</name>
7
+            <minimum-ant-version>1.6.5</minimum-ant-version>
8
+            <source-roots>
9
+                <root id="src.dir"/>
10
+            </source-roots>
11
+            <test-roots>
12
+                <root id="test.src.dir"/>
13
+            </test-roots>
14
+        </data>
15
+    </configuration>
16
+</project>

+ 26
- 0
src/com/md87/nat/CommsAgent.java View File

@@ -0,0 +1,26 @@
1
+/*
2
+ * To change this template, choose Tools | Templates
3
+ * and open the template in the editor.
4
+ */
5
+
6
+package com.md87.nat;
7
+
8
+import java.net.InetAddress;
9
+
10
+/**
11
+ *
12
+ * @author chris
13
+ */
14
+public interface CommsAgent {
15
+
16
+    void sendNATType(final NatType type);
17
+
18
+    NatType readNATType();
19
+
20
+    void sendAddress(final InetAddress address);
21
+    void sendPort(final int port);
22
+
23
+    InetAddress readAddress();
24
+    int readPort();
25
+
26
+}

+ 217
- 0
src/com/md87/nat/NatTraverser.java View File

@@ -0,0 +1,217 @@
1
+/*
2
+ * To change this template, choose Tools | Templates
3
+ * and open the template in the editor.
4
+ */
5
+
6
+package com.md87.nat;
7
+
8
+import de.javawi.jstun.attribute.MappedAddress;
9
+import de.javawi.jstun.attribute.MessageAttribute;
10
+import de.javawi.jstun.attribute.MessageAttributeException;
11
+import de.javawi.jstun.attribute.MessageAttributeParsingException;
12
+import de.javawi.jstun.header.MessageHeader;
13
+import de.javawi.jstun.header.MessageHeaderParsingException;
14
+import de.javawi.jstun.test.DiscoveryInfo;
15
+import de.javawi.jstun.test.DiscoveryTest;
16
+import de.javawi.jstun.util.UtilityException;
17
+
18
+import java.io.IOException;
19
+import java.net.DatagramPacket;
20
+import java.net.DatagramSocket;
21
+import java.net.InetAddress;
22
+import java.net.InetSocketAddress;
23
+import java.net.NetworkInterface;
24
+import java.net.SocketException;
25
+import java.util.Enumeration;
26
+import java.util.HashMap;
27
+import java.util.Map;
28
+import java.util.concurrent.Semaphore;
29
+
30
+/**
31
+ *
32
+ * @author chris
33
+ */
34
+public class NatTraverser {
35
+
36
+    protected final CommsAgent agent;
37
+
38
+    protected InetAddress address;
39
+    protected DiscoveryInfo result;
40
+
41
+    protected NatType theirType;
42
+    protected NatType ourType;
43
+
44
+    public NatTraverser(final CommsAgent agent) {
45
+        this.agent = agent;
46
+    }
47
+
48
+    public void setInetAddress(final InetAddress address) {
49
+        this.address = address;
50
+        this.result = getDiscoveryInfo(address);
51
+        addressUpdated();
52
+    }
53
+
54
+    public void findBestInetAddress() {
55
+        final Semaphore threadSem = new Semaphore(0);
56
+
57
+        NatType bestType = null;
58
+        Map.Entry<InetAddress, DiscoveryInfo> best = null;
59
+
60
+        for (final Map.Entry<InetAddress, DiscoveryInfo> info : getDiscoveryInfos().entrySet()) {
61
+            if (info.getValue() == null) {
62
+                return;
63
+            }
64
+
65
+            final NatType thisType = getTypeFromDI(info.getValue());
66
+            if (thisType != null &&
67
+                    (bestType == null || thisType.ordinal() < bestType.ordinal())) {
68
+                bestType = thisType;
69
+                best = info;
70
+            }
71
+        }
72
+
73
+        if (best != null) {
74
+            this.address = best.getKey();
75
+            this.result = best.getValue();
76
+            addressUpdated();
77
+        }
78
+    }
79
+
80
+    protected void addressUpdated() {
81
+        try {
82
+            ourType = getTypeFromDI(result);
83
+
84
+            System.out.println("Using local IP: " + result.getLocalIP().getHostAddress()
85
+                    + " (remote: " + result.getPublicIP().getHostAddress() + "; iface: "
86
+                    + NetworkInterface.getByInetAddress(result.getLocalIP()).getName()
87
+                    + "; type: " + ourType + ")");
88
+
89
+            agent.sendNATType(ourType);
90
+
91
+            theirType = agent.readNATType();
92
+        } catch (SocketException ex) {
93
+            // Meh
94
+        }
95
+    }
96
+
97
+    public DatagramSocket traverse() throws UnsupportedOperationException,
98
+            SocketException, UtilityException, IOException,
99
+            MessageHeaderParsingException, MessageAttributeParsingException, InterruptedException {
100
+        if (!getTypeFromDI(result).canTraverseWith(theirType)) {
101
+            throw new UnsupportedOperationException("Cannot traverse "
102
+                    + "between a " + ourType + " network and a "
103
+                    + theirType + " network.");
104
+        }
105
+
106
+        final DatagramSocket sock
107
+                = new DatagramSocket(new InetSocketAddress(address, 0));
108
+        sock.connect(InetAddress.getByName("stun.dmdirc.com"), 3478);
109
+
110
+        MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest);
111
+        sendMH.generateTransactionID();
112
+
113
+        byte[] data = sendMH.getBytes();
114
+        DatagramPacket send = new DatagramPacket(data, data.length);
115
+        sock.send(send);
116
+
117
+        MessageHeader receiveMH = new MessageHeader();
118
+        while (!(receiveMH.equalTransactionID(sendMH))) {
119
+            DatagramPacket receive = new DatagramPacket(new byte[200], 200);
120
+            sock.receive(receive);
121
+            receiveMH = MessageHeader.parseHeader(receive.getData());
122
+            receiveMH.parseAttributes(receive.getData());
123
+        }
124
+
125
+        final MappedAddress ma = (MappedAddress) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.MappedAddress);
126
+
127
+        System.out.println("I'm listening on " + address.getHostAddress() + ":"
128
+                + sock.getLocalPort() + ". STUN server says I'm sending packets from "
129
+                + ma.getAddress().getInetAddress().getHostAddress() + ":"
130
+                + ma.getPort());
131
+
132
+        agent.sendAddress(ma.getAddress().getInetAddress());
133
+        agent.sendPort(ma.getPort());
134
+
135
+        final InetAddress raddr = agent.readAddress();
136
+        final int rport = agent.readPort();
137
+
138
+        sock.disconnect();
139
+        sock.connect(raddr, rport);
140
+
141
+        boolean cont;
142
+
143
+        do {
144
+            cont = false;
145
+            DatagramPacket p = new DatagramPacket("NatTraverser!".getBytes(), 13);
146
+            
147
+            try {
148
+                sock.send(p);
149
+
150
+                p = new DatagramPacket(new byte[13], 13);
151
+
152
+                sock.receive(p);
153
+            } catch (IOException ex) {
154
+                ex.printStackTrace();
155
+
156
+                cont = true;
157
+                Thread.sleep(500);
158
+            }
159
+        } while (cont);
160
+
161
+        return sock;
162
+    }
163
+
164
+    public static NatType getTypeFromDI(final DiscoveryInfo info) {
165
+        if (info.isFullCone()) {
166
+            return NatType.FULL_CONE;
167
+        } else if (info.isOpenAccess()) {
168
+            return NatType.OPEN;
169
+        } else if (info.isPortRestrictedCone()) {
170
+            return NatType.PORT_RESTRICTED_CONE;
171
+        } else if (info.isRestrictedCone()) {
172
+            return NatType.RESTRICTED_CONE;
173
+        } else if (info.isSymmetric()) {
174
+            return NatType.SYMMETRIC;
175
+        } else {
176
+            return null;
177
+        }
178
+    }
179
+
180
+    protected Map<InetAddress, DiscoveryInfo> getDiscoveryInfos() {
181
+        final Map<InetAddress, DiscoveryInfo> res
182
+                = new HashMap<InetAddress, DiscoveryInfo>();
183
+
184
+        try {
185
+            final Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces();
186
+            while (en.hasMoreElements()) {
187
+                final Enumeration<InetAddress> inen = en.nextElement().getInetAddresses();
188
+
189
+                while (inen.hasMoreElements()) {
190
+                    final InetAddress ia = inen.nextElement();
191
+                    res.put(ia, getDiscoveryInfo(ia));
192
+                }
193
+            }
194
+        } catch (SocketException ex) {
195
+            // Meh
196
+        }
197
+
198
+        return res;
199
+    }
200
+
201
+    protected DiscoveryInfo getDiscoveryInfo(final InetAddress iaddress) {
202
+        try {
203
+            return new DiscoveryTest(iaddress, "stun.dmdirc.com", 3478).test();
204
+        } catch (IOException ex) {
205
+            // Meh
206
+        } catch (UtilityException ex) {
207
+            // Meh
208
+        } catch (MessageAttributeException ex) {
209
+            // Meh
210
+        } catch (MessageHeaderParsingException ex) {
211
+            // Meh
212
+        }
213
+
214
+        return null;
215
+    }
216
+
217
+}

+ 36
- 0
src/com/md87/nat/NatType.java View File

@@ -0,0 +1,36 @@
1
+/*
2
+ * To change this template, choose Tools | Templates
3
+ * and open the template in the editor.
4
+ */
5
+
6
+package com.md87.nat;
7
+
8
+/**
9
+ *
10
+ * @author chris
11
+ */
12
+public enum NatType {
13
+
14
+    OPEN(true),
15
+    FULL_CONE(true),
16
+    RESTRICTED_CONE(false),
17
+    PORT_RESTRICTED_CONE(false),
18
+    SYMMETRIC(false);
19
+
20
+    private final boolean canTraverseWithSymmetric;
21
+
22
+    NatType(final boolean canTraverseWithSymmetric) {
23
+        this.canTraverseWithSymmetric = canTraverseWithSymmetric;
24
+    }
25
+
26
+    public boolean canTraverseWith(final NatType type) {
27
+        if (type == SYMMETRIC) {
28
+            return canTraverseWithSymmetric;
29
+        } else if (this == SYMMETRIC) {
30
+            return type.canTraverseWith(this);
31
+        } else  {
32
+            return true;
33
+        }
34
+    }
35
+
36
+}

+ 105
- 0
src/com/md87/nat/test/SimpleDemo.java View File

@@ -0,0 +1,105 @@
1
+/*
2
+ * To change this template, choose Tools | Templates
3
+ * and open the template in the editor.
4
+ */
5
+
6
+package com.md87.nat.test;
7
+
8
+import com.md87.nat.CommsAgent;
9
+import com.md87.nat.NatTraverser;
10
+import com.md87.nat.NatType;
11
+import de.javawi.jstun.attribute.MessageAttributeParsingException;
12
+import de.javawi.jstun.header.MessageHeaderParsingException;
13
+import de.javawi.jstun.util.UtilityException;
14
+import java.io.Console;
15
+import java.io.IOException;
16
+import java.net.DatagramPacket;
17
+import java.net.DatagramSocket;
18
+import java.net.InetAddress;
19
+import java.net.SocketException;
20
+import java.net.UnknownHostException;
21
+
22
+/**
23
+ *
24
+ * @author chris
25
+ */
26
+public class SimpleDemo implements CommsAgent {
27
+
28
+    static final Console console = System.console();
29
+
30
+    public void sendNATType(NatType type) {
31
+        console.printf("Nat type: %s\n", type.name());
32
+    }
33
+
34
+    public NatType readNATType() {
35
+        console.printf("Enter NAT type: ");
36
+        return NatType.valueOf(console.readLine().trim());
37
+    }
38
+
39
+    public void sendAddress(InetAddress address) {
40
+        console.printf("Address: %s\n", address.getHostAddress());
41
+    }
42
+
43
+    public void sendPort(int port) {
44
+        console.printf("Port: %s\n", port);
45
+    }
46
+
47
+    public InetAddress readAddress() {
48
+        console.printf("Enter address: ");
49
+        try {
50
+            return InetAddress.getByName(console.readLine().trim());
51
+        } catch (UnknownHostException ex) {
52
+            return null;
53
+        }
54
+    }
55
+
56
+    public int readPort() {
57
+        console.printf("Enter port: ");
58
+        return Integer.parseInt(console.readLine().trim());
59
+    }
60
+
61
+    public static void main(final String ... args) throws
62
+            UnsupportedOperationException, IOException, SocketException,
63
+            UtilityException, MessageHeaderParsingException, 
64
+            MessageAttributeParsingException, InterruptedException {
65
+        final NatTraverser traverser = new NatTraverser(new SimpleDemo());
66
+        traverser.findBestInetAddress();
67
+        
68
+        final DatagramSocket sock = traverser.traverse();
69
+
70
+        System.out.println("Traversal complete. Say hello!");
71
+
72
+        new Thread(new Runnable() {
73
+
74
+            public void run() {
75
+                while (true) {
76
+                    DatagramPacket p = new DatagramPacket(new byte[255], 255);
77
+                try {
78
+                    
79
+                        sock.receive(p);
80
+                        System.out.printf("<< %s\n", new String(p.getData()));
81
+                    
82
+                } catch (IOException ex) {
83
+                    ex.printStackTrace();
84
+                }
85
+                }
86
+            }
87
+        }).start();
88
+
89
+        new Thread(new Runnable() {
90
+
91
+            public void run() {
92
+                while (true) {try {
93
+                    DatagramPacket p = new DatagramPacket(new byte[255], 255);
94
+                    p.setData((console.readLine() + "\n").getBytes());
95
+                    sock.send(p);
96
+                    System.out.printf(">> %s\n", new String(p.getData()));
97
+                    
98
+                } catch (IOException ex) {
99
+                    ex.printStackTrace();
100
+                }}
101
+            }
102
+        }).start();
103
+    }
104
+
105
+}

+ 81
- 0
src/de/javawi/jstun/attribute/ChangeRequest.java View File

@@ -0,0 +1,81 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.attribute;
13
+
14
+import de.javawi.jstun.util.*;
15
+
16
+public class ChangeRequest extends MessageAttribute {
17
+   /* 
18
+    *  0                   1                   2                   3
19
+    *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
20
+    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
21
+    * |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 A B 0|
22
+    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
23
+    */
24
+	boolean changeIP = false;
25
+	boolean changePort = false;
26
+	
27
+	public ChangeRequest() {
28
+		super(MessageAttribute.MessageAttributeType.ChangeRequest);
29
+	}
30
+	
31
+	public boolean isChangeIP() {
32
+		return changeIP;
33
+	}
34
+	
35
+	public boolean isChangePort() {
36
+		return changePort;
37
+	}
38
+	
39
+	public void setChangeIP() {
40
+		changeIP = true;
41
+	}
42
+	
43
+	public void setChangePort() {
44
+		changePort = true;
45
+	}
46
+	
47
+	public byte[] getBytes() throws UtilityException {
48
+		byte[] result = new byte[8];
49
+		// message attribute header
50
+		// type
51
+		System.arraycopy(Utility.integerToTwoBytes(typeToInteger(type)), 0, result, 0, 2);
52
+		// length
53
+		System.arraycopy(Utility.integerToTwoBytes(4), 0, result, 2, 2);
54
+		
55
+		// change request header
56
+		if (changeIP) result[7] = Utility.integerToOneByte(4);
57
+		if (changePort) result[7] = Utility.integerToOneByte(2);
58
+		if (changeIP && changePort) result[7] = Utility.integerToOneByte(6);
59
+		return result;
60
+	}
61
+	
62
+	public static ChangeRequest parse(byte[] data) throws MessageAttributeParsingException {
63
+		try {
64
+			if (data.length < 4) {
65
+				throw new MessageAttributeParsingException("Data array too short");
66
+			}
67
+			ChangeRequest cr = new ChangeRequest();
68
+			int status = Utility.oneByteToInteger(data[3]);
69
+			switch (status) {
70
+			case 0: break;
71
+			case 2: cr.setChangePort(); break;
72
+			case 4: cr.setChangeIP(); break;
73
+			case 6: cr.setChangeIP(); cr.setChangePort(); break;
74
+			default: throw new MessageAttributeParsingException("Status parsing error"); 
75
+			}
76
+			return cr;
77
+		} catch (UtilityException ue) {
78
+			throw new MessageAttributeParsingException("Parsing error");
79
+		}
80
+	}
81
+}

+ 30
- 0
src/de/javawi/jstun/attribute/ChangedAddress.java View File

@@ -0,0 +1,30 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.attribute;
13
+
14
+import java.util.logging.Logger;
15
+
16
+
17
+public class ChangedAddress extends MappedResponseChangedSourceAddressReflectedFrom {
18
+	private static Logger logger = Logger.getLogger("de.javawi.stun.attribute.ChangedAddress");
19
+	
20
+	public ChangedAddress() {
21
+		super(MessageAttribute.MessageAttributeType.ChangedAddress);
22
+	}
23
+	
24
+	public static MessageAttribute parse(byte[] data) throws MessageAttributeParsingException {
25
+		ChangedAddress ca = new ChangedAddress();
26
+		MappedResponseChangedSourceAddressReflectedFrom.parse(ca, data);
27
+		logger.finer("Message Attribute: Changed Address parsed: " + ca.toString() + ".");
28
+		return ca;
29
+	}
30
+}

+ 42
- 0
src/de/javawi/jstun/attribute/Dummy.java View File

@@ -0,0 +1,42 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.attribute;
13
+
14
+import de.javawi.jstun.util.Utility;
15
+import de.javawi.jstun.util.UtilityException;
16
+
17
+public class Dummy extends MessageAttribute {
18
+	int lengthValue;
19
+	public Dummy() {
20
+		super(MessageAttributeType.Dummy);
21
+	}
22
+	
23
+	public void setLengthValue(int length) {
24
+		this.lengthValue = length;
25
+	}
26
+
27
+	public byte[] getBytes() throws UtilityException {
28
+		byte[] result = new byte[lengthValue + 4];
29
+		//	message attribute header
30
+		// type
31
+		System.arraycopy(Utility.integerToTwoBytes(typeToInteger(type)), 0, result, 0, 2);
32
+		// length
33
+		System.arraycopy(Utility.integerToTwoBytes(lengthValue), 0, result, 2, 2);
34
+		return result;
35
+	}
36
+	
37
+	public static Dummy parse(byte[] data) {
38
+		Dummy dummy = new Dummy();
39
+		dummy.setLengthValue(data.length);
40
+		return dummy;
41
+	}
42
+}

+ 104
- 0
src/de/javawi/jstun/attribute/ErrorCode.java View File

@@ -0,0 +1,104 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.attribute;
13
+
14
+import de.javawi.jstun.util.Utility;
15
+import de.javawi.jstun.util.UtilityException;
16
+
17
+public class ErrorCode extends MessageAttribute {
18
+   /* 
19
+    *  0                   1                   2                   3
20
+    *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
21
+    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
22
+    * |                   0                     |Class|     Number    |
23
+    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
24
+    * |      Reason Phrase (variable)                                ..
25
+    * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
26
+    */
27
+	
28
+	int responseCode;
29
+	String reason;
30
+	
31
+	public ErrorCode() {
32
+		super(MessageAttribute.MessageAttributeType.ErrorCode);
33
+	}
34
+	
35
+	public void setResponseCode(int responseCode) throws MessageAttributeException {
36
+		switch (responseCode) {
37
+		case 400: reason = "Bad Request"; break;
38
+		case 401: reason = "Unauthorized"; break;
39
+		case 420: reason = "Unkown Attribute"; break;
40
+		case 430: reason = "Stale Credentials"; break;
41
+		case 431: reason = "Integrity Check Failure"; break;
42
+		case 432: reason = "Missing Username"; break;
43
+		case 433: reason = "Use TLS"; break;
44
+		case 500: reason = "Server Error"; break;
45
+		case 600: reason = "Global Failure"; break;
46
+		default: throw new MessageAttributeException("Response Code is not valid");
47
+		}
48
+		this.responseCode = responseCode;
49
+	}
50
+	
51
+	public int getResponseCode() {
52
+		return responseCode;
53
+	}
54
+	
55
+	public String getReason() {
56
+		return reason;
57
+	}
58
+
59
+	public byte[] getBytes() throws UtilityException {
60
+		int length = reason.length();
61
+		// length adjustment
62
+		if ((length % 4) != 0) {
63
+			length += 4 - (length % 4);
64
+		}
65
+		// message attribute header
66
+		length += 4;
67
+		byte[] result = new byte[length];
68
+		// message attribute header
69
+		// type
70
+		System.arraycopy(Utility.integerToTwoBytes(typeToInteger(type)), 0, result, 0, 2);
71
+		// length
72
+		System.arraycopy(Utility.integerToTwoBytes(length-4), 0, result, 2, 2);
73
+		
74
+		// error code header
75
+		int classHeader = (int) Math.floor(((double)responseCode)/100);
76
+		result[6] = Utility.integerToOneByte(classHeader);
77
+		result[7] = Utility.integerToOneByte(responseCode%100);
78
+		byte[] reasonArray = reason.getBytes();
79
+		System.arraycopy(reasonArray, 0, result, 8, reasonArray.length);		
80
+		return result;
81
+	}
82
+	
83
+	public static ErrorCode parse(byte[] data) throws MessageAttributeParsingException {
84
+		try {
85
+			if (data.length < 4) {
86
+				throw new MessageAttributeParsingException("Data array too short");
87
+			}
88
+			byte classHeaderByte = data[3];
89
+			int classHeader = Utility.oneByteToInteger(classHeaderByte);
90
+			if ((classHeader < 1) || (classHeader > 6)) throw new MessageAttributeParsingException("Class parsing error");
91
+			byte numberByte = data[4];
92
+			int number = Utility.oneByteToInteger(numberByte);
93
+			if ((number < 0) || (number > 99)) throw new MessageAttributeParsingException("Number parsing error");
94
+			int responseCode = (classHeader * 100) + number;
95
+			ErrorCode result = new ErrorCode();
96
+			result.setResponseCode(responseCode);
97
+			return result;
98
+		} catch (UtilityException ue) {
99
+			throw new MessageAttributeParsingException("Parsing error");
100
+		} catch (MessageAttributeException mae) {
101
+			throw new MessageAttributeParsingException("Parsing error");
102
+		}		
103
+	}
104
+}

+ 29
- 0
src/de/javawi/jstun/attribute/MappedAddress.java View File

@@ -0,0 +1,29 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.attribute;
13
+
14
+import java.util.logging.Logger;
15
+
16
+
17
+public class MappedAddress extends MappedResponseChangedSourceAddressReflectedFrom {
18
+	private static Logger logger = Logger.getLogger("de.javawi.stun.attribute.MappedAddress");
19
+	public MappedAddress() {
20
+		super(MessageAttribute.MessageAttributeType.MappedAddress);
21
+	}
22
+	
23
+	public static MessageAttribute parse(byte[] data) throws MessageAttributeParsingException {
24
+		MappedAddress ma = new MappedAddress();
25
+		MappedResponseChangedSourceAddressReflectedFrom.parse(ma, data);
26
+		logger.finer("Message Attribute: Mapped Address parsed: " + ma.toString() + ".");
27
+		return ma;
28
+	}
29
+}

+ 107
- 0
src/de/javawi/jstun/attribute/MappedResponseChangedSourceAddressReflectedFrom.java View File

@@ -0,0 +1,107 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.attribute;
13
+
14
+import de.javawi.jstun.util.*;
15
+
16
+public class MappedResponseChangedSourceAddressReflectedFrom extends MessageAttribute {
17
+	int port;
18
+	Address address;
19
+	
20
+	/*  
21
+	 *  0                   1                   2                   3
22
+	 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
23
+	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
24
+	 * |x x x x x x x x|    Family     |           Port                |
25
+	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
26
+	 * |                             Address                           |
27
+	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
28
+	 */
29
+	public MappedResponseChangedSourceAddressReflectedFrom() {
30
+		super();
31
+		try {
32
+			port = 0;
33
+			address = new Address("0.0.0.0");
34
+		} catch (UtilityException ue) {
35
+			ue.getMessage();
36
+			ue.printStackTrace();
37
+		}
38
+	}
39
+	
40
+	public MappedResponseChangedSourceAddressReflectedFrom(MessageAttribute.MessageAttributeType type) {
41
+		super(type);
42
+	}
43
+	
44
+	public int getPort() {
45
+		return port;
46
+	}
47
+	
48
+	public Address getAddress() {
49
+		return address;
50
+	}
51
+	
52
+	public void setPort(int port) throws MessageAttributeException {
53
+		if ((port > 65536) || (port < 0)) {
54
+			throw new MessageAttributeException("Port value " + port + " out of range.");
55
+		}
56
+		this.port = port;
57
+	}
58
+	
59
+	public void setAddress(Address address) {
60
+		this.address = address;
61
+	}
62
+	
63
+	public byte[] getBytes() throws UtilityException {
64
+		byte[] result = new byte[12];
65
+		// message attribute header
66
+		// type
67
+		System.arraycopy(Utility.integerToTwoBytes(typeToInteger(type)), 0, result, 0, 2);
68
+		// length
69
+		System.arraycopy(Utility.integerToTwoBytes(8), 0, result, 2, 2);
70
+		
71
+		// mappedaddress header
72
+		// family
73
+		result[5] = Utility.integerToOneByte(0x01); 
74
+		// port
75
+		System.arraycopy(Utility.integerToTwoBytes(port), 0, result, 6, 2);
76
+		// address
77
+		System.arraycopy(address.getBytes(), 0, result, 8, 4);
78
+		return result;
79
+	}
80
+	
81
+	protected static MappedResponseChangedSourceAddressReflectedFrom parse(MappedResponseChangedSourceAddressReflectedFrom ma, byte[] data) throws MessageAttributeParsingException {
82
+		try {
83
+			if (data.length < 8) {
84
+				throw new MessageAttributeParsingException("Data array too short");
85
+			}
86
+			int family = Utility.oneByteToInteger(data[1]);
87
+			if (family != 0x01) throw new MessageAttributeParsingException("Family " + family + " is not supported");
88
+			byte[] portArray = new byte[2];
89
+			System.arraycopy(data, 2, portArray, 0, 2);
90
+			ma.setPort(Utility.twoBytesToInteger(portArray));
91
+			int firstOctet = Utility.oneByteToInteger(data[4]);
92
+			int secondOctet = Utility.oneByteToInteger(data[5]);
93
+			int thirdOctet = Utility.oneByteToInteger(data[6]);
94
+			int fourthOctet = Utility.oneByteToInteger(data[7]);
95
+			ma.setAddress(new Address(firstOctet, secondOctet, thirdOctet, fourthOctet));
96
+			return ma;
97
+		} catch (UtilityException ue) {
98
+			throw new MessageAttributeParsingException("Parsing error");
99
+		} catch (MessageAttributeException mae) {
100
+			throw new MessageAttributeParsingException("Port parsing error");
101
+		}
102
+	}
103
+	
104
+	public String toString() {
105
+		return "Address " +address.toString() + ", Port " + port;
106
+	}
107
+}

+ 115
- 0
src/de/javawi/jstun/attribute/MessageAttribute.java View File

@@ -0,0 +1,115 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.attribute;
13
+
14
+import java.util.logging.*;
15
+
16
+import de.javawi.jstun.util.*;
17
+
18
+
19
+public abstract class MessageAttribute implements MessageAttributeInterface {
20
+	private static Logger logger = Logger.getLogger("de.javawi.stun.util.MessageAttribute");
21
+	MessageAttributeType type;
22
+	
23
+	public MessageAttribute() {
24
+	}
25
+	
26
+	public MessageAttribute(MessageAttributeType type) {
27
+		setType(type);
28
+	}
29
+	
30
+	public void setType(MessageAttributeType type) {
31
+		this.type = type;
32
+	}
33
+	
34
+	public MessageAttribute.MessageAttributeType getType() {
35
+		return type;
36
+	}
37
+	
38
+	public static int typeToInteger(MessageAttributeType type) {
39
+		if (type == MessageAttributeType.MappedAddress) return MAPPEDADDRESS;
40
+		if (type == MessageAttributeType.ResponseAddress) return RESPONSEADDRESS;
41
+		if (type == MessageAttributeType.ChangeRequest) return CHANGEREQUEST;
42
+		if (type == MessageAttributeType.SourceAddress) return SOURCEADDRESS;
43
+		if (type == MessageAttributeType.ChangedAddress) return CHANGEDADDRESS;
44
+		if (type == MessageAttributeType.Username) return USERNAME;
45
+		if (type == MessageAttributeType.Password) return PASSWORD;
46
+		if (type == MessageAttributeType.MessageIntegrity) return MESSAGEINTEGRITY;
47
+		if (type == MessageAttributeType.ErrorCode) return ERRORCODE;
48
+		if (type == MessageAttributeType.UnknownAttribute) return UNKNOWNATTRIBUTE;
49
+		if (type == MessageAttributeType.ReflectedFrom) return REFLECTEDFROM;
50
+		if (type == MessageAttributeType.Dummy) return DUMMY;
51
+		return -1;
52
+	}
53
+	
54
+	public static MessageAttributeType intToType(long type) {
55
+		if (type == MAPPEDADDRESS) return MessageAttributeType.MappedAddress;
56
+		if (type == RESPONSEADDRESS) return MessageAttributeType.ResponseAddress;
57
+		if (type == CHANGEREQUEST) return MessageAttributeType.ChangeRequest;
58
+		if (type == SOURCEADDRESS) return MessageAttributeType.SourceAddress;
59
+		if (type == CHANGEDADDRESS) return MessageAttributeType.ChangedAddress;
60
+		if (type == USERNAME) return MessageAttributeType.Username;
61
+		if (type == PASSWORD) return MessageAttributeType.Password;
62
+		if (type == MESSAGEINTEGRITY) return MessageAttributeType.MessageIntegrity;
63
+		if (type == ERRORCODE) return MessageAttributeType.ErrorCode;
64
+		if (type == UNKNOWNATTRIBUTE) return MessageAttributeType.UnknownAttribute;
65
+		if (type == REFLECTEDFROM) return MessageAttributeType.ReflectedFrom;
66
+		if (type == DUMMY) return MessageAttributeType.Dummy;
67
+		return null;
68
+	}
69
+	
70
+	abstract public byte[] getBytes() throws UtilityException;
71
+	//abstract public MessageAttribute parse(byte[] data) throws MessageAttributeParsingException;
72
+	
73
+	public int getLength() throws UtilityException {
74
+		int length = getBytes().length;
75
+		return length;
76
+	}
77
+	
78
+	public static MessageAttribute parseCommonHeader(byte[] data) throws MessageAttributeParsingException {
79
+		try {			
80
+			byte[] typeArray = new byte[2];
81
+			System.arraycopy(data, 0, typeArray, 0, 2);
82
+			int type = Utility.twoBytesToInteger(typeArray);
83
+			byte[] lengthArray = new byte[2];
84
+			System.arraycopy(data, 2, lengthArray, 0, 2);
85
+			int lengthValue = Utility.twoBytesToInteger(lengthArray);
86
+			byte[] valueArray = new byte[lengthValue];
87
+			System.arraycopy(data, 4, valueArray, 0, lengthValue);
88
+			MessageAttribute ma;
89
+			switch (type) {
90
+			case MAPPEDADDRESS: ma = MappedAddress.parse(valueArray); break;
91
+			case RESPONSEADDRESS: ma = ResponseAddress.parse(valueArray); break;
92
+			case CHANGEREQUEST: ma = ChangeRequest.parse(valueArray); break;
93
+			case SOURCEADDRESS: ma = SourceAddress.parse(valueArray); break;
94
+			case CHANGEDADDRESS: ma = ChangedAddress.parse(valueArray); break;
95
+			case USERNAME: ma = Username.parse(valueArray); break;
96
+			case PASSWORD: ma = Password.parse(valueArray); break;
97
+			case MESSAGEINTEGRITY: ma = MessageIntegrity.parse(valueArray); break;
98
+			case ERRORCODE: ma = ErrorCode.parse(valueArray); break;
99
+			case UNKNOWNATTRIBUTE: ma = UnknownAttribute.parse(valueArray); break;
100
+			case REFLECTEDFROM: ma = ReflectedFrom.parse(valueArray); break;
101
+			default:
102
+				if (type <= 0x7fff) {
103
+					throw new UnknownMessageAttributeException("Unkown mandatory message attribute", intToType(type));
104
+				} else {
105
+					logger.finer("MessageAttribute with type " + type + " unkown.");
106
+					ma = Dummy.parse(valueArray);
107
+					break;
108
+				}
109
+			}
110
+			return ma;
111
+		} catch (UtilityException ue) {
112
+			throw new MessageAttributeParsingException("Parsing error");
113
+		}
114
+	}
115
+}

+ 20
- 0
src/de/javawi/jstun/attribute/MessageAttributeException.java View File

@@ -0,0 +1,20 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.attribute;
13
+
14
+public class MessageAttributeException extends Exception {
15
+	private static final long serialVersionUID = 3258131345099404850L;
16
+
17
+	public MessageAttributeException(String mesg) {
18
+		super(mesg);
19
+	}
20
+}

+ 28
- 0
src/de/javawi/jstun/attribute/MessageAttributeInterface.java View File

@@ -0,0 +1,28 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.attribute;
13
+
14
+public interface MessageAttributeInterface {
15
+	public enum MessageAttributeType { MappedAddress, ResponseAddress, ChangeRequest, SourceAddress, ChangedAddress, Username, Password, MessageIntegrity, ErrorCode, UnknownAttribute, ReflectedFrom, Dummy };
16
+	final static int MAPPEDADDRESS = 0x0001;
17
+	final static int RESPONSEADDRESS = 0x0002;
18
+	final static int CHANGEREQUEST = 0x0003;
19
+	final static int SOURCEADDRESS = 0x0004;
20
+	final static int CHANGEDADDRESS = 0x0005;
21
+	final static int USERNAME = 0x0006;
22
+	final static int PASSWORD = 0x0007;
23
+	final static int MESSAGEINTEGRITY = 0x0008;
24
+	final static int ERRORCODE = 0x0009;
25
+	final static int UNKNOWNATTRIBUTE = 0x000a;
26
+	final static int REFLECTEDFROM = 0x000b;
27
+	final static int DUMMY = 0x0000;
28
+}

+ 20
- 0
src/de/javawi/jstun/attribute/MessageAttributeParsingException.java View File

@@ -0,0 +1,20 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.attribute;
13
+
14
+public class MessageAttributeParsingException extends MessageAttributeException { 
15
+	private static final long serialVersionUID = 3258409534426263605L;
16
+
17
+	public MessageAttributeParsingException(String mesg) {
18
+		super(mesg);
19
+	}
20
+}

+ 27
- 0
src/de/javawi/jstun/attribute/MessageIntegrity.java View File

@@ -0,0 +1,27 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.attribute;
13
+
14
+public class MessageIntegrity extends MessageAttribute {
15
+	// incomplete message integrity implementation
16
+	public MessageIntegrity() {
17
+		super(MessageAttribute.MessageAttributeType.MessageIntegrity);
18
+	}
19
+	
20
+	public byte[] getBytes() {
21
+		return new byte[0];
22
+	}
23
+	
24
+	public static MessageIntegrity parse(byte[] data) {
25
+		return new MessageIntegrity();
26
+	}
27
+}

+ 64
- 0
src/de/javawi/jstun/attribute/Password.java View File

@@ -0,0 +1,64 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.attribute;
13
+
14
+import de.javawi.jstun.util.Utility;
15
+import de.javawi.jstun.util.UtilityException;
16
+
17
+public class Password extends MessageAttribute {
18
+	String password;
19
+	
20
+	public Password() {
21
+		super(MessageAttribute.MessageAttributeType.Password);
22
+	}
23
+	
24
+	public Password(String password) {
25
+		super(MessageAttribute.MessageAttributeType.Password);
26
+		setPassword(password);
27
+	}
28
+	
29
+	public String getPassword() {
30
+		return password;
31
+	}
32
+	
33
+	public void setPassword(String password) {
34
+		this.password = password;
35
+	}
36
+	
37
+	public byte[] getBytes() throws UtilityException {
38
+		int length = password.length();
39
+		// password header
40
+		if ((length % 4) != 0) {
41
+			length += 4 - (length % 4);
42
+		}
43
+		// message attribute header
44
+		length += 4;
45
+		byte[] result = new byte[length];
46
+		// message attribute header
47
+		// type
48
+		System.arraycopy(Utility.integerToTwoBytes(typeToInteger(type)), 0, result, 0, 2);
49
+		// length
50
+		System.arraycopy(Utility.integerToTwoBytes(length - 4), 0, result, 2, 2);
51
+		
52
+		// password header
53
+		byte[] temp = password.getBytes();
54
+		System.arraycopy(temp, 0, result, 4, temp.length);
55
+		return result;
56
+	}
57
+	
58
+	public static Password parse(byte[] data) {
59
+		Password result = new Password();
60
+		String password = new String(data);
61
+		result.setPassword(password);
62
+		return result;
63
+	}
64
+}

+ 31
- 0
src/de/javawi/jstun/attribute/ReflectedFrom.java View File

@@ -0,0 +1,31 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.attribute;
13
+
14
+import java.util.logging.Logger;
15
+
16
+public class ReflectedFrom extends MappedResponseChangedSourceAddressReflectedFrom {
17
+	private static Logger logger = Logger.getLogger("de.javawi.stun.attribute.ReflectedFrom");
18
+	
19
+	public ReflectedFrom() {
20
+		super(MessageAttribute.MessageAttributeType.ReflectedFrom);
21
+	}
22
+	
23
+	public static ReflectedFrom parse(byte[] data) throws MessageAttributeParsingException {
24
+		ReflectedFrom result = new ReflectedFrom();
25
+		MappedResponseChangedSourceAddressReflectedFrom.parse(result, data);
26
+		logger.finer("Message Attribute: ReflectedFrom parsed: " + result.toString() + ".");
27
+		return result;
28
+	}
29
+
30
+	
31
+}

+ 29
- 0
src/de/javawi/jstun/attribute/ResponseAddress.java View File

@@ -0,0 +1,29 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.attribute;
13
+
14
+import java.util.logging.Logger;
15
+
16
+
17
+public class ResponseAddress extends MappedResponseChangedSourceAddressReflectedFrom {
18
+	private static Logger logger = Logger.getLogger("de.javawi.stun.attribute.ResponseAddress");
19
+	public ResponseAddress() {
20
+		super(MessageAttribute.MessageAttributeType.ResponseAddress);
21
+	}
22
+	
23
+	public static MessageAttribute parse(byte[] data) throws MessageAttributeParsingException {
24
+		ResponseAddress ra = new ResponseAddress();
25
+		MappedResponseChangedSourceAddressReflectedFrom.parse(ra, data);
26
+		logger.finer("Message Attribute: Response Address parsed: " + ra.toString() + ".");
27
+		return ra;
28
+	}
29
+}

+ 29
- 0
src/de/javawi/jstun/attribute/SourceAddress.java View File

@@ -0,0 +1,29 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.attribute;
13
+
14
+import java.util.logging.Logger;
15
+
16
+
17
+public class SourceAddress extends MappedResponseChangedSourceAddressReflectedFrom {
18
+	private static Logger logger = Logger.getLogger("de.javawi.stun.attribute.SourceAddress");
19
+	public SourceAddress() {
20
+		super(MessageAttribute.MessageAttributeType.SourceAddress);
21
+	}
22
+	
23
+	public static MessageAttribute parse(byte[] data) throws MessageAttributeParsingException {
24
+		SourceAddress sa = new SourceAddress();
25
+		MappedResponseChangedSourceAddressReflectedFrom.parse(sa, data);
26
+		logger.finer("Message Attribute: Source Address parsed: " + sa.toString() + ".");
27
+		return sa;
28
+	}
29
+}

+ 82
- 0
src/de/javawi/jstun/attribute/UnknownAttribute.java View File

@@ -0,0 +1,82 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.attribute;
13
+
14
+import java.util.*;
15
+
16
+import de.javawi.jstun.util.Utility;
17
+import de.javawi.jstun.util.UtilityException;
18
+
19
+public class UnknownAttribute extends MessageAttribute {
20
+	/* 
21
+	 *  0                   1                   2                   3
22
+	 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
23
+	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
24
+	 * |      Attribute 1 Type           |     Attribute 2 Type        |
25
+	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
26
+	 * |      Attribute 3 Type           |     Attribute 4 Type    ...
27
+	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
28
+	 */
29
+	
30
+	Vector<MessageAttributeType> unkown = new Vector<MessageAttributeType>();
31
+	
32
+	public UnknownAttribute() {
33
+		super(MessageAttribute.MessageAttributeType.UnknownAttribute);
34
+	}
35
+	
36
+	public void addAttribute(MessageAttributeType attribute) {
37
+		unkown.add(attribute);
38
+	}
39
+	
40
+	public byte[] getBytes() throws UtilityException {
41
+		int length = 0;
42
+		if (unkown.size()%2 == 1) {
43
+			length = 2 * (unkown.size() + 1) + 4;
44
+		} else {
45
+			length = 2 * unkown.size() + 4;
46
+		}
47
+		byte[] result = new byte[length];
48
+		// message attribute header
49
+		// type
50
+		System.arraycopy(Utility.integerToTwoBytes(typeToInteger(type)), 0, result, 0, 2);
51
+		// length
52
+		System.arraycopy(Utility.integerToTwoBytes(length - 4), 0, result, 2, 2);
53
+		
54
+		// unkown attribute header
55
+		Iterator<MessageAttributeType> it = unkown.iterator();
56
+		while(it.hasNext()) {
57
+			MessageAttributeType attri = it.next();
58
+			System.arraycopy(Utility.integerToTwoBytes(typeToInteger(attri)), 0, result, 4, 2);
59
+		}
60
+		// padding
61
+		if (unkown.size()%2 == 1) {
62
+			System.arraycopy(Utility.integerToTwoBytes(typeToInteger(unkown.elementAt(1))), 0, result, 4, 2);
63
+		}
64
+		return result;
65
+	}
66
+
67
+	public static UnknownAttribute parse(byte[] data) throws MessageAttributeParsingException {
68
+		try {
69
+			UnknownAttribute result = new UnknownAttribute();
70
+			if (data.length % 4 != 0) throw new MessageAttributeParsingException("Data array too short");
71
+			for (int i = 0; i < data.length; i += 4) {
72
+				byte[] temp = new byte[4];
73
+				System.arraycopy(data, i, temp, 0, 4);
74
+				long attri = Utility.fourBytesToLong(temp);
75
+				result.addAttribute(MessageAttribute.intToType(attri));
76
+			}
77
+			return result;
78
+		} catch (UtilityException ue) {
79
+			throw new MessageAttributeParsingException("Parsing error");
80
+		}
81
+	}
82
+}

+ 18
- 0
src/de/javawi/jstun/attribute/UnknownMessageAttributeException.java View File

@@ -0,0 +1,18 @@
1
+package de.javawi.jstun.attribute;
2
+
3
+import de.javawi.jstun.attribute.MessageAttributeInterface.MessageAttributeType;
4
+
5
+public class UnknownMessageAttributeException extends MessageAttributeParsingException {
6
+	private static final long serialVersionUID = 5375193544145543299L;
7
+	
8
+	private MessageAttributeType type;
9
+	
10
+	public UnknownMessageAttributeException(String mesg, MessageAttributeType type) {
11
+		super(mesg);
12
+		this.type = type;
13
+	}
14
+	
15
+	public MessageAttributeType getType() {
16
+		return type;
17
+	}
18
+}

+ 64
- 0
src/de/javawi/jstun/attribute/Username.java View File

@@ -0,0 +1,64 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.attribute;
13
+
14
+import de.javawi.jstun.util.Utility;
15
+import de.javawi.jstun.util.UtilityException;
16
+
17
+public class Username extends MessageAttribute {
18
+	String username;
19
+	
20
+	public Username() {
21
+		super(MessageAttribute.MessageAttributeType.Username);
22
+	}
23
+	
24
+	public Username(String username) {
25
+		super(MessageAttribute.MessageAttributeType.Username);
26
+		setUsername(username);
27
+	}
28
+	
29
+	public String getUsername() {
30
+		return username;
31
+	}
32
+	
33
+	public void setUsername(String username) {
34
+		this.username = username;
35
+	}
36
+	
37
+	public byte[] getBytes() throws UtilityException {
38
+		int length = username.length();
39
+		// username header
40
+		if ((length % 4) != 0) {
41
+			length += 4 - (length % 4);
42
+		}
43
+		// message attribute header
44
+		length += 4;
45
+		byte[] result = new byte[length];
46
+		// message attribute header
47
+		// type
48
+		System.arraycopy(Utility.integerToTwoBytes(typeToInteger(type)), 0, result, 0, 2);
49
+		// length
50
+		System.arraycopy(Utility.integerToTwoBytes(length-4), 0, result, 2, 2);
51
+		
52
+		// username header
53
+		byte[] temp = username.getBytes();
54
+		System.arraycopy(temp, 0, result, 4, temp.length);
55
+		return result;
56
+	}
57
+	
58
+	public static Username parse(byte[] data) {
59
+		Username result = new Username();
60
+		String username = new String(data);
61
+		result.setUsername(username);
62
+		return result;
63
+	}
64
+}

+ 180
- 0
src/de/javawi/jstun/header/MessageHeader.java View File

@@ -0,0 +1,180 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.header;
13
+
14
+import de.javawi.jstun.attribute.*;
15
+import de.javawi.jstun.util.*;
16
+
17
+import java.util.*;
18
+import java.util.logging.*;
19
+
20
+public class MessageHeader implements MessageHeaderInterface {
21
+	/*
22
+	 *  0                   1                   2                   3
23
+     *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
24
+     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
25
+     * |      STUN Message Type        |         Message Length        |
26
+     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
27
+     * |
28
+     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
29
+     *
30
+     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
31
+     *                          Transaction ID
32
+     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
33
+     *                                                                 |
34
+     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
35
+	 */
36
+	private static Logger logger = Logger.getLogger("de.javawi.stun.header.MessageHeader");
37
+	MessageHeaderType type;
38
+	byte[] id = new byte[16];
39
+	
40
+	TreeMap<MessageAttribute.MessageAttributeType, MessageAttribute> ma = new TreeMap<MessageAttribute.MessageAttributeType, MessageAttribute>();
41
+	
42
+	public MessageHeader() {
43
+		super();
44
+	}
45
+	
46
+	public MessageHeader(MessageHeaderType type) {
47
+		super();
48
+		setType(type);
49
+	}
50
+		
51
+    public void setType(MessageHeaderType type) {
52
+		this.type = type;
53
+    }
54
+    
55
+    public MessageHeaderType getType() {
56
+    	return type;
57
+    }
58
+	
59
+	public static int typeToInteger(MessageHeaderType type) {
60
+		if (type == MessageHeaderType.BindingRequest) return BINDINGREQUEST;
61
+		if (type == MessageHeaderType.BindingResponse) return BINDINGRESPONSE;
62
+		if (type == MessageHeaderType.BindingErrorResponse) return BINDINGERRORRESPONSE;
63
+		if (type == MessageHeaderType.SharedSecretRequest) return SHAREDSECRETREQUEST;
64
+		if (type == MessageHeaderType.SharedSecretResponse) return SHAREDSECRETRESPONSE;
65
+		if (type == MessageHeaderType.SharedSecretErrorResponse) return SHAREDSECRETERRORRESPONSE;
66
+		return -1;
67
+	}
68
+	
69
+	public void setTransactionID(byte[] id) {
70
+		System.arraycopy(id, 0, this.id, 0, 16);
71
+	}
72
+	
73
+	public void generateTransactionID() throws UtilityException {
74
+		System.arraycopy(Utility.integerToTwoBytes((int)(Math.random() * 65536)), 0, id, 0, 2);
75
+		System.arraycopy(Utility.integerToTwoBytes((int)(Math.random() * 65536)), 0, id, 2, 2);
76
+		System.arraycopy(Utility.integerToTwoBytes((int)(Math.random() * 65536)), 0, id, 4, 2);
77
+		System.arraycopy(Utility.integerToTwoBytes((int)(Math.random() * 65536)), 0, id, 6, 2);
78
+		System.arraycopy(Utility.integerToTwoBytes((int)(Math.random() * 65536)), 0, id, 8, 2);
79
+		System.arraycopy(Utility.integerToTwoBytes((int)(Math.random() * 65536)), 0, id, 10, 2);
80
+		System.arraycopy(Utility.integerToTwoBytes((int)(Math.random() * 65536)), 0, id, 12, 2);
81
+		System.arraycopy(Utility.integerToTwoBytes((int)(Math.random() * 65536)), 0, id, 14, 2);
82
+	}
83
+	
84
+	public byte[] getTransactionID() {
85
+		byte[] idCopy = new byte[id.length];
86
+		System.arraycopy(id, 0, idCopy, 0, id.length);
87
+		return idCopy;
88
+	}
89
+	
90
+	public boolean equalTransactionID(MessageHeader header) {
91
+		byte[] idHeader = header.getTransactionID();
92
+		if (idHeader.length != 16) return false;
93
+		if ((idHeader[0] == id[0]) && (idHeader[1] == id[1]) && (idHeader[2] == id[2]) && (idHeader[3] == id[3]) && 
94
+			(idHeader[4] == id[4]) && (idHeader[5] == id[5]) && (idHeader[6] == id[6]) && (idHeader[7] == id[7]) && 
95
+			(idHeader[8] == id[8]) && (idHeader[9] == id[9]) && (idHeader[10] == id[10]) && (idHeader[11] == id[11]) &&
96
+			(idHeader[12] == id[12]) && (idHeader[13] == id[13]) && (idHeader[14] == id[14]) && (idHeader[15] == id[15])) {
97
+			return true;
98
+		} else {
99
+			return false;
100
+		}
101
+	}
102
+	
103
+	public void addMessageAttribute(MessageAttribute attri) {
104
+		ma.put(attri.getType(), attri);
105
+	}
106
+	
107
+	public MessageAttribute getMessageAttribute(MessageAttribute.MessageAttributeType type) {
108
+		return ma.get(type);
109
+	}
110
+	
111
+	public byte[] getBytes() throws UtilityException {
112
+		int length = 20;
113
+		Iterator<MessageAttribute.MessageAttributeType> it = ma.keySet().iterator();
114
+		while (it.hasNext()) {
115
+			MessageAttribute attri = ma.get(it.next());
116
+			length += attri.getLength();
117
+		}
118
+		// add attribute size + attributes.getSize();
119
+		byte[] result = new byte[length];
120
+		System.arraycopy(Utility.integerToTwoBytes(typeToInteger(type)), 0, result, 0, 2);
121
+		System.arraycopy(Utility.integerToTwoBytes(length-20), 0, result, 2, 2);
122
+		System.arraycopy(id, 0, result, 4, 16);
123
+		
124
+		// arraycopy of attributes
125
+		int offset = 20;
126
+		it = ma.keySet().iterator();
127
+		while (it.hasNext()) {
128
+			MessageAttribute attri = ma.get(it.next());
129
+			System.arraycopy(attri.getBytes(), 0, result, offset, attri.getLength());
130
+			offset += attri.getLength();
131
+		}
132
+		return result;
133
+	}
134
+	
135
+	public int getLength() throws UtilityException {
136
+		return getBytes().length;
137
+	}
138
+	
139
+	public void parseAttributes(byte[] data) throws MessageAttributeParsingException {
140
+		try {
141
+			byte[] lengthArray = new byte[2];
142
+			System.arraycopy(data, 2, lengthArray, 0, 2);
143
+			int length = Utility.twoBytesToInteger(lengthArray);
144
+			System.arraycopy(data, 4, id, 0, 16);
145
+			byte[] cuttedData;
146
+			int offset = 20;
147
+			while (length > 0) {
148
+				cuttedData = new byte[length];
149
+				System.arraycopy(data, offset, cuttedData, 0, length);
150
+				MessageAttribute ma = MessageAttribute.parseCommonHeader(cuttedData); 
151
+				addMessageAttribute(ma);
152
+				length -= ma.getLength();
153
+				offset += ma.getLength();
154
+			}
155
+		} catch (UtilityException ue) {
156
+			throw new MessageAttributeParsingException("Parsing error");
157
+		}
158
+	}
159
+	
160
+	public static MessageHeader parseHeader(byte[] data) throws MessageHeaderParsingException {
161
+		try {
162
+			MessageHeader mh = new MessageHeader();
163
+			byte[] typeArray = new byte[2];
164
+			System.arraycopy(data, 0, typeArray, 0, 2);
165
+			int type = Utility.twoBytesToInteger(typeArray);
166
+			switch (type) {
167
+			case BINDINGREQUEST: mh.setType(MessageHeaderType.BindingRequest); logger.finer("Binding Request received."); break;
168
+			case BINDINGRESPONSE: mh.setType(MessageHeaderType.BindingResponse); logger.finer("Binding Response received."); break;
169
+			case BINDINGERRORRESPONSE: mh.setType(MessageHeaderType.BindingErrorResponse); logger.finer("Binding Error Response received."); break;
170
+			case SHAREDSECRETREQUEST: mh.setType(MessageHeaderType.SharedSecretRequest); logger.finer("Shared Secret Request received."); break;
171
+			case SHAREDSECRETRESPONSE: mh.setType(MessageHeaderType.SharedSecretResponse); logger.finer("Shared Secret Response received."); break;
172
+			case SHAREDSECRETERRORRESPONSE: mh.setType(MessageHeaderType.SharedSecretErrorResponse); logger.finer("Shared Secret Error Response received.");break;
173
+			default: throw new MessageHeaderParsingException("Message type " + type + "is not supported"); 
174
+			}
175
+			return mh;
176
+		} catch (UtilityException ue) {
177
+			throw new MessageHeaderParsingException("Parsing error");
178
+		}
179
+	}
180
+}

+ 20
- 0
src/de/javawi/jstun/header/MessageHeaderException.java View File

@@ -0,0 +1,20 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.header;
13
+
14
+public class MessageHeaderException extends Exception {
15
+	private static final long serialVersionUID = 3689066248944103737L;
16
+
17
+	public MessageHeaderException(String mesg) {
18
+		super(mesg);
19
+	}
20
+}

+ 22
- 0
src/de/javawi/jstun/header/MessageHeaderInterface.java View File

@@ -0,0 +1,22 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.header;
13
+
14
+public interface MessageHeaderInterface {
15
+	public enum MessageHeaderType { BindingRequest, BindingResponse, BindingErrorResponse, SharedSecretRequest, SharedSecretResponse, SharedSecretErrorResponse };
16
+	final static int BINDINGREQUEST = 0x0001;
17
+	final static int BINDINGRESPONSE = 0x0101;
18
+	final static int BINDINGERRORRESPONSE = 0x0111;
19
+	final static int SHAREDSECRETREQUEST = 0x0002;
20
+	final static int SHAREDSECRETRESPONSE = 0x0102;
21
+	final static int SHAREDSECRETERRORRESPONSE = 0x0112;
22
+}

+ 20
- 0
src/de/javawi/jstun/header/MessageHeaderParsingException.java View File

@@ -0,0 +1,20 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.header;
13
+
14
+public class MessageHeaderParsingException extends MessageHeaderException {
15
+	private static final long serialVersionUID = 3544393617029607478L;
16
+
17
+	public MessageHeaderParsingException(String mesg) {
18
+		super(mesg);
19
+	}
20
+}

+ 192
- 0
src/de/javawi/jstun/test/BindingLifetimeTest.java View File

@@ -0,0 +1,192 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.test;
13
+
14
+import java.util.logging.*;
15
+import java.util.*;
16
+import java.io.*;
17
+import java.net.*;
18
+
19
+import de.javawi.jstun.attribute.*;
20
+import de.javawi.jstun.header.*;
21
+import de.javawi.jstun.util.UtilityException;
22
+
23
+public class BindingLifetimeTest {
24
+	private static Logger logger = Logger.getLogger("de.javawi.stun.test.BindingLifetimeTest");
25
+	String stunServer;
26
+	int port;
27
+	int timeout = 300; //ms
28
+	MappedAddress ma;
29
+	Timer timer;
30
+	DatagramSocket initialSocket;
31
+	
32
+	// start value for binary search - should be carefully choosen
33
+	int upperBinarySearchLifetime = 345000; // ms
34
+	int lowerBinarySearchLifetime = 0;
35
+	int binarySearchLifetime = ( upperBinarySearchLifetime + lowerBinarySearchLifetime ) / 2;
36
+	
37
+	// lifetime value
38
+	int lifetime = -1; // -1 means undefined.
39
+	boolean completed = false;
40
+		
41
+	public BindingLifetimeTest(String stunServer, int port) {
42
+		super();
43
+		this.stunServer = stunServer;
44
+		this.port = port;
45
+		timer = new Timer(true);
46
+	}
47
+	
48
+	public void test() throws UtilityException, SocketException, UnknownHostException, IOException, MessageAttributeParsingException, MessageAttributeException, MessageHeaderParsingException {
49
+		initialSocket = new DatagramSocket();
50
+		initialSocket.connect(InetAddress.getByName(stunServer), port);
51
+		initialSocket.setSoTimeout(timeout);
52
+		
53
+		if (bindingCommunicationInitialSocket()) {
54
+			return;
55
+		}
56
+		BindingLifetimeTask task = new BindingLifetimeTask();
57
+		timer.schedule(task, binarySearchLifetime);
58
+		logger.finer("Timer scheduled initially: " + binarySearchLifetime + ".");
59
+	}
60
+	
61
+	private boolean bindingCommunicationInitialSocket() throws UtilityException, IOException, MessageHeaderParsingException, MessageAttributeParsingException {
62
+		MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest);
63
+		sendMH.generateTransactionID();
64
+		ChangeRequest changeRequest = new ChangeRequest();
65
+		sendMH.addMessageAttribute(changeRequest);
66
+		byte[] data = sendMH.getBytes();
67
+		
68
+		DatagramPacket send = new DatagramPacket(data, data.length, InetAddress.getByName(stunServer), port);
69
+		initialSocket.send(send);
70
+		logger.finer("Binding Request sent.");
71
+	
72
+		MessageHeader receiveMH = new MessageHeader();
73
+		while (!(receiveMH.equalTransactionID(sendMH))) {
74
+			DatagramPacket receive = new DatagramPacket(new byte[200], 200);
75
+			initialSocket.receive(receive);
76
+			receiveMH = MessageHeader.parseHeader(receive.getData());
77
+			receiveMH.parseAttributes(receive.getData());
78
+		}
79
+		ma = (MappedAddress) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.MappedAddress);
80
+		ErrorCode ec = (ErrorCode) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.ErrorCode);
81
+		if (ec != null) {
82
+			logger.config("Message header contains an Errorcode message attribute.");
83
+			return true;
84
+		}
85
+		if (ma == null) {
86
+			logger.config("Response does not contain a Mapped Address message attribute.");
87
+			return true;
88
+		}
89
+		return false;
90
+	}
91
+	
92
+	public int getLifetime() {
93
+		return lifetime;
94
+	}
95
+	
96
+	public boolean isCompleted() {
97
+		return completed;
98
+	}
99
+	
100
+	public void setUpperBinarySearchLifetime(int upperBinarySearchLifetime) {
101
+		this.upperBinarySearchLifetime = upperBinarySearchLifetime;
102
+		binarySearchLifetime = ( upperBinarySearchLifetime + lowerBinarySearchLifetime ) / 2;
103
+	}
104
+	
105
+	class BindingLifetimeTask extends TimerTask {
106
+		
107
+		public BindingLifetimeTask() {
108
+			super();
109
+		}
110
+		
111
+		public void run() {
112
+			try {
113
+				lifetimeQuery();
114
+			} catch (Exception e) {
115
+				logger.config("Unhandled Exception. BindLifetimeTasks stopped.");
116
+				e.printStackTrace();
117
+			}
118
+		}
119
+		
120
+		public void lifetimeQuery() throws UtilityException, MessageAttributeException, MessageHeaderParsingException, MessageAttributeParsingException, IOException {
121
+			try {
122
+				DatagramSocket socket = new DatagramSocket();
123
+				socket.connect(InetAddress.getByName(stunServer), port);
124
+				socket.setSoTimeout(timeout);
125
+			
126
+				MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest);
127
+				sendMH.generateTransactionID();
128
+				ChangeRequest changeRequest = new ChangeRequest();
129
+				ResponseAddress responseAddress = new ResponseAddress();
130
+				responseAddress.setAddress(ma.getAddress());
131
+				responseAddress.setPort(ma.getPort());
132
+				sendMH.addMessageAttribute(changeRequest);
133
+				sendMH.addMessageAttribute(responseAddress);
134
+				byte[] data = sendMH.getBytes();
135
+			
136
+				DatagramPacket send = new DatagramPacket(data, data.length, InetAddress.getByName(stunServer), port);
137
+				socket.send(send);
138
+				logger.finer("Binding Request sent.");
139
+		
140
+				MessageHeader receiveMH = new MessageHeader();
141
+				while (!(receiveMH.equalTransactionID(sendMH))) {
142
+					DatagramPacket receive = new DatagramPacket(new byte[200], 200);
143
+					initialSocket.receive(receive);
144
+					receiveMH = MessageHeader.parseHeader(receive.getData());
145
+					receiveMH.parseAttributes(receive.getData());
146
+				}
147
+				ErrorCode ec = (ErrorCode) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.ErrorCode);
148
+				if (ec != null) {
149
+					logger.config("Message header contains errorcode message attribute.");
150
+					return;
151
+				}
152
+				logger.finer("Binding Response received.");
153
+				if (upperBinarySearchLifetime == (lowerBinarySearchLifetime + 1)) {
154
+					logger.config("BindingLifetimeTest completed. UDP binding lifetime: " + binarySearchLifetime + ".");
155
+					completed = true;
156
+					return;
157
+				}
158
+				lifetime = binarySearchLifetime;
159
+				logger.finer("Lifetime update: " + lifetime + ".");
160
+				lowerBinarySearchLifetime = binarySearchLifetime;
161
+				binarySearchLifetime = ( upperBinarySearchLifetime + lowerBinarySearchLifetime ) / 2;
162
+				if (binarySearchLifetime > 0) {
163
+					BindingLifetimeTask task = new BindingLifetimeTask();
164
+					timer.schedule(task, binarySearchLifetime);
165
+					logger.finer("Timer scheduled: " + binarySearchLifetime + ".");
166
+				} else {
167
+					completed = true;
168
+				}
169
+			} catch (SocketTimeoutException ste) {
170
+				logger.finest("Read operation at query socket timeout.");
171
+				if (upperBinarySearchLifetime == (lowerBinarySearchLifetime + 1)) {
172
+					logger.config("BindingLifetimeTest completed. UDP binding lifetime: " + binarySearchLifetime + ".");
173
+					completed = true;
174
+					return;
175
+				}
176
+				upperBinarySearchLifetime = binarySearchLifetime;
177
+				binarySearchLifetime = ( upperBinarySearchLifetime + lowerBinarySearchLifetime ) / 2;
178
+				if (binarySearchLifetime > 0) {
179
+					if (bindingCommunicationInitialSocket()) {
180
+						return;
181
+					}
182
+					BindingLifetimeTask task = new BindingLifetimeTask();
183
+					timer.schedule(task, binarySearchLifetime);
184
+					logger.finer("Timer scheduled: " + binarySearchLifetime + ".");
185
+				} else {
186
+					completed = true;
187
+				}
188
+			}
189
+		}
190
+	}
191
+}
192
+

+ 153
- 0
src/de/javawi/jstun/test/DiscoveryInfo.java View File

@@ -0,0 +1,153 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.test;
13
+
14
+import java.net.*;
15
+
16
+public class DiscoveryInfo {
17
+	private InetAddress testIP;
18
+	private boolean error = false;
19
+	private int errorResponseCode = 0;
20
+	private String errorReason;
21
+	private boolean openAccess = false;
22
+	private boolean blockedUDP = false;
23
+	private boolean fullCone = false;
24
+	private boolean restrictedCone = false;
25
+	private boolean portRestrictedCone = false;
26
+	private boolean symmetric = false;
27
+	private boolean symmetricUDPFirewall = false;
28
+	private InetAddress publicIP;
29
+	
30
+	public DiscoveryInfo(InetAddress testIP) {
31
+		this.testIP = testIP;
32
+	}
33
+	
34
+	public boolean isError() {
35
+		return error;
36
+	}
37
+	
38
+	public void setError(int responseCode, String reason) {
39
+		this.error = true;
40
+		this.errorResponseCode = responseCode;
41
+		this.errorReason = reason;
42
+	}
43
+	
44
+	public boolean isOpenAccess() {
45
+		if (error) return false;
46
+		return openAccess;
47
+	}
48
+
49
+	public void setOpenAccess() {
50
+		this.openAccess = true;
51
+	}
52
+
53
+	public boolean isBlockedUDP() {
54
+		if (error) return false;
55
+		return blockedUDP;
56
+	}
57
+
58
+	public void setBlockedUDP() {
59
+		this.blockedUDP = true;
60
+	}
61
+	
62
+	public boolean isFullCone() {
63
+		if (error) return false;
64
+		return fullCone;
65
+	}
66
+
67
+	public void setFullCone() {
68
+		this.fullCone = true;
69
+	}
70
+
71
+	public boolean isPortRestrictedCone() {
72
+		if (error) return false;
73
+		return portRestrictedCone;
74
+	}
75
+
76
+	public void setPortRestrictedCone() {
77
+		this.portRestrictedCone = true;
78
+	}
79
+
80
+	public boolean isRestrictedCone() {
81
+		if (error) return false;
82
+		return restrictedCone;
83
+	}
84
+
85
+	public void setRestrictedCone() {
86
+		this.restrictedCone = true;
87
+	}
88
+
89
+	public boolean isSymmetric() {
90
+		if (error) return false;
91
+		return symmetric;
92
+	}
93
+
94
+	public void setSymmetric() {
95
+		this.symmetric = true;
96
+	}
97
+
98
+	public boolean isSymmetricUDPFirewall() {
99
+		if (error) return false;
100
+		return symmetricUDPFirewall;
101
+	}
102
+
103
+	public void setSymmetricUDPFirewall() {
104
+		this.symmetricUDPFirewall = true;
105
+	}
106
+	
107
+	public InetAddress getPublicIP() {
108
+		return publicIP;
109
+	}
110
+	
111
+	public InetAddress getLocalIP() {
112
+		return testIP;
113
+	}
114
+	
115
+	public void setPublicIP(InetAddress publicIP) {
116
+		this.publicIP = publicIP;
117
+	}
118
+	
119
+	public String toString() {
120
+		StringBuffer sb = new StringBuffer();
121
+		sb.append("Network interface: ");
122
+		try {
123
+			sb.append(NetworkInterface.getByInetAddress(testIP).getName());
124
+		} catch (SocketException se) {
125
+			sb.append("unknown");
126
+		}
127
+		sb.append("\n");
128
+		sb.append("Local IP address: ");
129
+		sb.append(testIP.getHostAddress());
130
+		sb.append("\n");
131
+		if (error) {
132
+			sb.append(errorReason + " - Responsecode: " + errorResponseCode);
133
+			return sb.toString();
134
+		}
135
+		sb.append("Result: ");
136
+		if (openAccess) sb.append("Open access to the Internet.\n");
137
+		if (blockedUDP) sb.append("Firewall blocks UDP.\n");
138
+		if (fullCone) sb.append("Full Cone NAT handles connections.\n");
139
+		if (restrictedCone) sb.append("Restricted Cone NAT handles connections.\n");
140
+		if (portRestrictedCone) sb.append("Port restricted Cone NAT handles connections.\n");
141
+		if (symmetric) sb.append("Symmetric Cone NAT handles connections.\n");
142
+		if (symmetricUDPFirewall) sb.append ("Symmetric UDP Firewall handles connections.\n");
143
+		if (!openAccess && !blockedUDP && !fullCone && !restrictedCone && !portRestrictedCone && !symmetric && !symmetricUDPFirewall) sb.append("unkown\n");
144
+		sb.append("Public IP address: ");
145
+		if (publicIP != null) {
146
+			sb.append(publicIP.getHostAddress());
147
+		} else {
148
+			sb.append("unknown");
149
+		}
150
+		sb.append("\n");
151
+		return sb.toString();
152
+	}	
153
+}

+ 347
- 0
src/de/javawi/jstun/test/DiscoveryTest.java View File

@@ -0,0 +1,347 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.test;
13
+
14
+import java.io.IOException;
15
+import java.net.DatagramPacket;
16
+import java.net.DatagramSocket;
17
+import java.net.InetAddress;
18
+import java.net.InetSocketAddress;
19
+import java.net.SocketException;
20
+import java.net.SocketTimeoutException;
21
+import java.net.UnknownHostException;
22
+import java.util.logging.Logger;
23
+
24
+import de.javawi.jstun.attribute.ChangeRequest;
25
+import de.javawi.jstun.attribute.ChangedAddress;
26
+import de.javawi.jstun.attribute.ErrorCode;
27
+import de.javawi.jstun.attribute.MappedAddress;
28
+import de.javawi.jstun.attribute.MessageAttribute;
29
+import de.javawi.jstun.attribute.MessageAttributeException;
30
+import de.javawi.jstun.attribute.MessageAttributeParsingException;
31
+import de.javawi.jstun.header.MessageHeader;
32
+import de.javawi.jstun.header.MessageHeaderParsingException;
33
+import de.javawi.jstun.util.UtilityException;
34
+
35
+public class DiscoveryTest {
36
+	private static Logger logger = Logger.getLogger("de.javawi.stun.test.DiscoveryTest");
37
+	InetAddress iaddress;
38
+	String stunServer;
39
+	int port;
40
+	int timeoutInitValue = 300; //ms
41
+	MappedAddress ma = null;
42
+	ChangedAddress ca = null;
43
+	boolean nodeNatted = true;
44
+	DatagramSocket socketTest1 = null;
45
+	DiscoveryInfo di = null;
46
+	
47
+	public DiscoveryTest(InetAddress iaddress , String stunServer, int port) {
48
+		super();
49
+		this.iaddress = iaddress;
50
+		this.stunServer = stunServer;
51
+		this.port = port;
52
+	}
53
+		
54
+	public DiscoveryInfo test() throws UtilityException, SocketException, UnknownHostException, IOException, MessageAttributeParsingException, MessageAttributeException, MessageHeaderParsingException{
55
+		ma = null;
56
+		ca = null;
57
+		nodeNatted = true;
58
+		socketTest1 = null;
59
+		di = new DiscoveryInfo(iaddress);
60
+		
61
+		if (test1()) {
62
+			if (test2()) {
63
+				if (test1Redo()) {
64
+					test3();
65
+				}
66
+			}
67
+		}
68
+		
69
+		socketTest1.close();
70
+		
71
+		return di;
72
+	}
73
+	
74
+	private boolean test1() throws UtilityException, SocketException, UnknownHostException, IOException, MessageAttributeParsingException, MessageHeaderParsingException {
75
+		int timeSinceFirstTransmission = 0;
76
+		int timeout = timeoutInitValue;
77
+		while (true) {
78
+			try {
79
+				// Test 1 including response
80
+				socketTest1 = new DatagramSocket(new InetSocketAddress(iaddress, 0));
81
+				socketTest1.setReuseAddress(true);
82
+				socketTest1.connect(InetAddress.getByName(stunServer), port);
83
+				socketTest1.setSoTimeout(timeout);
84
+				
85
+				MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest);
86
+				sendMH.generateTransactionID();
87
+				
88
+				ChangeRequest changeRequest = new ChangeRequest();
89
+				sendMH.addMessageAttribute(changeRequest);
90
+				
91
+				byte[] data = sendMH.getBytes();
92
+				DatagramPacket send = new DatagramPacket(data, data.length);
93
+				socketTest1.send(send);
94
+				logger.finer("Test 1: Binding Request sent.");
95
+			
96
+				MessageHeader receiveMH = new MessageHeader();
97
+				while (!(receiveMH.equalTransactionID(sendMH))) {
98
+					DatagramPacket receive = new DatagramPacket(new byte[200], 200);
99
+					socketTest1.receive(receive);
100
+					receiveMH = MessageHeader.parseHeader(receive.getData());
101
+					receiveMH.parseAttributes(receive.getData());
102
+				}
103
+				
104
+				ma = (MappedAddress) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.MappedAddress);
105
+				ca = (ChangedAddress) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.ChangedAddress);
106
+				ErrorCode ec = (ErrorCode) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.ErrorCode);
107
+				if (ec != null) {
108
+					di.setError(ec.getResponseCode(), ec.getReason());
109
+					logger.config("Message header contains an Errorcode message attribute.");
110
+					return false;
111
+				}
112
+				if ((ma == null) || (ca == null)) {
113
+					di.setError(700, "The server is sending an incomplete response (Mapped Address and Changed Address message attributes are missing). The client should not retry.");
114
+					logger.config("Response does not contain a Mapped Address or Changed Address message attribute.");
115
+					return false;
116
+				} else {
117
+					di.setPublicIP(ma.getAddress().getInetAddress());
118
+					if ((ma.getPort() == socketTest1.getLocalPort()) && (ma.getAddress().getInetAddress().equals(socketTest1.getLocalAddress()))) {
119
+						logger.fine("Node is not natted.");
120
+						nodeNatted = false;
121
+					} else {
122
+						logger.fine("Node is natted.");
123
+					}
124
+					return true;
125
+				}
126
+			} catch (SocketTimeoutException ste) {
127
+				if (timeSinceFirstTransmission < 7900) {
128
+					logger.finer("Test 1: Socket timeout while receiving the response.");
129
+					timeSinceFirstTransmission += timeout;
130
+					int timeoutAddValue = (timeSinceFirstTransmission * 2);
131
+					if (timeoutAddValue > 1600) timeoutAddValue = 1600;
132
+					timeout = timeoutAddValue;
133
+				} else {
134
+					// node is not capable of udp communication
135
+					logger.finer("Test 1: Socket timeout while receiving the response. Maximum retry limit exceed. Give up.");
136
+					di.setBlockedUDP();
137
+					logger.fine("Node is not capable of UDP communication.");
138
+					return false;
139
+				}
140
+			} 
141
+		}
142
+	}
143
+		
144
+	private boolean test2() throws UtilityException, SocketException, UnknownHostException, IOException, MessageAttributeParsingException, MessageAttributeException, MessageHeaderParsingException {
145
+		int timeSinceFirstTransmission = 0;
146
+		int timeout = timeoutInitValue;
147
+		while (true) {
148
+			try {
149
+				// Test 2 including response
150
+				DatagramSocket sendSocket = new DatagramSocket(new InetSocketAddress(iaddress, 0));
151
+				sendSocket.connect(InetAddress.getByName(stunServer), port);
152
+				sendSocket.setSoTimeout(timeout);
153
+				
154
+				MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest);
155
+				sendMH.generateTransactionID();
156
+				
157
+				ChangeRequest changeRequest = new ChangeRequest();
158
+				changeRequest.setChangeIP();
159
+				changeRequest.setChangePort();
160
+				sendMH.addMessageAttribute(changeRequest);
161
+					 
162
+				byte[] data = sendMH.getBytes(); 
163
+				DatagramPacket send = new DatagramPacket(data, data.length);
164
+				sendSocket.send(send);
165
+				logger.finer("Test 2: Binding Request sent.");
166
+				
167
+				int localPort = sendSocket.getLocalPort();
168
+				InetAddress localAddress = sendSocket.getLocalAddress();
169
+				
170
+				sendSocket.close();
171
+				
172
+				DatagramSocket receiveSocket = new DatagramSocket(localPort, localAddress);
173
+				receiveSocket.connect(ca.getAddress().getInetAddress(), ca.getPort());
174
+				receiveSocket.setSoTimeout(timeout);
175
+				
176
+				MessageHeader receiveMH = new MessageHeader();
177
+				while(!(receiveMH.equalTransactionID(sendMH))) {
178
+					DatagramPacket receive = new DatagramPacket(new byte[200], 200);
179
+					receiveSocket.receive(receive);
180
+					receiveMH = MessageHeader.parseHeader(receive.getData());
181
+					receiveMH.parseAttributes(receive.getData());
182
+				}
183
+				ErrorCode ec = (ErrorCode) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.ErrorCode);
184
+				if (ec != null) {
185
+					di.setError(ec.getResponseCode(), ec.getReason());
186
+					logger.config("Message header contains an Errorcode message attribute.");
187
+					return false;
188
+				}
189
+				if (!nodeNatted) {
190
+					di.setOpenAccess();
191
+					logger.fine("Node has open access to the Internet (or, at least the node is behind a full-cone NAT without translation).");
192
+				} else {
193
+					di.setFullCone();
194
+					logger.fine("Node is behind a full-cone NAT.");
195
+				}
196
+				return false;
197
+			} catch (SocketTimeoutException ste) {
198
+				if (timeSinceFirstTransmission < 7900) {
199
+					logger.finer("Test 2: Socket timeout while receiving the response.");
200
+					timeSinceFirstTransmission += timeout;
201
+					int timeoutAddValue = (timeSinceFirstTransmission * 2);
202
+					if (timeoutAddValue > 1600) timeoutAddValue = 1600;
203
+					timeout = timeoutAddValue;
204
+				} else {
205
+					logger.finer("Test 2: Socket timeout while receiving the response. Maximum retry limit exceed. Give up.");
206
+					if (!nodeNatted) {
207
+						di.setSymmetricUDPFirewall();
208
+						logger.fine("Node is behind a symmetric UDP firewall.");
209
+						return false;
210
+					} else {
211
+						// not is natted
212
+						// redo test 1 with address and port as offered in the changed-address message attribute
213
+						return true;
214
+					}
215
+				}
216
+			}
217
+		}
218
+	}
219
+	
220
+	private boolean test1Redo() throws UtilityException, SocketException, UnknownHostException, IOException, MessageAttributeParsingException, MessageHeaderParsingException{
221
+		int timeSinceFirstTransmission = 0;
222
+		int timeout = timeoutInitValue;
223
+		while (true) {
224
+			// redo test 1 with address and port as offered in the changed-address message attribute
225
+			try {
226
+				// Test 1 with changed port and address values
227
+				socketTest1.connect(ca.getAddress().getInetAddress(), ca.getPort());
228
+				socketTest1.setSoTimeout(timeout);
229
+				
230
+				MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest);
231
+				sendMH.generateTransactionID();
232
+				
233
+				ChangeRequest changeRequest = new ChangeRequest();
234
+				sendMH.addMessageAttribute(changeRequest);
235
+				
236
+				byte[] data = sendMH.getBytes();
237
+				DatagramPacket send = new DatagramPacket(data, data.length);
238
+				socketTest1.send(send);
239
+				logger.finer("Test 1 redo with changed address: Binding Request sent.");
240
+				
241
+				MessageHeader receiveMH = new MessageHeader();
242
+				while (!(receiveMH.equalTransactionID(sendMH))) {
243
+					DatagramPacket receive = new DatagramPacket(new byte[200], 200);
244
+					socketTest1.receive(receive);
245
+					receiveMH = MessageHeader.parseHeader(receive.getData());
246
+					receiveMH.parseAttributes(receive.getData());
247
+				}
248
+				MappedAddress ma2 = (MappedAddress) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.MappedAddress);
249
+				ErrorCode ec = (ErrorCode) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.ErrorCode);
250
+				if (ec != null) {
251
+					di.setError(ec.getResponseCode(), ec.getReason());
252
+					logger.config("Message header contains an Errorcode message attribute.");
253
+					return false;
254
+				}
255
+				if (ma2 == null) {
256
+					di.setError(700, "The server is sending an incomplete response (Mapped Address message attribute is missing). The client should not retry.");
257
+					logger.config("Response does not contain a Mapped Address message attribute.");
258
+					return false;
259
+				} else {
260
+					if ((ma.getPort() != ma2.getPort()) || (!(ma.getAddress().getInetAddress().equals(ma2.getAddress().getInetAddress())))) {
261
+						di.setSymmetric();
262
+						logger.fine("Node is behind a symmetric NAT.");
263
+						return false;
264
+					}
265
+				}
266
+				return true;
267
+			} catch (SocketTimeoutException ste2) {
268
+				if (timeSinceFirstTransmission < 7900) {
269
+					logger.config("Test 1 redo with changed address: Socket timeout while receiving the response.");
270
+					timeSinceFirstTransmission += timeout;
271
+					int timeoutAddValue = (timeSinceFirstTransmission * 2);
272
+					if (timeoutAddValue > 1600) timeoutAddValue = 1600;
273
+					timeout = timeoutAddValue;
274
+				} else {
275
+					logger.config("Test 1 redo with changed address: Socket timeout while receiving the response.  Maximum retry limit exceed. Give up.");
276
+					return false;
277
+				}
278
+			}
279
+		}
280
+	}
281
+	
282
+	private void test3() throws UtilityException, SocketException, UnknownHostException, IOException, MessageAttributeParsingException, MessageAttributeException, MessageHeaderParsingException {
283
+		int timeSinceFirstTransmission = 0;
284
+		int timeout = timeoutInitValue;
285
+		while (true) {
286
+			try {
287
+				// Test 3 including response
288
+				DatagramSocket sendSocket = new DatagramSocket(new InetSocketAddress(iaddress, 0));
289
+				sendSocket.connect(InetAddress.getByName(stunServer), port);
290
+				sendSocket.setSoTimeout(timeout);
291
+				
292
+				MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest);
293
+				sendMH.generateTransactionID();
294
+				
295
+				ChangeRequest changeRequest = new ChangeRequest();
296
+				changeRequest.setChangePort();
297
+				sendMH.addMessageAttribute(changeRequest);
298
+				
299
+				byte[] data = sendMH.getBytes();
300
+				DatagramPacket send = new DatagramPacket(data, data.length);
301
+				sendSocket.send(send);
302
+				logger.finer("Test 3: Binding Request sent.");
303
+				
304
+				int localPort = sendSocket.getLocalPort();
305
+				InetAddress localAddress = sendSocket.getLocalAddress();
306
+				
307
+				sendSocket.close();
308
+				
309
+				DatagramSocket receiveSocket = new DatagramSocket(localPort, localAddress);
310
+				receiveSocket.connect(InetAddress.getByName(stunServer), ca.getPort());
311
+				receiveSocket.setSoTimeout(timeout);
312
+				
313
+				MessageHeader receiveMH = new MessageHeader();
314
+				while (!(receiveMH.equalTransactionID(sendMH))) {
315
+					DatagramPacket receive = new DatagramPacket(new byte[200], 200);
316
+					receiveSocket.receive(receive);
317
+					receiveMH = MessageHeader.parseHeader(receive.getData());
318
+					receiveMH.parseAttributes(receive.getData());
319
+				}
320
+				ErrorCode ec = (ErrorCode) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.ErrorCode);
321
+				if (ec != null) {
322
+					di.setError(ec.getResponseCode(), ec.getReason());
323
+					logger.config("Message header contains an Errorcode message attribute.");
324
+					return;
325
+				}
326
+				if (nodeNatted) {
327
+					di.setRestrictedCone();
328
+					logger.fine("Node is behind a restricted NAT.");
329
+					return;
330
+				}
331
+			} catch (SocketTimeoutException ste) {
332
+				if (timeSinceFirstTransmission < 7900) {
333
+					logger.finer("Test 3: Socket timeout while receiving the response.");
334
+					timeSinceFirstTransmission += timeout;
335
+					int timeoutAddValue = (timeSinceFirstTransmission * 2);
336
+					if (timeoutAddValue > 1600) timeoutAddValue = 1600;
337
+					timeout = timeoutAddValue;
338
+				} else {
339
+					logger.finer("Test 3: Socket timeout while receiving the response. Maximum retry limit exceed. Give up.");
340
+					di.setPortRestrictedCone();
341
+					logger.fine("Node is behind a port restricted NAT.");
342
+					return;
343
+				}
344
+			}
345
+		}
346
+	}
347
+}

+ 73
- 0
src/de/javawi/jstun/test/demo/.#DiscoveryTestDemo.java.1.3 View File

@@ -0,0 +1,73 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.test.demo;
13
+
14
+import java.net.BindException;
15
+import java.net.InetAddress;
16
+import java.net.NetworkInterface;
17
+import java.util.Enumeration;
18
+import java.util.logging.FileHandler;
19
+import java.util.logging.Handler;
20
+import java.util.logging.Level;
21
+import java.util.logging.Logger;
22
+import java.util.logging.SimpleFormatter;
23
+
24
+import de.javawi.jstun.test.DiscoveryTest;
25
+
26
+public class DiscoveryTestDemo implements Runnable {
27
+	InetAddress iaddress;
28
+	
29
+	public DiscoveryTestDemo(InetAddress iaddress) {
30
+		this.iaddress = iaddress;
31
+	}
32
+	
33
+	public void run() {
34
+		try {
35
+			DiscoveryTest test = new DiscoveryTest(iaddress, "stun.sipgate.net", 10000);
36
+			//DiscoveryTest test = new DiscoveryTest(iaddress, "stun.sipgate.net", 10000);
37
+			// iphone-stun.freenet.de:3478
38
+			// larry.gloo.net:3478
39
+			// stun.xten.net:3478
40
+			// stun.sipgate.net:10000
41
+			System.out.println(test.test());
42
+		} catch (BindException be) {
43
+			System.out.println(iaddress.toString() + ": " + be.getMessage());
44
+		} catch (Exception e) {
45
+			System.out.println(e.getMessage());
46
+			e.printStackTrace();
47
+		}
48
+	}
49
+	
50
+	public static void main(String[] args) {
51
+		try {
52
+			Handler fh = new FileHandler("logging.txt");
53
+			fh.setFormatter(new SimpleFormatter());
54
+			Logger.getLogger("de.javawi.stun").addHandler(fh);
55
+			Logger.getLogger("de.javawi.stun").setLevel(Level.ALL);
56
+
57
+			Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces();
58
+			while (ifaces.hasMoreElements()) {
59
+				NetworkInterface iface = ifaces.nextElement();
60
+				Enumeration<InetAddress> iaddresses = iface.getInetAddresses();
61
+				while (iaddresses.hasMoreElements()) {
62
+					InetAddress iaddress = iaddresses.nextElement();
63
+					if (!iaddress.isLoopbackAddress() && !iaddress.isLinkLocalAddress()) {
64
+						Thread thread = new Thread(new DiscoveryTestDemo(iaddress));
65
+						thread.start();
66
+					}
67
+				}
68
+			}
69
+		} catch (Exception e) {
70
+			System.out.println(e.getMessage());
71
+		}
72
+	}
73
+}

+ 73
- 0
src/de/javawi/jstun/test/demo/.#DiscoveryTestDemo.java.1.7 View File

@@ -0,0 +1,73 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.test.demo;
13
+
14
+import java.net.BindException;
15
+import java.net.InetAddress;
16
+import java.net.NetworkInterface;
17
+import java.util.Enumeration;
18
+import java.util.logging.FileHandler;
19
+import java.util.logging.Handler;
20
+import java.util.logging.Level;
21
+import java.util.logging.Logger;
22
+import java.util.logging.SimpleFormatter;
23
+
24
+import de.javawi.jstun.test.DiscoveryTest;
25
+
26
+public class DiscoveryTestDemo implements Runnable {
27
+	InetAddress iaddress;
28
+	
29
+	public DiscoveryTestDemo(InetAddress iaddress) {
30
+		this.iaddress = iaddress;
31
+	}
32
+	
33
+	public void run() {
34
+		try {
35
+			DiscoveryTest test = new DiscoveryTest(iaddress, "jstun.javawi.de", 3478);
36
+			//DiscoveryTest test = new DiscoveryTest(iaddress, "stun.sipgate.net", 10000);
37
+			// iphone-stun.freenet.de:3478
38
+			// larry.gloo.net:3478
39
+			// stun.xten.net:3478
40
+			// stun.sipgate.net:10000
41
+			System.out.println(test.test());
42
+		} catch (BindException be) {
43
+			System.out.println(iaddress.toString() + ": " + be.getMessage());
44
+		} catch (Exception e) {
45
+			System.out.println(e.getMessage());
46
+			e.printStackTrace();
47
+		}
48
+	}
49
+	
50
+	public static void main(String args[]) {
51
+		try {
52
+			Handler fh = new FileHandler("logging.txt");
53
+			fh.setFormatter(new SimpleFormatter());
54
+			Logger.getLogger("de.javawi.stun").addHandler(fh);
55
+			Logger.getLogger("de.javawi.stun").setLevel(Level.ALL);
56
+
57
+			Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces();
58
+			while (ifaces.hasMoreElements()) {
59
+				NetworkInterface iface = ifaces.nextElement();
60
+				Enumeration<InetAddress> iaddresses = iface.getInetAddresses();
61
+				while (iaddresses.hasMoreElements()) {
62
+					InetAddress iaddress = iaddresses.nextElement();
63
+					if (!iaddress.isLoopbackAddress() && !iaddress.isLinkLocalAddress()) {
64
+						Thread thread = new Thread(new DiscoveryTestDemo(iaddress));
65
+						thread.start();
66
+					}
67
+				}
68
+			}
69
+		} catch (Exception e) {
70
+			System.out.println(e.getMessage());
71
+		}
72
+	}
73
+}

+ 47
- 0
src/de/javawi/jstun/test/demo/BindingLifetimeTestDemo.java View File

@@ -0,0 +1,47 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.test.demo;
13
+
14
+import java.util.logging.FileHandler;
15
+import java.util.logging.Handler;
16
+import java.util.logging.Level;
17
+import java.util.logging.Logger;
18
+import java.util.logging.SimpleFormatter;
19
+
20
+import de.javawi.jstun.test.BindingLifetimeTest;
21
+
22
+public class BindingLifetimeTestDemo {
23
+	public static void main(String args[]) {
24
+		try {
25
+			Handler fh = new FileHandler("logging.txt");
26
+			fh.setFormatter(new SimpleFormatter());
27
+			Logger.getLogger("de.javawi.stun").addHandler(fh);
28
+			Logger.getLogger("de.javawi.stun").setLevel(Level.ALL);
29
+			BindingLifetimeTest test = new BindingLifetimeTest("iphone-stun.freenet.de", 3478);
30
+			// iphone-stun.freenet.de:3478
31
+			// larry.gloo.net:3478
32
+			// stun.xten.net:3478
33
+			test.test();
34
+			boolean continueWhile = true;
35
+			while(continueWhile) {
36
+				Thread.sleep(5000);
37
+				if (test.getLifetime() != -1) {
38
+					System.out.println("Lifetime: " + test.getLifetime() + " Finished: " + test.isCompleted());
39
+					if (test.isCompleted()) continueWhile = false;
40
+				}
41
+			}
42
+		} catch (Exception e) {
43
+			System.out.println(e.getMessage());
44
+			e.printStackTrace();
45
+		}
46
+	}
47
+}

+ 75
- 0
src/de/javawi/jstun/test/demo/DiscoveryTestDemo.java View File

@@ -0,0 +1,75 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.test.demo;
13
+
14
+import java.net.BindException;
15
+import java.net.InetAddress;
16
+import java.net.NetworkInterface;
17
+import java.util.Enumeration;
18
+import java.util.logging.FileHandler;
19
+import java.util.logging.Handler;
20
+import java.util.logging.Level;
21
+import java.util.logging.Logger;
22
+import java.util.logging.SimpleFormatter;
23
+
24
+import de.javawi.jstun.test.DiscoveryTest;
25
+
26
+public class DiscoveryTestDemo implements Runnable {
27
+	InetAddress iaddress;
28
+	
29
+	public DiscoveryTestDemo(InetAddress iaddress) {
30
+		this.iaddress = iaddress;
31
+	}
32
+	
33
+	public void run() {
34
+		try {
35
+			DiscoveryTest test = new DiscoveryTest(iaddress, "jstun.javawi.de", 3478);
36
+			//DiscoveryTest test = new DiscoveryTest(iaddress, "stun.sipgate.net", 10000);
37
+			// iphone-stun.freenet.de:3478
38
+			// larry.gloo.net:3478
39
+			// stun.xten.net:3478
40
+			// stun.sipgate.net:10000
41
+			System.out.println(test.test());
42
+		} catch (BindException be) {
43
+			System.out.println(iaddress.toString() + ": " + be.getMessage());
44
+		} catch (Exception e) {
45
+			System.out.println(e.getMessage());
46
+			e.printStackTrace();
47
+		}
48
+	}
49
+	
50
+	public static void main(String args[]) {
51
+		try {
52
+			Handler fh = new FileHandler("logging.txt");
53
+			fh.setFormatter(new SimpleFormatter());
54
+			Logger.getLogger("de.javawi.stun").addHandler(fh);
55
+			Logger.getLogger("de.javawi.stun").setLevel(Level.ALL);
56
+
57
+			Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces();
58
+			while (ifaces.hasMoreElements()) {
59
+				NetworkInterface iface = ifaces.nextElement();
60
+				Enumeration<InetAddress> iaddresses = iface.getInetAddresses();
61
+				while (iaddresses.hasMoreElements()) {
62
+					InetAddress iaddress = iaddresses.nextElement();
63
+					if (Class.forName("java.net.Inet4Address").isInstance(iaddress)) {
64
+						if ((!iaddress.isLoopbackAddress()) && (!iaddress.isLinkLocalAddress())) {
65
+							Thread thread = new Thread(new DiscoveryTestDemo(iaddress));
66
+							thread.start();
67
+						}
68
+					}
69
+				}
70
+			}
71
+		} catch (Exception e) {
72
+			System.out.println(e.getMessage());
73
+		}
74
+	}
75
+}

+ 263
- 0
src/de/javawi/jstun/test/demo/StunServer.java View File

@@ -0,0 +1,263 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.test.demo;
13
+
14
+import java.io.IOException;
15
+import java.net.DatagramPacket;
16
+import java.net.DatagramSocket;
17
+import java.net.InetAddress;
18
+import java.net.SocketException;
19
+import java.net.UnknownHostException;
20
+import java.util.Vector;
21
+import java.util.logging.FileHandler;
22
+import java.util.logging.Handler;
23
+import java.util.logging.Level;
24
+import java.util.logging.Logger;
25
+import java.util.logging.SimpleFormatter;
26
+
27
+import de.javawi.jstun.attribute.ChangeRequest;
28
+import de.javawi.jstun.attribute.ChangedAddress;
29
+import de.javawi.jstun.attribute.MappedAddress;
30
+import de.javawi.jstun.attribute.MessageAttributeException;
31
+import de.javawi.jstun.attribute.MessageAttributeParsingException;
32
+import de.javawi.jstun.attribute.ResponseAddress;
33
+import de.javawi.jstun.attribute.SourceAddress;
34
+import de.javawi.jstun.attribute.UnknownAttribute;
35
+import de.javawi.jstun.attribute.UnknownMessageAttributeException;
36
+import de.javawi.jstun.attribute.MessageAttributeInterface.MessageAttributeType;
37
+import de.javawi.jstun.header.MessageHeader;
38
+import de.javawi.jstun.header.MessageHeaderParsingException;
39
+import de.javawi.jstun.header.MessageHeaderInterface.MessageHeaderType;
40
+import de.javawi.jstun.util.Address;
41
+import de.javawi.jstun.util.UtilityException;
42
+
43
+/*
44
+ * This class implements a STUN server as described in RFC 3489.
45
+ * The server requires a machine that is dual-homed to be functional. 
46
+ */
47
+public class StunServer {
48
+	private static Logger logger = Logger.getLogger("de.javawi.stun.test.StunServer");
49
+	Vector<DatagramSocket> sockets;
50
+	
51
+	public StunServer(int primaryPort, InetAddress primary, int secondaryPort, InetAddress secondary) throws SocketException {
52
+		sockets = new Vector<DatagramSocket>();
53
+		sockets.add(new DatagramSocket(primaryPort, primary));
54
+		sockets.add(new DatagramSocket(secondaryPort, primary));
55
+		sockets.add(new DatagramSocket(primaryPort, secondary));
56
+		sockets.add(new DatagramSocket(secondaryPort, secondary));
57
+	}
58
+	
59
+	public void start() throws SocketException {
60
+		for (DatagramSocket socket : sockets) {
61
+			socket.setReceiveBufferSize(2000);
62
+			StunServerReceiverThread ssrt = new StunServerReceiverThread(socket);
63
+			ssrt.start();
64
+		}
65
+	}
66
+	
67
+	/*
68
+	 * Inner class to handle incoming packets and react accordingly.
69
+	 * I decided not to start a thread for every received Binding Request, because the time
70
+	 * required to receive a Binding Request, parse it, generate a Binding Response and send
71
+	 * it varies only between 2 and 4 milliseconds. This amount of time is small enough so
72
+	 * that no extra thread is needed for incoming Binding Request. 
73
+	 */
74
+	class StunServerReceiverThread extends Thread {
75
+		private DatagramSocket receiverSocket;
76
+		private DatagramSocket changedPort;
77
+		private DatagramSocket changedIP;
78
+		private DatagramSocket changedPortIP;
79
+		
80
+		StunServerReceiverThread(DatagramSocket datagramSocket) {
81
+			this.receiverSocket = datagramSocket;
82
+			for (DatagramSocket socket : sockets) {
83
+				if ((socket.getLocalPort() != receiverSocket.getLocalPort()) &&
84
+					(socket.getLocalAddress().equals(receiverSocket.getLocalAddress())))
85
+					changedPort = socket;
86
+				if ((socket.getLocalPort() == receiverSocket.getLocalPort()) &&
87
+					(!socket.getLocalAddress().equals(receiverSocket.getLocalAddress())))
88
+					changedIP = socket;
89
+				if ((socket.getLocalPort() != receiverSocket.getLocalPort()) &&
90
+					(!socket.getLocalAddress().equals(receiverSocket.getLocalAddress())))
91
+					changedPortIP = socket;
92
+			}
93
+		}
94
+		
95
+		public void run() {
96
+			while (true) {
97
+				try {
98
+					DatagramPacket receive = new DatagramPacket(new byte[200], 200);
99
+					receiverSocket.receive(receive);
100
+					logger.finest(receiverSocket.getLocalAddress().getHostAddress() + ":" + receiverSocket.getLocalPort() + " datagram received from " + receive.getAddress().getHostAddress() + ":" + receive.getPort());
101
+					MessageHeader receiveMH = MessageHeader.parseHeader(receive.getData()); 
102
+					try {
103
+						receiveMH.parseAttributes(receive.getData());
104
+						if (receiveMH.getType() == MessageHeaderType.BindingRequest) {
105
+							logger.config(receiverSocket.getLocalAddress().getHostAddress() + ":" + receiverSocket.getLocalPort() + " Binding Request received from " + receive.getAddress().getHostAddress() + ":" + receive.getPort());
106
+							ChangeRequest cr = (ChangeRequest) receiveMH.getMessageAttribute(MessageAttributeType.ChangeRequest);
107
+							if (cr == null) throw new MessageAttributeException("Message attribute change request is not set.");
108
+							ResponseAddress ra = (ResponseAddress) receiveMH.getMessageAttribute(MessageAttributeType.ResponseAddress);
109
+						
110
+							MessageHeader sendMH = new MessageHeader(MessageHeaderType.BindingResponse);
111
+							sendMH.setTransactionID(receiveMH.getTransactionID());
112
+						
113
+							// Mapped address attribute
114
+							MappedAddress ma = new MappedAddress();
115
+							ma.setAddress(new Address(receive.getAddress().getAddress()));
116
+							ma.setPort(receive.getPort());
117
+							sendMH.addMessageAttribute(ma);
118
+							// Changed address attribute
119
+							ChangedAddress ca = new ChangedAddress();
120
+							ca.setAddress(new Address(changedPortIP.getLocalAddress().getAddress()));
121
+							ca.setPort(changedPortIP.getLocalPort());
122
+							sendMH.addMessageAttribute(ca);
123
+							if (cr.isChangePort() && (!cr.isChangeIP())) {
124
+								logger.finer("Change port received in Change Request attribute");
125
+								// Source address attribute
126
+								SourceAddress sa = new SourceAddress();
127
+								sa.setAddress(new Address(changedPort.getLocalAddress().getAddress()));
128
+								sa.setPort(changedPort.getLocalPort());
129
+								sendMH.addMessageAttribute(sa);
130
+								byte[] data = sendMH.getBytes();
131
+								DatagramPacket send = new DatagramPacket(data, data.length);
132
+								if (ra != null) {
133
+									send.setPort(ra.getPort());
134
+									send.setAddress(ra.getAddress().getInetAddress());
135
+								} else {
136
+									send.setPort(receive.getPort());
137
+									send.setAddress(receive.getAddress());
138
+								}
139
+								changedPort.send(send);
140
+								logger.config(changedPort.getLocalAddress().getHostAddress() + ":" + changedPort.getLocalPort() + " send Binding Response to " + send.getAddress().getHostAddress() + ":" + send.getPort());
141
+							} else if ((!cr.isChangePort()) && cr.isChangeIP()) {
142
+								logger.finer("Change ip received in Change Request attribute");
143
+								// Source address attribute
144
+								SourceAddress sa = new SourceAddress();
145
+								sa.setAddress(new Address(changedIP.getLocalAddress().getAddress()));
146
+								sa.setPort(changedIP.getLocalPort());
147
+								sendMH.addMessageAttribute(sa);
148
+								byte[] data = sendMH.getBytes();
149
+								DatagramPacket send = new DatagramPacket(data, data.length);
150
+								if (ra != null) {
151
+									send.setPort(ra.getPort());
152
+									send.setAddress(ra.getAddress().getInetAddress());
153
+								} else {
154
+									send.setPort(receive.getPort());
155
+									send.setAddress(receive.getAddress());
156
+								}
157
+								changedIP.send(send);
158
+								logger.config(changedIP.getLocalAddress().getHostAddress() + ":" + changedIP.getLocalPort() + " send Binding Response to " + send.getAddress().getHostAddress() + ":" + send.getPort());
159
+							} else if ((!cr.isChangePort()) && (!cr.isChangeIP())) {
160
+								logger.finer("Nothing received in Change Request attribute");
161
+								// Source address attribute
162
+								SourceAddress sa = new SourceAddress();
163
+								sa.setAddress(new Address(receiverSocket.getLocalAddress().getAddress()));
164
+								sa.setPort(receiverSocket.getLocalPort());
165
+								sendMH.addMessageAttribute(sa);
166
+								byte[] data = sendMH.getBytes();
167
+								DatagramPacket send = new DatagramPacket(data, data.length);
168
+								if (ra != null) {
169
+									send.setPort(ra.getPort());
170
+									send.setAddress(ra.getAddress().getInetAddress());
171
+								} else {
172
+									send.setPort(receive.getPort());
173
+									send.setAddress(receive.getAddress());
174
+								}
175
+								receiverSocket.send(send);
176
+								logger.config(receiverSocket.getLocalAddress().getHostAddress() + ":" + receiverSocket.getLocalPort() + " send Binding Response to " + send.getAddress().getHostAddress() + ":" + send.getPort());
177
+							} else if (cr.isChangePort() && cr.isChangeIP()) {
178
+								logger.finer("Change port and ip received in Change Request attribute");
179
+								// Source address attribute
180
+								SourceAddress sa = new SourceAddress();
181
+								sa.setAddress(new Address(changedPortIP.getLocalAddress().getAddress()));
182
+								sa.setPort(changedPortIP.getLocalPort());
183
+								sendMH.addMessageAttribute(sa);
184
+								byte[] data = sendMH.getBytes();
185
+								DatagramPacket send = new DatagramPacket(data, data.length);
186
+								if (ra != null) {
187
+									send.setPort(ra.getPort());
188
+									send.setAddress(ra.getAddress().getInetAddress());
189
+								} else {
190
+									send.setPort(receive.getPort());
191
+									send.setAddress(receive.getAddress());
192
+								}
193
+								changedPortIP.send(send);
194
+								logger.config(changedPortIP.getLocalAddress().getHostAddress() + ":" + changedPortIP.getLocalPort() + " send Binding Response to " + send.getAddress().getHostAddress() + ":" + send.getPort());
195
+							}
196
+						}
197
+					} catch (UnknownMessageAttributeException umae) {
198
+						umae.printStackTrace();
199
+						// Generate Binding error response
200
+						MessageHeader sendMH = new MessageHeader(MessageHeaderType.BindingErrorResponse);
201
+						sendMH.setTransactionID(receiveMH.getTransactionID());
202
+						
203
+						// Unknown attributes
204
+						UnknownAttribute ua = new UnknownAttribute();
205
+						ua.addAttribute(umae.getType());
206
+						sendMH.addMessageAttribute(ua);
207
+						
208
+						byte[] data = sendMH.getBytes();
209
+						DatagramPacket send = new DatagramPacket(data, data.length);
210
+						send.setPort(receive.getPort());
211
+						send.setAddress(receive.getAddress());
212
+						receiverSocket.send(send);
213
+						logger.config(changedPortIP.getLocalAddress().getHostAddress() + ":" + changedPortIP.getLocalPort() + " send Binding Error Response to " + send.getAddress().getHostAddress() + ":" + send.getPort());
214
+					}	
215
+				} catch (IOException ioe) {
216
+					ioe.printStackTrace();
217
+				} catch (MessageAttributeParsingException mape) {
218
+					mape.printStackTrace();
219
+				} catch (MessageAttributeException mae) {
220
+					mae.printStackTrace();
221
+				} catch (MessageHeaderParsingException mhpe) {
222
+					mhpe.printStackTrace();
223
+				} catch (UtilityException ue) {
224
+					ue.printStackTrace();
225
+				} catch (ArrayIndexOutOfBoundsException aioobe) {
226
+					aioobe.printStackTrace();
227
+				}
228
+			}
229
+		}
230
+	}
231
+	
232
+	/*
233
+	 * To invoke the STUN server two IP addresses and two ports are required.
234
+	 */
235
+	public static void main(String args[]) {
236
+		try {
237
+			if (args.length != 4) {
238
+				System.out.println("usage: java de.javawi.jstun.test.demo.StunServer PORT1 IP1 PORT2 IP2");
239
+				System.out.println();
240
+				System.out.println(" PORT1 - the first port that should be used by the server");
241
+				System.out.println("   IP1 - the first ip address that should be used by the server");
242
+				System.out.println(" PORT2 - the second port that should be used by the server");
243
+				System.out.println("   IP2 - the second ip address that should be used by the server");
244
+				System.exit(0);
245
+			}
246
+			Handler fh = new FileHandler("logging_server.txt");
247
+			fh.setFormatter(new SimpleFormatter());
248
+			Logger.getLogger("de.javawi.stun").addHandler(fh);
249
+			Logger.getLogger("de.javawi.stun").setLevel(Level.ALL);
250
+			StunServer ss = new StunServer(Integer.parseInt(args[0]),
251
+										   InetAddress.getByName(args[1]),
252
+										   Integer.parseInt(args[2]),
253
+										   InetAddress.getByName(args[3]));
254
+			ss.start();
255
+		} catch (SocketException se) {
256
+			se.printStackTrace();
257
+		} catch (UnknownHostException uhe) {
258
+			uhe.printStackTrace();
259
+		} catch (IOException ioe) {
260
+			ioe.printStackTrace();
261
+		}
262
+	}
263
+}

+ 115
- 0
src/de/javawi/jstun/test/demo/ice/Candidate.java View File

@@ -0,0 +1,115 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.test.demo.ice;
13
+
14
+import java.net.DatagramSocket;
15
+import java.net.SocketException;
16
+import java.net.UnknownHostException;
17
+
18
+import de.javawi.jstun.util.Address;
19
+import de.javawi.jstun.util.UtilityException;
20
+
21
+public class Candidate implements Comparable {
22
+	// The ieft-mmusic-ice-12 draft is not non-ambigious about the number of types.
23
+	// Chapter 5.1 defines 3 and 4 types on page 16 and page 17, respectively. 
24
+	public enum CandidateType { Local, ServerReflexive, PeerReflexive, Relayed };
25
+	
26
+	private DatagramSocket socket;
27
+	private CandidateType type;
28
+	private short componentId;
29
+	private int priority;
30
+	private int foundationId;
31
+	private Candidate base;
32
+	private boolean isInUse;
33
+	
34
+	public Candidate(Address address, short componentId) throws SocketException, UnknownHostException, UtilityException {
35
+		this.socket = new DatagramSocket(0, address.getInetAddress());
36
+		this.type = CandidateType.Local;
37
+		this.componentId = componentId;
38
+		this.priority = 0;
39
+		this.base = this;
40
+		this.isInUse = false;
41
+	}
42
+	
43
+	public Candidate(Address address, CandidateType type, short componentId, Candidate base) throws SocketException, UnknownHostException, UtilityException {
44
+		this.socket = new DatagramSocket(0, address.getInetAddress());
45
+		this.type = type;
46
+		setComponentId(componentId);
47
+		this.priority = 0;
48
+		this.base = base;
49
+		this.isInUse = false;
50
+	}
51
+
52
+	public void setBase(Candidate base) {
53
+		this.base = base;
54
+	}
55
+	
56
+	public Candidate getBase() {
57
+		return base;
58
+	}
59
+	
60
+	public CandidateType getCandidateType() {
61
+		return type;
62
+	}
63
+	
64
+	public void setComponentId(short componentId) {
65
+		if ((componentId < 1) || (componentId > 256)) throw new IllegalArgumentException(componentId + " is not between 1 and 256 inclusive.");
66
+		this.componentId = componentId;
67
+	}
68
+	
69
+	public short getComponentId() {
70
+		return componentId;
71
+	}
72
+	
73
+	public void setFoundationId(int foundationId) {
74
+		this.foundationId = foundationId;
75
+	}
76
+	
77
+	public int getFoundationId() {
78
+		return foundationId;
79
+	}
80
+	
81
+	public void setPriority(int priority) {
82
+		this.priority = priority;
83
+	}
84
+	
85
+	public int getPriority() {
86
+		return priority;
87
+	}
88
+	
89
+	public Address getAddress() throws UtilityException {
90
+		return new Address(socket.getLocalAddress().getAddress());
91
+	}
92
+
93
+	public int getPort() {
94
+		return socket.getLocalPort();
95
+	}
96
+	
97
+	public void setInUse(boolean isInUse) {
98
+		this.isInUse = isInUse;
99
+	}
100
+	
101
+	public boolean getInUse() {
102
+		return isInUse;
103
+	}
104
+	
105
+	public int compareTo(Object arg0) {
106
+		Candidate cand = (Candidate) arg0;
107
+		return cand.getPriority() - getPriority();
108
+	}
109
+	
110
+	public boolean equals(Object o) {
111
+		if (o == null) return false;
112
+		if ((((Candidate) o).socket.equals(socket)) && (((Candidate) o).base.equals(base))) return true;
113
+		return false;
114
+	}
115
+}

+ 146
- 0
src/de/javawi/jstun/test/demo/ice/ICENegociator.java View File

@@ -0,0 +1,146 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.test.demo.ice;
13
+
14
+import java.net.InetAddress;
15
+import java.net.NetworkInterface;
16
+import java.util.Collections;
17
+import java.util.Enumeration;
18
+import java.util.HashSet;
19
+import java.util.Iterator;
20
+import java.util.List;
21
+import java.util.Vector;
22
+
23
+import de.javawi.jstun.test.DiscoveryInfo;
24
+import de.javawi.jstun.test.DiscoveryTest;
25
+import de.javawi.jstun.test.demo.ice.Candidate.CandidateType;
26
+import de.javawi.jstun.util.Address;
27
+
28
+public class ICENegociator {
29
+	// type preference must be an integere from 0 (=lowest) to 126 (=highest) (inclusive)
30
+	private final static int LOCAL_PREFERENCE = 0;
31
+	private final static int SERVER_REFLEXIVE_PREFERENCE = 42;
32
+	private final static int PEER_REFLEXIVE_PREFERENCE = 84;
33
+	private final static int RELAYED_PREFERENCE = 126;
34
+	
35
+	// component id
36
+	private short componentId;
37
+	
38
+	// candidates
39
+	HashSet<Candidate> candidates;
40
+	
41
+	public ICENegociator(short componentId) {
42
+		this.componentId = componentId;
43
+		candidates = new HashSet<Candidate>();
44
+	}
45
+
46
+	/*
47
+	 * This method gathers candidate addresses as described in draft-ietf-mmusic-ice-12.txt Chapter 2.1
48
+	 * Unfortunately, only the candidates of the direct attached network interfaces and server reflexive
49
+	 * addreses are gathered. So far, no support for relayed candidates is available (because I am not
50
+	 * aware of any STUN relay server).
51
+	 */
52
+	public void gatherCandidateAddresses() {
53
+		candidates = new HashSet<Candidate>();
54
+		try {
55
+			Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces();
56
+			while (ifaces.hasMoreElements()) {
57
+				NetworkInterface iface = ifaces.nextElement();
58
+				Enumeration<InetAddress> iaddresses = iface.getInetAddresses();
59
+				while (iaddresses.hasMoreElements()) {
60
+					InetAddress iaddress = iaddresses.nextElement();
61
+					if (!iaddress.isLoopbackAddress() && !iaddress.isLinkLocalAddress()) {
62
+						// add host candidate
63
+						Candidate local = new Candidate(new Address(iaddress.getAddress()), componentId);
64
+						candidates.add(local);
65
+						// add server reflexive address
66
+						DiscoveryTest test = new DiscoveryTest(iaddress, "iphone-stun.freenet.de", 3478);
67
+						DiscoveryInfo di = test.test();
68
+						if (di.getPublicIP() != null) {
69
+							Candidate cand = new Candidate(new Address(di.getPublicIP().getAddress()), CandidateType.ServerReflexive, componentId, local);
70
+							cand.setComponentId(componentId);
71
+							candidates.add(cand);
72
+						}
73
+					}
74
+				}
75
+			}
76
+		} catch (Exception e) {
77
+			e.printStackTrace();
78
+		}
79
+	}
80
+	
81
+	public void prioritizeCandidates() {
82
+		// count number of candidate types
83
+		int numberLocal = 0;
84
+		int numberServerReflexive = 0;
85
+		int numberPeerReflexive = 0;
86
+		int numberRelayed = 0;
87
+		// count number of candidates of a particular type
88
+		Iterator<Candidate> iterCandidates = candidates.iterator();
89
+		while (iterCandidates.hasNext()) {
90
+			Candidate cand = iterCandidates.next();
91
+			CandidateType type = cand.getCandidateType();
92
+			if (type == CandidateType.Local) numberLocal++;
93
+			else if (type == CandidateType.ServerReflexive) numberServerReflexive++;
94
+			else if (type == CandidateType.PeerReflexive) numberPeerReflexive++;
95
+			else if (type == CandidateType.Relayed) numberRelayed++;
96
+		}
97
+		// assign priorities
98
+		iterCandidates = candidates.iterator();
99
+		while (iterCandidates.hasNext()) {
100
+			int typeValue = 0;
101
+			int localValue = 0;
102
+			int componentValue = 0;
103
+			Candidate cand = iterCandidates.next();
104
+			CandidateType type = cand.getCandidateType();
105
+			if (type == CandidateType.Local) {
106
+				typeValue = LOCAL_PREFERENCE;
107
+				localValue = numberLocal--;
108
+			}
109
+			else if (type == CandidateType.ServerReflexive) {
110
+				typeValue = SERVER_REFLEXIVE_PREFERENCE;
111
+				localValue = numberServerReflexive--;
112
+			}
113
+			else if (type == CandidateType.PeerReflexive) {
114
+				typeValue = PEER_REFLEXIVE_PREFERENCE;
115
+				localValue = numberPeerReflexive--;
116
+			}
117
+			else if (type == CandidateType.Relayed) {
118
+				typeValue = RELAYED_PREFERENCE;
119
+				localValue = numberRelayed--;
120
+			}
121
+			componentValue = cand.getComponentId();
122
+			int priority = ((2 ^ 24) * typeValue) + ((2 ^ 8) * localValue) + componentValue;
123
+			cand.setPriority(priority);
124
+		}
125
+	}
126
+	
127
+	public List<Candidate> getSortedCandidates() {
128
+		Vector<Candidate> sortedCandidates = new Vector<Candidate>(candidates);
129
+		Collections.sort(sortedCandidates);
130
+		return sortedCandidates;
131
+	}
132
+
133
+	public static void main(String args[]) {
134
+		ICENegociator cc = new ICENegociator((short) 1);
135
+		// gather candidates
136
+		cc.gatherCandidateAddresses();
137
+		// priorize candidates
138
+		cc.prioritizeCandidates();
139
+		// get SortedCandidates
140
+		List<Candidate> sortedCandidates = cc.getSortedCandidates();
141
+		
142
+		// sent sorted candidate addresses to peer over SDP
143
+		// received sorted candidate addresses of peer over SDP
144
+		
145
+	}
146
+}

+ 102
- 0
src/de/javawi/jstun/util/Address.java View File

@@ -0,0 +1,102 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.util;
13
+
14
+import java.util.*;
15
+import java.net.*;
16
+
17
+public class Address {
18
+	int firstOctet;
19
+	int secondOctet;
20
+	int thirdOctet;
21
+	int fourthOctet;
22
+	
23
+	public Address(int firstOctet, int secondOctet, int thirdOctet, int fourthOctet) throws UtilityException {
24
+		if ((firstOctet < 0) || (firstOctet > 255) || (secondOctet < 0) || (secondOctet > 255) || (thirdOctet < 0) || (thirdOctet > 255) || (fourthOctet < 0) || (fourthOctet > 255)) {
25
+			throw new UtilityException("Address is malformed.");
26
+		}
27
+		this.firstOctet = firstOctet;
28
+		this.secondOctet = secondOctet;
29
+		this.thirdOctet = thirdOctet;
30
+		this.fourthOctet = fourthOctet;
31
+	}
32
+	
33
+	public Address(String address) throws UtilityException {
34
+		StringTokenizer st = new StringTokenizer(address, ".");
35
+		if (st.countTokens() != 4) {
36
+			throw new UtilityException("4 octets in address string are required.");
37
+		}
38
+		int i = 0;
39
+		while (st.hasMoreTokens()) {
40
+			int temp = Integer.parseInt(st.nextToken());
41
+			if ((temp < 0) || (temp > 255)) {
42
+				throw new UtilityException("Address is in incorrect format.");
43
+			}
44
+			switch (i) {
45
+			case 0: firstOctet = temp; ++i; break;
46
+			case 1: secondOctet = temp; ++i; break;
47
+			case 2: thirdOctet = temp; ++i; break;
48
+			case 3: fourthOctet = temp; ++i; break;
49
+			}
50
+		}
51
+	}
52
+	
53
+	public Address(byte[] address) throws UtilityException {
54
+		if (address.length < 4) {
55
+			throw new UtilityException("4 bytes are required.");
56
+		}
57
+		firstOctet = Utility.oneByteToInteger(address[0]);
58
+		secondOctet = Utility.oneByteToInteger(address[1]);
59
+		thirdOctet = Utility.oneByteToInteger(address[2]);
60
+		fourthOctet = Utility.oneByteToInteger(address[3]);
61
+	}
62
+	
63
+	public String toString() {
64
+		return firstOctet + "." + secondOctet + "." + thirdOctet + "." + fourthOctet;
65
+	}
66
+	
67
+	public byte[] getBytes() throws UtilityException {
68
+		byte[] result = new byte[4];
69
+		result[0] = Utility.integerToOneByte(firstOctet);
70
+		result[1] = Utility.integerToOneByte(secondOctet);
71
+		result[2] = Utility.integerToOneByte(thirdOctet);
72
+		result[3] = Utility.integerToOneByte(fourthOctet);
73
+		return result;
74
+	}
75
+	
76
+	public InetAddress getInetAddress() throws UtilityException, UnknownHostException {
77
+		byte[] address = new byte[4];
78
+		address[0] = Utility.integerToOneByte(firstOctet);
79
+		address[1] = Utility.integerToOneByte(secondOctet);
80
+		address[2] = Utility.integerToOneByte(thirdOctet);
81
+		address[3] = Utility.integerToOneByte(fourthOctet);
82
+		return InetAddress.getByAddress(address);
83
+	}
84
+	
85
+	public boolean equals(Object obj) {
86
+		if (obj == null) return false;
87
+		try {
88
+			byte[] data1 = this.getBytes();
89
+			byte[] data2 = ((Address) obj).getBytes();
90
+			if ((data1[0] == data2[0]) && (data1[1] == data2[1]) &&
91
+			    (data1[2] == data2[2]) && (data1[3] == data2[3])) return true;
92
+			return false;
93
+		} catch (UtilityException ue) {
94
+			return false;
95
+		}
96
+	}
97
+	
98
+	public int hashCode() {
99
+		return (firstOctet << 24) + (secondOctet << 16) + (thirdOctet << 8) + fourthOctet; 
100
+	}
101
+
102
+}

+ 68
- 0
src/de/javawi/jstun/util/Utility.java View File

@@ -0,0 +1,68 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.util;
13
+
14
+public class Utility {
15
+
16
+	public static final byte integerToOneByte(int value) throws UtilityException {
17
+		if ((value > Math.pow(2,15)) || (value < 0)) {
18
+			throw new UtilityException("Integer value " + value + " is larger than 2^15");
19
+		}
20
+		return (byte)(value & 0xFF);
21
+	}
22
+	
23
+	public static final byte[] integerToTwoBytes(int value) throws UtilityException {
24
+		byte[] result = new byte[2];
25
+		if ((value > Math.pow(2,31)) || (value < 0)) {
26
+			throw new UtilityException("Integer value " + value + " is larger than 2^31");
27
+		}
28
+        result[0] = (byte)((value >>> 8) & 0xFF);
29
+        result[1] = (byte)(value & 0xFF);
30
+		return result; 
31
+	}
32
+	
33
+	public static final byte[] integerToFourBytes(int value) throws UtilityException {
34
+		byte[] result = new byte[4];
35
+		if ((value > Math.pow(2,63)) || (value < 0)) {
36
+			throw new UtilityException("Integer value " + value + " is larger than 2^63");
37
+		}
38
+        result[0] = (byte)((value >>> 24) & 0xFF);
39
+		result[1] = (byte)((value >>> 16) & 0xFF);
40
+		result[2] = (byte)((value >>> 8) & 0xFF);
41
+        result[3] = (byte)(value & 0xFF);
42
+		return result; 
43
+	}
44
+	
45
+	public static final int oneByteToInteger(byte value) throws UtilityException {
46
+		return (int)value & 0xFF;
47
+	}
48
+	
49
+	public static final int twoBytesToInteger(byte[] value) throws UtilityException {
50
+		if (value.length < 2) {
51
+			throw new UtilityException("Byte array too short!");
52
+		}
53
+        int temp0 = value[0] & 0xFF;
54
+        int temp1 = value[1] & 0xFF;
55
+        return ((temp0 << 8) + temp1);
56
+	}
57
+	
58
+	public static final long fourBytesToLong(byte[] value) throws UtilityException {
59
+		if (value.length < 4) {
60
+			throw new UtilityException("Byte array too short!");
61
+		}
62
+        int temp0 = value[0] & 0xFF;
63
+        int temp1 = value[1] & 0xFF;
64
+		int temp2 = value[2] & 0xFF;
65
+		int temp3 = value[3] & 0xFF;
66
+        return (((long)temp0 << 24) + (temp1 << 16) + (temp2 << 8) + temp3);
67
+	}	                                      
68
+}

+ 21
- 0
src/de/javawi/jstun/util/UtilityException.java View File

@@ -0,0 +1,21 @@
1
+/*
2
+ * This file is part of JSTUN. 
3
+ * 
4
+ * Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
5
+ * reserved.
6
+ * 
7
+ * This software is licensed under either the GNU Public License (GPL),
8
+ * or the Apache 2.0 license. Copies of both license agreements are
9
+ * included in this distribution.
10
+ */
11
+
12
+package de.javawi.jstun.util;
13
+
14
+public class UtilityException extends Exception {
15
+	private static final long serialVersionUID = 3545800974716581680L;
16
+
17
+	UtilityException(String mesg) {
18
+		super(mesg);
19
+	}
20
+
21
+}

Loading…
Cancel
Save