├── .github └── workflows │ ├── ci.yml │ ├── codeql-analysis.yml │ ├── prepare.yml │ └── publish.yml ├── .gitignore ├── .travis.settings.xml ├── .travis.yml ├── CHANGES ├── LICENSE ├── LICENSE-LGPL.txt ├── README.md ├── TODO ├── doc ├── tinyradius-big.png ├── tinyradius-small.png └── tinyradius.html ├── pom.xml └── src ├── main ├── java │ └── org │ │ └── tinyradius │ │ ├── attribute │ │ ├── IntegerAttribute.java │ │ ├── IpAttribute.java │ │ ├── Ipv6Attribute.java │ │ ├── Ipv6PrefixAttribute.java │ │ ├── RadiusAttribute.java │ │ ├── StringAttribute.java │ │ ├── VendorSpecificAttribute.java │ │ └── package.html │ │ ├── dictionary │ │ ├── AttributeType.java │ │ ├── DefaultDictionary.java │ │ ├── Dictionary.java │ │ ├── DictionaryParser.java │ │ ├── MemoryDictionary.java │ │ ├── WritableDictionary.java │ │ ├── dictionary.3gpp │ │ ├── dictionary.fortinet │ │ └── package.html │ │ ├── packet │ │ ├── AccessRequest.java │ │ ├── AccountingRequest.java │ │ ├── CoaRequest.java │ │ ├── RadiusPacket.java │ │ └── package.html │ │ ├── proxy │ │ ├── RadiusProxy.java │ │ ├── RadiusProxyConnection.java │ │ └── package.html │ │ ├── test │ │ ├── TestClient.java │ │ ├── TestDictionary.java │ │ ├── TestProxy.java │ │ ├── TestServer.java │ │ └── package.html │ │ └── util │ │ ├── RadiusClient.java │ │ ├── RadiusEndpoint.java │ │ ├── RadiusException.java │ │ ├── RadiusServer.java │ │ ├── RadiusUtil.java │ │ └── package.html └── resources │ └── org │ └── tinyradius │ └── dictionary │ └── default_dictionary └── test └── java └── org └── tinyradius └── attribute └── IntegerAttributeTest.java /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | on: 3 | push: 4 | branches: [master, main, develop] 5 | pull_request: 6 | branches: [master, main, develop] 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Set up JDK 1.8 14 | uses: actions/setup-java@v2 15 | with: 16 | java-version: 8 17 | distribution: adopt 18 | settings-path: ${{ github.workspace }} 19 | - name: Build with Maven 20 | run: mvn -V -B -ntp package --file pom.xml 21 | 22 | - name: Calculate dependency tree 23 | run: mvn -V -B -ntp dependency:tree --file pom.xml 24 | 25 | - name: Publish to https://maven.pkg.github.com/ctran/TinyRadius 26 | run: mvn -V -B -ntp -s $GITHUB_WORKSPACE/settings.xml deploy 27 | if: ${{ github.event_name == 'push' }} 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main, master, develop ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main, master ] 20 | schedule: 21 | - cron: '20 0 * * 6' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'java' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v2 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v1 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 52 | 53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 54 | # If this step fails, then you should remove it and run the build manually (see below) 55 | - name: Autobuild 56 | uses: github/codeql-action/autobuild@v1 57 | 58 | # ℹ️ Command-line programs to run using the OS shell. 59 | # 📚 https://git.io/JvXDl 60 | 61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 62 | # and modify them (or add more) to build your code if your project 63 | # uses a compiled language 64 | 65 | #- run: | 66 | # make bootstrap 67 | # make release 68 | 69 | - name: Perform CodeQL Analysis 70 | uses: github/codeql-action/analyze@v1 71 | -------------------------------------------------------------------------------- /.github/workflows/prepare.yml: -------------------------------------------------------------------------------- 1 | name: Prepare release 2 | on: 3 | workflow_dispatch: 4 | inputs: 5 | releaseVersion: 6 | description: "Release version" 7 | required: true 8 | default: "X.Y.Z" 9 | developmentVersion: 10 | description: "Development version" 11 | required: true 12 | default: "X.Y.Z-SNAPSHOT" 13 | jobs: 14 | prepare: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v2 18 | with: 19 | token: ${{ secrets.RELEASE_PAT }} 20 | - run: | 21 | git config --global user.name 'Cuong Tran' 22 | git config --global user.email 'ctran@users.noreply.github.com' 23 | 24 | - uses: actions/setup-java@v2 25 | with: 26 | java-version: '11' 27 | distribution: 'adopt' 28 | - name: Prepare release 29 | run: | 30 | mvn -V -B -ntp release:prepare \ 31 | -DpreparationGoals=validate \ 32 | -DpushChanges=false \ 33 | -DdevelopmentVersion=${{ github.event.inputs.developmentVersion }} \ 34 | -DreleaseVersion=${{ github.event.inputs.releaseVersion }} 35 | 36 | git push --tags && git push -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | on: 3 | push: 4 | tags: 5 | - v** 6 | jobs: 7 | publish: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Set up Maven Central Repository 12 | uses: actions/setup-java@v2 13 | with: 14 | java-version: '11' 15 | distribution: 'adopt' 16 | server-id: ossrh 17 | server-username: MAVEN_USERNAME 18 | server-password: MAVEN_PASSWORD 19 | gpg-private-key: ${{ secrets.OSSRH_GPG_SECRET_KEY }} 20 | gpg-passphrase: MAVEN_GPG_PASSPHRASE 21 | settings-path: ${{ github.workspace }} 22 | - name: Publish package to Maven Central 23 | run: mvn -V -B -Possrh -s $GITHUB_WORKSPACE/settings.xml clean deploy 24 | env: 25 | MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} 26 | MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} 27 | MAVEN_GPG_PASSPHRASE: ${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }} 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | dist 3 | build 4 | doc/apidoc 5 | release.properties 6 | target 7 | .DS_Store 8 | .settings 9 | .classpath 10 | .project 11 | .flattened-pom.xml 12 | 13 | *.releaseBackup 14 | 15 | # IntelliJ IDEA 16 | *.iml 17 | .idea/* 18 | -------------------------------------------------------------------------------- /.travis.settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | ctran-maven-repo 7 | ctran 8 | ${env.BINTRAY_TOKEN} 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | false 17 | 18 | central 19 | bintray 20 | http://jcenter.bintray.com 21 | 22 | 23 | 24 | 25 | 26 | false 27 | 28 | central 29 | bintray-plugins 30 | http://jcenter.bintray.com 31 | 32 | 33 | bintray 34 | 35 | 36 | 37 | bintray 38 | 39 | 40 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: java 3 | jdk: 4 | - openjdk8 5 | - openjdk11 6 | services: 7 | - docker 8 | cache: 9 | directories: 10 | - "$HOME/.cache" 11 | 12 | deploy: 13 | provider: script 14 | script: "cp .travis.settings.xml $HOME/.m2/settings.xml && mvn deploy" 15 | skip_cleanup: true 16 | on: 17 | tags: true 18 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | TinyRadius Change Log 2 | 3 | Plans for 1.0: Asynchronous Radius client (see TODO) 4 | 5 | 0.9.9: 6 | - small bug fix in MemoryDictionary 7 | - switch to Maven2 8 | 9 | 0.9.8: 10 | - small bug fix in attribute class 11 | 12 | 0.9.7: 13 | - bug fix: request authenticator for accounting request packets 14 | has not been checked, added 15 | AccountingRequest.checkRequestAuthenticator() 16 | thanks to Radovan Semancik! 17 | 18 | 0.9.6: 19 | - bug fix: implicit conversion of byte to int failed 20 | for values bigger than 127 (class VendorSpecificAttribute) 21 | thanks to Aldir O. Brandao Jr and to Bjørn Nordbø! 22 | - bug fix: SocketException terminated Radius listen thread 23 | in RadiusServer class 24 | - improved duplicate packet check by using Radius Authenticator 25 | field which includes a checksum over the whole packet 26 | 27 | 0.9.5: 28 | - further Radius packet type codes (COA, Disconnect) 29 | - implemented Radius proxy support (see org.tinyradius.proxy) 30 | thanks to glanz! 31 | 32 | 0.9.4: 33 | - greatly improved dictionary support: new package 34 | org.tinyradius.dictionary, see package.html 35 | - INCLUDE directive for dictionary files 36 | - easy access to Vendor-Specific sub-attributes 37 | by their name or by vendor ID/type code 38 | through methods in RadiusPacket 39 | - ability to set listen address for RadiusServer 40 | - improved IP attribute (IP represented as string or long) 41 | 42 | 0.9.3: 43 | - fixed a bug concerning sub attributes of sub-type 26 44 | of a Vendor-Specific attributes 45 | - structural enhancements for Vendor-Specific attributes 46 | - fixed a NullPointerException occuring for sub attributes not 47 | in the attribute dictionary 48 | 49 | 0.9.2: 50 | - enhanced RadiusServer to include the client address 51 | in the callback methods accessRequestReceived() and 52 | accountingRequestReceived() 53 | 54 | 0.9.1: 55 | - added commons-logging.jar and various log messages to 56 | the classes RadiusServer/RadiusClient 57 | 58 | 0.9: 59 | - initial public release 60 | -------------------------------------------------------------------------------- /LICENSE-LGPL.txt: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 489 | 490 | Also add information on how to contact you by electronic and paper mail. 491 | 492 | You should also get your employer (if you work as a programmer) or your 493 | school, if any, to sign a "copyright disclaimer" for the library, if 494 | necessary. Here is a sample; alter the names: 495 | 496 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 497 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 498 | 499 | , 1 April 1990 500 | Ty Coon, President of Vice 501 | 502 | That's all there is to it! 503 | 504 | 505 | 506 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://github.com/ctran/TinyRadius/actions/workflows/ci.yml/badge.svg)](https://github.com/ctran/TinyRadius/actions/workflows/ci.yml) 2 | 3 | TinyRadius README 4 | 5 | TinyRadius is a simple, small and fast Java Radius library capable of 6 | sending and receiving Radius packets of all types. It is released 7 | under the terms of the LGPL. 8 | 9 | For documentation, please have a look at doc/tinyradius.html 10 | and doc/apidoc. Every method has got JavaDoc comments, and every 11 | package has got a package.html document detailing the package 12 | usage. 13 | 14 | Please do not hesitate to contact me if you have got questions or 15 | suggestions. 16 | 17 | Matthias Wuttke 18 | post@matthias-wuttke.de 19 | http://tinyradius.sourceforge.net/ 20 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | - implement asynchronous radius client for sending multiple requests 2 | in parallel while still waiting for responses to earlier packets 3 | 4 | - global dictionary manager allowing to configure a dictionary 5 | to be used for all future Radius packets: 6 | replace DefaultDictionary.getInstance() -------------------------------------------------------------------------------- /doc/tinyradius-big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctran/TinyRadius/c5d4463c32bb5142a4fa5f8526b4a93f24afacb2/doc/tinyradius-big.png -------------------------------------------------------------------------------- /doc/tinyradius-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctran/TinyRadius/c5d4463c32bb5142a4fa5f8526b4a93f24afacb2/doc/tinyradius-small.png -------------------------------------------------------------------------------- /doc/tinyradius.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TinyRadius: Java Radius library 5 | 6 | 7 | 8 | 9 |

TinyRadius: Java Radius library

10 | 11 |

TinyRadius is a simple, small and fast Java Radius library capable of 12 | sending and receiving Radius packets of all types. It is released under the 13 | terms of the LGPL.

14 | 15 |

SourceForge Project Overview | 16 | JavaDoc API documentation | 17 | Download current release

18 | 19 |

What you can do with it:

20 | 21 |
    22 |
  • send and receive Radius packets (Access-Request, Access-Accept, 23 | Access-Reject, Access-Challenge, Accounting-Request, Accounting-Response 24 | and others) from within your Java application
  • 25 |
  • use PAP and CHAP as authentication types for Access-Request messages
  • 26 |
  • attach arbitrary Radius attributes to the packets employing attribute 27 | names read from a dictionary file
  • 28 |
  • send and receive Radius packets with "Vendor-Specific" attributes
  • 29 |
30 | 31 |

What you cannot/should not do with it:

32 | 33 |
    34 |
  • set up a complex Radius server (please use FreeRadius 35 | or JRadius)
  • 36 |
  • connect the server to a user database without writing Java code 37 | (this library is ment to be plugged in applications and not to be 38 | used as a stand-alone server)
  • 39 |
40 | 41 |

TinyRadius comes with small sample applications which show how to integrate 42 | it as a Radius server and a Radius client.

43 | 44 |

What are the requirements?

45 | 46 |
    47 |
  • TinyRadius works well and is tested with JDK 1.1 through 1.6. 48 | I recommend using at least JDK 1.4.
  • 49 |
  • You need Apache Commons Logging to compile and run 50 | TinyRadius. This small library is included with the 51 | TinyRadius release.
  • 52 |
53 | 54 |

EXAMPLE 1: Authentication made easy

55 | 56 |

If you do not need to set special attribute values, you can just use the 57 | method authenticate() from the RadiusClient:

58 | 59 |
RadiusClient rc = new RadiusClient(host, sharedSecret);
 60 | if (rc.authenticate(userName, password)) {
 61 | 	...
62 | 63 | 64 |

EXAMPLE 2: Sending an Access-Request with multiple attributes

65 | 66 |

1. Create a RadiusClient object with the host name and shared secret 67 | of the Radius server you wish to contact. You may set additional details 68 | (port numbers, for example) using methods of this object.

69 | 70 |
RadiusClient rc = new RadiusClient(host, shared);
71 | 72 |

2. Create the Access-Request Radius packet. Pass the user name and 73 | password in the constructor. The User-Name attribute will be added 74 | on construction of the object, while the User-Password attribute (PAP) 75 | or the CHAP-Password and CHAP-Challenge attributes (CHAP) will be 76 | generated when encoding the packet because the request authenticator of 77 | the packet is required to encrypt the password.

78 | 79 |
AccessRequest ar = new AccessRequest(user, pass);
 80 | ar.setAuthProtocol(AccessRequest.AUTH_CHAP); // or AUTH_PAP
81 | 82 |

3. Set further attributes. Note that TinyRadius resolves the attribute type 83 | from the given type name and that it converts the IP address and the name 84 | of the constant (Login-User) to the right values. Please also note how 85 | the Vendor-Specific (WISPr) sub-attribute "WISPr-Location-ID" 86 | is set. This call results in the creation of a Vendor-Specific attribute 87 | with the proper vendor ID and the addition of a sub-attribute to this 88 | attribute.

89 | 90 |
ar.addAttribute("NAS-Identifier", "this.is.my.nas-identifier.de");
 91 | ar.addAttribute("NAS-IP-Address", "192.168.0.100");
 92 | ar.addAttribute("Service-Type", "Login-User");
 93 | ar.addAttribute("WISPr-Location-ID", "ger,de.sample-location");
94 | 95 |

4. Send the packet and receive the response.

96 | 97 |
RadiusPacket response = rc.authenticate(ar);
 98 | if (response.getPacketType() == RadiusPacket.ACCESS_ACCEPT) {
 99 | 	...
100 | 101 | 102 |

EXAMPLE 3: How to implement a Radius server

103 | 104 |

You need to subclass org.tinyradius.util.RadiusServer. 105 | Provide an implementation for the following methods:

106 | 107 |
String getSharedSecret(InetAddress client);
108 | 109 |

This method should check whether the passed client is allowed to 110 | communicate with the Radius server. If this is the case, it should 111 | return the shared secret that secures the communication to the client.

112 | 113 |
String getUserPassword(String userName);
114 | 115 |

This method returns the password for the given user. If you have not 116 | access to the password (in the case of CHAP) or you need finer control 117 | (you want to set attributes for the response packet, for example), you 118 | have to override the following method.

119 | 120 |
RadiusPacket accessRequestReceived(AccessRequest request, InetAddress client);
121 | RadiusPacket accountingRequestReceived(AccountingRequest request, InetAddress client);
122 | 123 |

Override this methods for fine control about the way Accounting-Request 124 | and Access-Request packets are handled. Just return the Radius packet 125 | to be sent as a response or null if the request should be ignored.

126 | 127 |

After implementing your own server class, you can start and stop the server 128 | using the methods start() and stop(). For start(), you pass whether the server 129 | should listen on the auth and/or the acct port. This method spawns new threads.

130 | 131 |
RadiusServer server = new MyRadiusServer();
132 | server.start(true, true);
133 | server.stop();
134 | 135 | 136 |

EXAMPLE 4: How to implement a Radius proxy server

137 | 138 |

You need to subclass org.tinyradius.proxy.RadiusProxy. 139 | In addition to implementing the abstract methods from RadiusServer, 140 | you have to provide an implementation for the following method.

141 | 142 |
RadiusEndpoint getProxyServer(RadiusPacket packet, RadiusEndpoint client);
143 | 144 |

Using the provided client endpoint (containing the client's IP address, 145 | the port number as well as the shared secret) you have to decide whether 146 | the given Radius packet should be forwarded or if the TinyRadius server 147 | itself shall handle the packet.

148 | 149 |

If you return null, the packet will be dealt with as 150 | usual. Otherwise, the packet will be proxied (adding a Proxy-State attribute) 151 | to the returned Radius server.

152 | 153 |

For a complete example, please check the three classes TestProxy, 154 | TestServer and TestClient, which can be used to 155 | set up an easy proxying Radius infrastructure only with TinyRadius and 156 | only using "localhost".

157 | 158 |
159 | 160 |

Please do not hesitate to contact me if you have got questions or 161 | suggestions.

162 | 163 | Matthias Wuttke
164 | post@matthias-wuttke.de
165 | http://tinyradius.sourceforge.net/ 166 | 167 |
168 | 169 | 170 | SourceForge.net Logo 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | org.tinyradius 5 | tinyradius 6 | 1.1.4-SNAPSHOT 7 | jar 8 | TinyRadius Java Radius Library 9 | 10 | TinyRadius is a simple, small and fast Java Radius library capable of sending and receiving Radius packets of all types. It is released under the terms of the LGPL. 11 | 12 | https://github.com/ctran/TinyRadius 13 | 14 | 15 | 16 | wuttke 17 | Matthias Wuttke 18 | mw@teuto.net 19 | CEST 20 | 21 | 22 | ctran 23 | cuong.tran@gmail.com 24 | 25 | 26 | 27 | 28 | 29 | Lesser General Public License (LGPL) 30 | ${project.basedir}/LICENSE 31 | local 32 | 33 | 34 | 35 | 36 | scm:git:http://github.com/ctran/TinyRadius.git 37 | scm:git:http://github.com/ctran/TinyRadius.git 38 | https://github.com/ctran/TinyRadius/ 39 | HEAD 40 | 41 | 42 | 43 | UTF-8 44 | 8 45 | 8 46 | 47 | 48 | 49 | 50 | commons-logging 51 | commons-logging 52 | 1.2 53 | 54 | 55 | junit 56 | junit 57 | 4.13.1 58 | test 59 | 60 | 61 | com.github.seancfoley 62 | ipaddress 63 | 5.3.3 64 | 65 | 66 | 67 | 68 | 69 | 70 | org.apache.maven.plugins 71 | maven-release-plugin 72 | 3.0.0-M5 73 | 74 | v@{project.version} 75 | 76 | 77 | 78 | org.apache.maven.plugins 79 | maven-source-plugin 80 | 2.1.2 81 | 82 | 83 | attach-sources 84 | verify 85 | 86 | jar-no-fork 87 | 88 | 89 | 90 | 91 | 92 | org.codehaus.mojo 93 | flatten-maven-plugin 94 | 1.1.0 95 | 96 | true 97 | oss 98 | 99 | remove 100 | remove 101 | 102 | 103 | 104 | 105 | 106 | flatten 107 | process-resources 108 | 109 | flatten 110 | 111 | 112 | 113 | 114 | flatten.clean 115 | clean 116 | 117 | clean 118 | 119 | 120 | 121 | 122 | 123 | org.apache.maven.plugins 124 | maven-javadoc-plugin 125 | 3.3.1 126 | 127 | 128 | attach-javadocs 129 | 130 | jar 131 | 132 | 133 | false 134 | false 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | github 145 | https://maven.pkg.github.com/ctran/TinyRadius 146 | 147 | 148 | 149 | 150 | 151 | ossrh 152 | 153 | false 154 | 155 | 156 | gpg 157 | 158 | 159 | 160 | ossrh 161 | https://s01.oss.sonatype.org/content/repositories/snapshots 162 | 163 | 164 | ossrh 165 | https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ 166 | 167 | 168 | 169 | 170 | 171 | org.apache.maven.plugins 172 | maven-gpg-plugin 173 | 1.6 174 | 175 | 176 | sign-artifacts 177 | verify 178 | 179 | sign 180 | 181 | 182 | 183 | 184 | 185 | --pinentry-mode 186 | loopback 187 | 188 | 189 | 190 | 191 | org.sonatype.plugins 192 | nexus-staging-maven-plugin 193 | 1.6.7 194 | true 195 | 196 | ossrh 197 | https://s01.oss.sonatype.org/ 198 | true 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/attribute/IntegerAttribute.java: -------------------------------------------------------------------------------- 1 | /** 2 | * $Id: IntegerAttribute.java,v 1.4 2005/09/04 22:11:03 wuttke Exp $ 3 | * Created on 08.04.2005 4 | * @author Matthias Wuttke 5 | * @version $Revision: 1.4 $ 6 | */ 7 | package org.tinyradius.attribute; 8 | 9 | import org.tinyradius.dictionary.AttributeType; 10 | import org.tinyradius.util.RadiusException; 11 | 12 | /** 13 | * This class represents a Radius attribute which only 14 | * contains a 32 bit integer. 15 | */ 16 | public class IntegerAttribute extends RadiusAttribute { 17 | 18 | /** 19 | * Constructs an empty integer attribute. 20 | */ 21 | public IntegerAttribute() { 22 | super(); 23 | } 24 | 25 | /** 26 | * Constructs an integer attribute with the given value. 27 | * @param type attribute type 28 | * @param value attribute value 29 | */ 30 | public IntegerAttribute(int type, int value) { 31 | setAttributeType(type); 32 | setAttributeValue(value); 33 | } 34 | 35 | /** 36 | * Returns the string value of this attribute. 37 | * @return a string 38 | */ 39 | public int getAttributeValueInt() { 40 | byte[] data = getAttributeData(); 41 | return (((data[0] & 0x0ff) << 24) | ((data[1] & 0x0ff) << 16) | 42 | ((data[2] & 0x0ff) << 8) | (data[3] & 0x0ff)); 43 | } 44 | 45 | /** 46 | * Returns the value of this attribute as a string. 47 | * Tries to resolve enumerations. 48 | * @see org.tinyradius.attribute.RadiusAttribute#getAttributeValue() 49 | */ 50 | public String getAttributeValue() { 51 | int value = getAttributeValueInt(); 52 | AttributeType at = getAttributeTypeObject(); 53 | if (at != null) { 54 | String name = at.getEnumeration(value); 55 | if (name != null) 56 | return name; 57 | } 58 | // Radius uses only unsigned values.... 59 | return Long.toString(((long)value & 0xffffffffl)); 60 | } 61 | 62 | /** 63 | * Sets the value of this attribute. 64 | * @param value integer value 65 | */ 66 | public void setAttributeValue(int value) { 67 | byte[] data = new byte[4]; 68 | data[0] = (byte)(value >> 24 & 0x0ff); 69 | data[1] = (byte)(value >> 16 & 0x0ff); 70 | data[2] = (byte)(value >> 8 & 0x0ff); 71 | data[3] = (byte)(value & 0x0ff); 72 | setAttributeData(data); 73 | } 74 | 75 | /** 76 | * Sets the value of this attribute. 77 | * @exception NumberFormatException if value is not a number and constant cannot be resolved 78 | * @see org.tinyradius.attribute.RadiusAttribute#setAttributeValue(java.lang.String) 79 | */ 80 | public void setAttributeValue(String value) { 81 | AttributeType at = getAttributeTypeObject(); 82 | if (at != null) { 83 | Integer val = at.getEnumeration(value); 84 | if (val != null) { 85 | setAttributeValue(val.intValue()); 86 | return; 87 | } 88 | } 89 | 90 | // Radius uses only unsigned integers for this the parser should consider as Long to parse high bit correctly... 91 | setAttributeValue((int)Long.parseLong(value)); 92 | } 93 | 94 | /** 95 | * Check attribute length. 96 | * @see org.tinyradius.attribute.RadiusAttribute#readAttribute(byte[], int, int) 97 | */ 98 | public void readAttribute(byte[] data, int offset, int length) 99 | throws RadiusException { 100 | if (length != 6) 101 | throw new RadiusException("integer attribute: expected 4 bytes data"); 102 | super.readAttribute(data, offset, length); 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/attribute/IpAttribute.java: -------------------------------------------------------------------------------- 1 | /** 2 | * $Id: IpAttribute.java,v 1.3 2005/09/06 16:38:41 wuttke Exp $ 3 | * Created on 10.04.2005 4 | * @author Matthias Wuttke 5 | * @version $Revision: 1.3 $ 6 | */ 7 | package org.tinyradius.attribute; 8 | 9 | import java.util.StringTokenizer; 10 | 11 | import org.tinyradius.util.RadiusException; 12 | 13 | /** 14 | * This class represents a Radius attribute for an IP number. 15 | */ 16 | public class IpAttribute extends RadiusAttribute { 17 | 18 | /** 19 | * Constructs an empty IP attribute. 20 | */ 21 | public IpAttribute() { 22 | super(); 23 | } 24 | 25 | /** 26 | * Constructs an IP attribute. 27 | * @param type attribute type code 28 | * @param value value, format: xx.xx.xx.xx 29 | */ 30 | public IpAttribute(int type, String value) { 31 | setAttributeType(type); 32 | setAttributeValue(value); 33 | } 34 | 35 | /** 36 | * Constructs an IP attribute. 37 | * @param type attribute type code 38 | * @param ipNum value as a 32 bit unsigned int 39 | */ 40 | public IpAttribute(int type, long ipNum) { 41 | setAttributeType(type); 42 | setIpAsLong(ipNum); 43 | } 44 | 45 | /** 46 | * Returns the attribute value (IP number) as a string of the 47 | * format "xx.xx.xx.xx". 48 | * @see org.tinyradius.attribute.RadiusAttribute#getAttributeValue() 49 | */ 50 | public String getAttributeValue() { 51 | StringBuffer ip = new StringBuffer(); 52 | byte[] data = getAttributeData(); 53 | if (data == null || data.length != 4) 54 | throw new RuntimeException("ip attribute: expected 4 bytes attribute data"); 55 | 56 | ip.append(data[0] & 0x0ff); 57 | ip.append("."); 58 | ip.append(data[1] & 0x0ff); 59 | ip.append("."); 60 | ip.append(data[2] & 0x0ff); 61 | ip.append("."); 62 | ip.append(data[3] & 0x0ff); 63 | 64 | return ip.toString(); 65 | } 66 | 67 | /** 68 | * Sets the attribute value (IP number). String format: 69 | * "xx.xx.xx.xx". 70 | * @throws IllegalArgumentException 71 | * @throws NumberFormatException 72 | * @see org.tinyradius.attribute.RadiusAttribute#setAttributeValue(java.lang.String) 73 | */ 74 | public void setAttributeValue(String value) { 75 | if (value == null || value.length() < 7 || value.length() > 15) 76 | throw new IllegalArgumentException("bad IP number"); 77 | 78 | StringTokenizer tok = new StringTokenizer(value, "."); 79 | if (tok.countTokens() != 4) 80 | throw new IllegalArgumentException("bad IP number: 4 numbers required"); 81 | 82 | byte[] data = new byte[4]; 83 | for (int i = 0; i < 4; i++) { 84 | int num = Integer.parseInt(tok.nextToken()); 85 | if (num < 0 || num > 255) 86 | throw new IllegalArgumentException("bad IP number: num out of bounds"); 87 | data[i] = (byte)num; 88 | } 89 | 90 | setAttributeData(data); 91 | } 92 | 93 | /** 94 | * Returns the IP number as a 32 bit unsigned number. The number is 95 | * returned in a long because Java does not support unsigned ints. 96 | * @return IP number 97 | */ 98 | public long getIpAsLong() { 99 | byte[] data = getAttributeData(); 100 | if (data == null || data.length != 4) 101 | throw new RuntimeException("expected 4 bytes attribute data"); 102 | return ((long)(data[0] & 0x0ff)) << 24 | (data[1] & 0x0ff) << 16 | 103 | (data[2] & 0x0ff) << 8 | (data[3] & 0x0ff); 104 | } 105 | 106 | /** 107 | * Sets the IP number represented by this IpAttribute 108 | * as a 32 bit unsigned number. 109 | * @param ip 110 | */ 111 | public void setIpAsLong(long ip) { 112 | byte[] data = new byte[4]; 113 | data[0] = (byte)((ip >> 24) & 0x0ff); 114 | data[1] = (byte)((ip >> 16) & 0x0ff); 115 | data[2] = (byte)((ip >> 8) & 0x0ff); 116 | data[3] = (byte)(ip & 0x0ff); 117 | setAttributeData(data); 118 | } 119 | 120 | /** 121 | * Check attribute length. 122 | * @see org.tinyradius.attribute.RadiusAttribute#readAttribute(byte[], int, int) 123 | */ 124 | public void readAttribute(byte[] data, int offset, int length) 125 | throws RadiusException { 126 | if (length != 6) 127 | throw new RadiusException("IP attribute: expected 4 bytes data"); 128 | super.readAttribute(data, offset, length); 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/attribute/Ipv6Attribute.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created on 24/Jun/2016 3 | * @author Ivan F. Martinez 4 | */ 5 | package org.tinyradius.attribute; 6 | 7 | import java.util.StringTokenizer; 8 | import java.net.Inet6Address; 9 | import java.net.UnknownHostException; 10 | 11 | import org.tinyradius.util.RadiusException; 12 | 13 | /** 14 | * This class represents a Radius attribute for an IPv6 number. 15 | */ 16 | public class Ipv6Attribute extends RadiusAttribute { 17 | 18 | /** 19 | * Constructs an empty IPv6 attribute. 20 | */ 21 | public Ipv6Attribute() { 22 | super(); 23 | } 24 | 25 | /** 26 | * Constructs an IPv6 attribute. 27 | * @param type attribute type code 28 | * @param value value, format:ipv6 address 29 | */ 30 | public Ipv6Attribute(int type, String value) { 31 | setAttributeType(type); 32 | setAttributeValue(value); 33 | } 34 | 35 | /** 36 | * Returns the attribute value (IPv6 number) as a string of the 37 | * format ipv6 address 38 | * @see org.tinyradius.attribute.RadiusAttribute#getAttributeValue() 39 | */ 40 | public String getAttributeValue() { 41 | byte[] data = getAttributeData(); 42 | if (data == null || data.length != 16) 43 | throw new RuntimeException("ip attribute: expected 16 bytes attribute data"); 44 | try { 45 | Inet6Address addr = (Inet6Address)Inet6Address.getByAddress(null, data); 46 | 47 | return addr.getHostAddress(); 48 | } catch (UnknownHostException e) { 49 | throw new IllegalArgumentException("bad IPv6 address", e); 50 | } 51 | 52 | } 53 | 54 | /** 55 | * Sets the attribute value (IPv6 number). String format: 56 | * ipv6 address. 57 | * @throws IllegalArgumentException 58 | * @throws NumberFormatException 59 | * @see org.tinyradius.attribute.RadiusAttribute#setAttributeValue(java.lang.String) 60 | */ 61 | public void setAttributeValue(String value) { 62 | if (value == null || value.length() < 3) 63 | throw new IllegalArgumentException("bad IPv6 address : " + value); 64 | try { 65 | final Inet6Address addr = (Inet6Address)Inet6Address.getByName(value); 66 | 67 | byte[] data = addr.getAddress(); 68 | 69 | setAttributeData(data); 70 | } catch (UnknownHostException e) { 71 | throw new IllegalArgumentException("bad IPv6 address : " + value, e); 72 | } 73 | } 74 | 75 | 76 | /** 77 | * Check attribute length. 78 | * @see org.tinyradius.attribute.RadiusAttribute#readAttribute(byte[], int, int) 79 | */ 80 | public void readAttribute(byte[] data, int offset, int length) 81 | throws RadiusException { 82 | if (length != 18) 83 | throw new RadiusException("IP attribute: expected 16 bytes data"); 84 | super.readAttribute(data, offset, length); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/attribute/Ipv6PrefixAttribute.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created on 24/Jun/2016 3 | * @author Ivan F. Martinez 4 | */ 5 | package org.tinyradius.attribute; 6 | 7 | import inet.ipaddr.AddressStringException; 8 | import inet.ipaddr.AddressValueException; 9 | import inet.ipaddr.IPAddressString; 10 | import inet.ipaddr.ipv6.IPv6Address; 11 | import org.tinyradius.util.RadiusException; 12 | 13 | import java.util.Arrays; 14 | 15 | /** 16 | * This class represents a Radius attribute for an IPv6 prefix. 17 | */ 18 | public class Ipv6PrefixAttribute extends RadiusAttribute { 19 | 20 | /** 21 | * Constructs an empty IP attribute. 22 | */ 23 | public Ipv6PrefixAttribute() { 24 | super(); 25 | } 26 | 27 | /** 28 | * Constructs an IPv6 prefix attribute. 29 | * @param type attribute type code 30 | * @param value value, format: "ipv6 address"/prefix 31 | */ 32 | public Ipv6PrefixAttribute(int type, String value) { 33 | setAttributeType(type); 34 | setAttributeValue(value); 35 | } 36 | 37 | /** 38 | * Returns the attribute value (IP number) as a string of the 39 | * format "xx.xx.xx.xx". 40 | * @see org.tinyradius.attribute.RadiusAttribute#getAttributeValue() 41 | */ 42 | public String getAttributeValue() { 43 | final byte[] data = getAttributeData(); 44 | if (data == null) throw new RuntimeException("ipv6 prefix attribute: expected 2-18 bytes attribute data and got null."); 45 | if (data.length < 2 || data.length > 18) 46 | throw new RuntimeException("ipv6 prefix attribute: expected 2-18 bytes attribute data and got " + data.length); 47 | try { 48 | final int prefixSize = (data[1] & 0xff); 49 | byte[] prefix = Arrays.copyOfRange(data,2,data.length); 50 | if(prefix.length < 16) { 51 | // Pad w/ trailing 0's if length not 128 bits (IPv6Address will pad w/ leading 0's if less than 128 bits) 52 | prefix = Arrays.copyOf(prefix, 16); 53 | } 54 | final IPv6Address ipv6prefix = new IPv6Address(prefix, prefixSize); 55 | return ipv6prefix.toString(); 56 | } catch (AddressValueException e) { 57 | throw new IllegalArgumentException("bad IPv6 prefix", e); 58 | } 59 | 60 | } 61 | 62 | /** 63 | * Sets the attribute value (IPv6 number/prefix). String format: 64 | * ipv6 address. 65 | * @throws IllegalArgumentException 66 | * @throws NumberFormatException 67 | * @see org.tinyradius.attribute.RadiusAttribute#setAttributeValue(java.lang.String) 68 | */ 69 | public void setAttributeValue(String value) { 70 | if (value == null || value.length() < 3) 71 | throw new IllegalArgumentException("bad IPv6 address : " + value); 72 | try { 73 | IPAddressString ipAddressString = new IPAddressString(value); 74 | if( !ipAddressString.isIPAddress() || !ipAddressString.isIPv6() ) 75 | throw new IllegalArgumentException("bad IPv6 address : " + value); 76 | 77 | IPv6Address ipv6Prefix = ipAddressString.toAddress().toIPv6(); 78 | 79 | final byte[] data = new byte[18]; 80 | data[0] = 0; 81 | data[1] = (byte) (ipv6Prefix.getPrefixLength() & 0xff); 82 | 83 | byte[] ipData = ipv6Prefix.getNetworkSection().getBytes(); 84 | for (int i = 0; i < ipData.length; i++) { 85 | data[i + 2] = ipData[i]; 86 | } 87 | 88 | setAttributeData(data); 89 | 90 | } catch (AddressStringException e) { 91 | throw new IllegalArgumentException("bad IPv6 address : " + value, e); 92 | } 93 | } 94 | 95 | 96 | /** 97 | * Check attribute length. 98 | * @see org.tinyradius.attribute.RadiusAttribute#readAttribute(byte[], int, int) 99 | */ 100 | public void readAttribute(byte[] data, int offset, int length) 101 | throws RadiusException { 102 | if (length > 20 || length < 4) 103 | throw new RadiusException("IPv6 prefix attribute: expected 4-20 bytes data"); 104 | super.readAttribute(data, offset, length); 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/attribute/RadiusAttribute.java: -------------------------------------------------------------------------------- 1 | /** 2 | * $Id: RadiusAttribute.java,v 1.4 2006/02/20 23:37:38 wuttke Exp $ 3 | * Created on 07.04.2005 4 | * Released under the terms of the LGPL 5 | * 6 | * @author Matthias Wuttke 7 | * @version $Revision: 1.4 $ 8 | */ 9 | package org.tinyradius.attribute; 10 | 11 | import org.tinyradius.dictionary.AttributeType; 12 | import org.tinyradius.dictionary.DefaultDictionary; 13 | import org.tinyradius.dictionary.Dictionary; 14 | import org.tinyradius.util.RadiusException; 15 | import org.tinyradius.util.RadiusUtil; 16 | 17 | /** 18 | * This class represents a generic Radius attribute. Subclasses implement 19 | * methods to access the fields of special attributes. 20 | */ 21 | public class RadiusAttribute { 22 | 23 | /** 24 | * Constructs an empty Radius attribute. 25 | */ 26 | public RadiusAttribute() { 27 | } 28 | 29 | /** 30 | * Constructs a Radius attribute with the specified 31 | * type and data. 32 | * 33 | * @param type 34 | * attribute type, see AttributeTypes.* 35 | * @param data 36 | * attribute data 37 | */ 38 | public RadiusAttribute(int type, byte[] data) { 39 | setAttributeType(type); 40 | setAttributeData(data); 41 | } 42 | 43 | /** 44 | * Returns the data for this attribute. 45 | * 46 | * @return attribute data 47 | */ 48 | public byte[] getAttributeData() { 49 | return attributeData; 50 | } 51 | 52 | /** 53 | * Sets the data for this attribute. 54 | * 55 | * @param attributeData 56 | * attribute data 57 | */ 58 | public void setAttributeData(byte[] attributeData) { 59 | if (attributeData == null) 60 | throw new NullPointerException("attribute data is null"); 61 | this.attributeData = attributeData; 62 | } 63 | 64 | /** 65 | * Returns the type of this Radius attribute. 66 | * 67 | * @return type code, 0-255 68 | */ 69 | public int getAttributeType() { 70 | return attributeType; 71 | } 72 | 73 | /** 74 | * Sets the type of this Radius attribute. 75 | * 76 | * @param attributeType 77 | * type code, 0-255 78 | */ 79 | public void setAttributeType(int attributeType) { 80 | if (attributeType < 0 || attributeType > 255) 81 | throw new IllegalArgumentException("attribute type invalid: " + attributeType); 82 | this.attributeType = attributeType; 83 | } 84 | 85 | /** 86 | * Sets the value of the attribute using a string. 87 | * 88 | * @param value 89 | * value as a string 90 | */ 91 | public void setAttributeValue(String value) { 92 | throw new RuntimeException("cannot set the value of attribute " + attributeType + " as a string"); 93 | } 94 | 95 | /** 96 | * Gets the value of this attribute as a string. 97 | * 98 | * @return value 99 | */ 100 | public String getAttributeValue() { 101 | return RadiusUtil.getHexString(getAttributeData()); 102 | } 103 | 104 | /** 105 | * Gets the Vendor-Id of the Vendor-Specific attribute this 106 | * attribute belongs to. Returns -1 if this attribute is not 107 | * a sub attribute of a Vendor-Specific attribute. 108 | * 109 | * @return vendor ID 110 | */ 111 | public int getVendorId() { 112 | return vendorId; 113 | } 114 | 115 | /** 116 | * Sets the Vendor-Id of the Vendor-Specific attribute this 117 | * attribute belongs to. The default value of -1 means this attribute 118 | * is not a sub attribute of a Vendor-Specific attribute. 119 | * 120 | * @param vendorId 121 | * vendor ID 122 | */ 123 | public void setVendorId(int vendorId) { 124 | this.vendorId = vendorId; 125 | } 126 | 127 | /** 128 | * Returns the dictionary this Radius attribute uses. 129 | * 130 | * @return Dictionary instance 131 | */ 132 | public Dictionary getDictionary() { 133 | return dictionary; 134 | } 135 | 136 | /** 137 | * Sets a custom dictionary to use. If no dictionary is set, 138 | * the default dictionary is used. 139 | * 140 | * @param dictionary 141 | * Dictionary class to use 142 | * @see DefaultDictionary 143 | */ 144 | public void setDictionary(Dictionary dictionary) { 145 | this.dictionary = dictionary; 146 | } 147 | 148 | /** 149 | * Returns this attribute encoded as a byte array. 150 | * 151 | * @return attribute 152 | */ 153 | public byte[] writeAttribute() { 154 | if (getAttributeType() == -1) 155 | throw new IllegalArgumentException("attribute type not set"); 156 | if (attributeData == null) 157 | throw new NullPointerException("attribute data not set"); 158 | 159 | byte[] attr = new byte[2 + attributeData.length]; 160 | attr[0] = (byte) getAttributeType(); 161 | attr[1] = (byte) (2 + attributeData.length); 162 | System.arraycopy(attributeData, 0, attr, 2, attributeData.length); 163 | return attr; 164 | } 165 | 166 | /** 167 | * Reads in this attribute from the passed byte array. 168 | * @param data data buffer 169 | * @param offset the offset to read 170 | * @param length the amount of data to read 171 | * @throws RadiusException when length is less than 2 172 | */ 173 | public void readAttribute(byte[] data, int offset, int length) throws RadiusException { 174 | if (length < 2) 175 | throw new RadiusException("attribute length too small: " + length); 176 | int attrType = data[offset] & 0x0ff; 177 | int attrLen = data[offset + 1] & 0x0ff; 178 | byte[] attrData = new byte[attrLen - 2]; 179 | System.arraycopy(data, offset + 2, attrData, 0, attrLen - 2); 180 | setAttributeType(attrType); 181 | setAttributeData(attrData); 182 | } 183 | 184 | /** 185 | * String representation for debugging purposes. 186 | * 187 | * @see java.lang.Object#toString() 188 | */ 189 | public String toString() { 190 | String name; 191 | 192 | // determine attribute name 193 | AttributeType at = getAttributeTypeObject(); 194 | if (at != null) 195 | name = at.getName(); 196 | else if (getVendorId() != -1) 197 | name = "Unknown-Sub-Attribute-" + getAttributeType(); 198 | else 199 | name = "Unknown-Attribute-" + getAttributeType(); 200 | 201 | // indent sub attributes 202 | if (getVendorId() != -1) 203 | name = " " + name; 204 | 205 | return name + ": " + getAttributeValue(); 206 | } 207 | 208 | /** 209 | * Retrieves an AttributeType object for this attribute. 210 | * 211 | * @return AttributeType object for (sub-)attribute or null 212 | */ 213 | public AttributeType getAttributeTypeObject() { 214 | if (getVendorId() != -1) { 215 | return dictionary.getAttributeTypeByCode(getVendorId(), getAttributeType()); 216 | } 217 | return dictionary.getAttributeTypeByCode(getAttributeType()); 218 | } 219 | 220 | /** 221 | * Creates a RadiusAttribute object of the appropriate type. 222 | * 223 | * @param dictionary 224 | * Dictionary to use 225 | * @param vendorId 226 | * vendor ID or -1 227 | * @param attributeType 228 | * attribute type 229 | * @return RadiusAttribute object 230 | */ 231 | public static RadiusAttribute createRadiusAttribute(Dictionary dictionary, int vendorId, int attributeType) { 232 | RadiusAttribute attribute = new RadiusAttribute(); 233 | 234 | AttributeType at = dictionary.getAttributeTypeByCode(vendorId, attributeType); 235 | if (at != null && at.getAttributeClass() != null) { 236 | try { 237 | attribute = (RadiusAttribute) at.getAttributeClass().newInstance(); 238 | } 239 | catch (Exception e) { 240 | // error instantiating class - should not occur 241 | } 242 | } 243 | 244 | attribute.setAttributeType(attributeType); 245 | attribute.setDictionary(dictionary); 246 | attribute.setVendorId(vendorId); 247 | return attribute; 248 | } 249 | 250 | /** 251 | * Creates a Radius attribute, including vendor-specific 252 | * attributes. The default dictionary is used. 253 | * 254 | * @param vendorId 255 | * vendor ID or -1 256 | * @param attributeType 257 | * attribute type 258 | * @return RadiusAttribute instance 259 | */ 260 | public static RadiusAttribute createRadiusAttribute(int vendorId, int attributeType) { 261 | Dictionary dictionary = DefaultDictionary.getDefaultDictionary(); 262 | return createRadiusAttribute(dictionary, vendorId, attributeType); 263 | } 264 | 265 | /** 266 | * Creates a Radius attribute. The default dictionary is 267 | * used. 268 | * 269 | * @param attributeType 270 | * attribute type 271 | * @return RadiusAttribute instance 272 | */ 273 | public static RadiusAttribute createRadiusAttribute(int attributeType) { 274 | Dictionary dictionary = DefaultDictionary.getDefaultDictionary(); 275 | return createRadiusAttribute(dictionary, -1, attributeType); 276 | } 277 | 278 | /** 279 | * Dictionary to look up attribute names. 280 | */ 281 | private Dictionary dictionary = DefaultDictionary.getDefaultDictionary(); 282 | 283 | /** 284 | * Attribute type 285 | */ 286 | private int attributeType = -1; 287 | 288 | /** 289 | * Vendor ID, only for sub-attributes of Vendor-Specific attributes. 290 | */ 291 | private int vendorId = -1; 292 | 293 | /** 294 | * Attribute data 295 | */ 296 | private byte[] attributeData = null; 297 | 298 | } 299 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/attribute/StringAttribute.java: -------------------------------------------------------------------------------- 1 | /** 2 | * $Id: StringAttribute.java,v 1.1.1.1 2005/04/17 14:51:33 wuttke Exp $ 3 | * Created on 08.04.2005 4 | * @author Matthias Wuttke 5 | * @version $Revision: 1.1.1.1 $ 6 | */ 7 | package org.tinyradius.attribute; 8 | 9 | import java.io.UnsupportedEncodingException; 10 | 11 | /** 12 | * This class represents a Radius attribute which only 13 | * contains a string. 14 | */ 15 | public class StringAttribute extends RadiusAttribute { 16 | 17 | /** 18 | * Constructs an empty string attribute. 19 | */ 20 | public StringAttribute() { 21 | super(); 22 | } 23 | 24 | /** 25 | * Constructs a string attribute with the given value. 26 | * @param type attribute type 27 | * @param value attribute value 28 | */ 29 | public StringAttribute(int type, String value) { 30 | setAttributeType(type); 31 | setAttributeValue(value); 32 | } 33 | 34 | /** 35 | * Returns the string value of this attribute. 36 | * @return a string 37 | */ 38 | public String getAttributeValue() { 39 | try { 40 | return new String(getAttributeData(), "UTF-8"); 41 | } catch (UnsupportedEncodingException uee) { 42 | return new String(getAttributeData()); 43 | } 44 | } 45 | 46 | /** 47 | * Sets the string value of this attribute. 48 | * @param value string, not null 49 | */ 50 | public void setAttributeValue(String value) { 51 | if (value == null) 52 | throw new NullPointerException("string value not set"); 53 | try { 54 | setAttributeData(value.getBytes("UTF-8")); 55 | } catch (UnsupportedEncodingException uee) { 56 | setAttributeData(value.getBytes()); 57 | } 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/attribute/VendorSpecificAttribute.java: -------------------------------------------------------------------------------- 1 | /** 2 | * $Id: VendorSpecificAttribute.java,v 1.7 2005/11/22 10:18:38 wuttke Exp $ 3 | * Created on 10.04.2005 4 | * 5 | * @author Matthias Wuttke 6 | * @version $Revision: 1.7 $ 7 | */ 8 | package org.tinyradius.attribute; 9 | 10 | import java.io.ByteArrayOutputStream; 11 | import java.io.IOException; 12 | import java.util.ArrayList; 13 | import java.util.Iterator; 14 | import java.util.LinkedList; 15 | import java.util.List; 16 | import org.tinyradius.dictionary.AttributeType; 17 | import org.tinyradius.dictionary.Dictionary; 18 | import org.tinyradius.util.RadiusException; 19 | 20 | /** 21 | * This class represents a "Vendor-Specific" attribute. 22 | */ 23 | public class VendorSpecificAttribute extends RadiusAttribute { 24 | 25 | /** 26 | * Radius attribute type code for Vendor-Specific 27 | */ 28 | public static final int VENDOR_SPECIFIC = 26; 29 | 30 | /** 31 | * Constructs an empty Vendor-Specific attribute that can be read from a 32 | * Radius packet. 33 | */ 34 | public VendorSpecificAttribute() { 35 | super(); 36 | } 37 | 38 | /** 39 | * Constructs a new Vendor-Specific attribute to be sent. 40 | * 41 | * @param vendorId 42 | * vendor ID of the sub-attributes 43 | */ 44 | public VendorSpecificAttribute(int vendorId) { 45 | setAttributeType(VENDOR_SPECIFIC); 46 | setChildVendorId(vendorId); 47 | } 48 | 49 | /** 50 | * Sets the vendor ID of the child attributes. 51 | * 52 | * @param childVendorId 53 | */ 54 | public void setChildVendorId(int childVendorId) { 55 | this.childVendorId = childVendorId; 56 | } 57 | 58 | /** 59 | * Returns the vendor ID of the sub-attributes. 60 | * 61 | * @return vendor ID of sub attributes 62 | */ 63 | public int getChildVendorId() { 64 | return childVendorId; 65 | } 66 | 67 | /** 68 | * Also copies the new dictionary to sub-attributes. 69 | * 70 | * @param dictionary 71 | * dictionary to set 72 | * @see org.tinyradius.attribute.RadiusAttribute#setDictionary(org.tinyradius.dictionary.Dictionary) 73 | */ 74 | public void setDictionary(Dictionary dictionary) { 75 | super.setDictionary(dictionary); 76 | for (Iterator i = subAttributes.iterator(); i.hasNext();) { 77 | RadiusAttribute attr = (RadiusAttribute) i.next(); 78 | attr.setDictionary(dictionary); 79 | } 80 | } 81 | 82 | /** 83 | * Adds a sub-attribute to this attribute. 84 | * 85 | * @param attribute 86 | * sub-attribute to add 87 | */ 88 | public void addSubAttribute(RadiusAttribute attribute) { 89 | if (attribute.getVendorId() != getChildVendorId()) 90 | throw new IllegalArgumentException("sub attribut has incorrect vendor ID"); 91 | 92 | subAttributes.add(attribute); 93 | } 94 | 95 | /** 96 | * Adds a sub-attribute with the specified name to this attribute. 97 | * 98 | * @param name 99 | * name of the sub-attribute 100 | * @param value 101 | * value of the sub-attribute 102 | * @exception IllegalArgumentException 103 | * invalid sub-attribute name or value 104 | */ 105 | public void addSubAttribute(String name, String value) { 106 | if (name == null || name.length() == 0) 107 | throw new IllegalArgumentException("type name is empty"); 108 | if (value == null || value.length() == 0) 109 | throw new IllegalArgumentException("value is empty"); 110 | 111 | AttributeType type = getDictionary().getAttributeTypeByName(name); 112 | if (type == null) 113 | throw new IllegalArgumentException("unknown attribute type '" + name + "'"); 114 | if (type.getVendorId() == -1) 115 | throw new IllegalArgumentException("attribute type '" + name + "' is not a Vendor-Specific sub-attribute"); 116 | if (type.getVendorId() != getChildVendorId()) 117 | throw new IllegalArgumentException("attribute type '" + name + "' does not belong to vendor ID " + getChildVendorId()); 118 | 119 | RadiusAttribute attribute = createRadiusAttribute(getDictionary(), getChildVendorId(), type.getTypeCode()); 120 | attribute.setAttributeValue(value); 121 | addSubAttribute(attribute); 122 | } 123 | 124 | /** 125 | * Removes the specified sub-attribute from this attribute. 126 | * 127 | * @param attribute 128 | * RadiusAttribute to remove 129 | */ 130 | public void removeSubAttribute(RadiusAttribute attribute) { 131 | if (!subAttributes.remove(attribute)) 132 | throw new IllegalArgumentException("no such attribute"); 133 | } 134 | 135 | /** 136 | * Returns the list of sub-attributes. 137 | * 138 | * @return List of RadiusAttribute objects 139 | */ 140 | public List getSubAttributes() { 141 | return subAttributes; 142 | } 143 | 144 | /** 145 | * Returns all sub-attributes of this attribut which have the given type. 146 | * 147 | * @param attributeType 148 | * type of sub-attributes to get 149 | * @return list of RadiusAttribute objects, does not return null 150 | */ 151 | public List getSubAttributes(int attributeType) { 152 | if (attributeType < 1 || attributeType > 255) 153 | throw new IllegalArgumentException("sub-attribute type out of bounds"); 154 | 155 | LinkedList result = new LinkedList(); 156 | for (Iterator i = subAttributes.iterator(); i.hasNext();) { 157 | RadiusAttribute a = (RadiusAttribute) i.next(); 158 | if (attributeType == a.getAttributeType()) 159 | result.add(a); 160 | } 161 | return result; 162 | } 163 | 164 | /** 165 | * Returns a sub-attribute of the given type which may only occur once in 166 | * this attribute. 167 | * 168 | * @param type 169 | * sub-attribute type 170 | * @return RadiusAttribute object or null if there is no such sub-attribute 171 | * @throws RuntimeException 172 | * if there are multiple occurences of the 173 | * requested sub-attribute type 174 | */ 175 | public RadiusAttribute getSubAttribute(int type) { 176 | List attrs = getSubAttributes(type); 177 | if (attrs.size() > 1) 178 | throw new RuntimeException("multiple sub-attributes of requested type " + type); 179 | else if (attrs.size() == 0) 180 | return null; 181 | else 182 | return (RadiusAttribute) attrs.get(0); 183 | } 184 | 185 | /** 186 | * Returns a single sub-attribute of the given type name. 187 | * 188 | * @param type 189 | * attribute type name 190 | * @return RadiusAttribute object or null if there is no such attribute 191 | * @throws RadiusException 192 | * @throws RuntimeException 193 | * if the attribute occurs multiple times 194 | */ 195 | public RadiusAttribute getSubAttribute(String type) throws RadiusException { 196 | if (type == null || type.length() == 0) 197 | throw new IllegalArgumentException("type name is empty"); 198 | 199 | AttributeType t = getDictionary().getAttributeTypeByName(type); 200 | if (t == null) 201 | throw new IllegalArgumentException("unknown attribute type name '" + type + "'"); 202 | if (t.getVendorId() != getChildVendorId()) 203 | throw new IllegalArgumentException("vendor ID mismatch"); 204 | 205 | return getSubAttribute(t.getTypeCode()); 206 | } 207 | 208 | /** 209 | * Returns the value of the Radius attribute of the given type or null if 210 | * there is no such attribute. 211 | * 212 | * @param type 213 | * attribute type name 214 | * @return value of the attribute as a string or null if there is no such 215 | * attribute 216 | * @throws IllegalArgumentException 217 | * if the type name is unknown 218 | * @throws RuntimeException 219 | * attribute occurs multiple times 220 | */ 221 | public String getSubAttributeValue(String type) throws RadiusException { 222 | RadiusAttribute attr = getSubAttribute(type); 223 | if (attr == null) { 224 | return null; 225 | } 226 | return attr.getAttributeValue(); 227 | } 228 | 229 | /** 230 | * Renders this attribute as a byte array. 231 | * 232 | * @see org.tinyradius.attribute.RadiusAttribute#writeAttribute() 233 | */ 234 | public byte[] writeAttribute() { 235 | // write vendor ID 236 | ByteArrayOutputStream bos = new ByteArrayOutputStream(255); 237 | bos.write(getChildVendorId() >> 24 & 0x0ff); 238 | bos.write(getChildVendorId() >> 16 & 0x0ff); 239 | bos.write(getChildVendorId() >> 8 & 0x0ff); 240 | bos.write(getChildVendorId() & 0x0ff); 241 | 242 | // write sub-attributes 243 | try { 244 | for (Iterator i = subAttributes.iterator(); i.hasNext();) { 245 | RadiusAttribute a = (RadiusAttribute) i.next(); 246 | bos.write(a.writeAttribute()); 247 | } 248 | } 249 | catch (IOException ioe) { 250 | // occurs never 251 | throw new RuntimeException("error writing data", ioe); 252 | } 253 | 254 | // check data length 255 | byte[] attrData = bos.toByteArray(); 256 | int len = attrData.length; 257 | if (len > 253) 258 | throw new RuntimeException("Vendor-Specific attribute too long: " + bos.size()); 259 | 260 | // compose attribute 261 | byte[] attr = new byte[len + 2]; 262 | attr[0] = VENDOR_SPECIFIC; // code 263 | attr[1] = (byte) (len + 2); // length 264 | System.arraycopy(attrData, 0, attr, 2, len); 265 | return attr; 266 | } 267 | 268 | /** 269 | * Reads a Vendor-Specific attribute and decodes the internal sub-attribute 270 | * structure. 271 | * 272 | * @see org.tinyradius.attribute.RadiusAttribute#readAttribute(byte[], int, int) 273 | */ 274 | public void readAttribute(byte[] data, int offset, int length) throws RadiusException { 275 | // check length 276 | if (length < 6) 277 | throw new RadiusException("Vendor-Specific attribute too short: " + length); 278 | 279 | int vsaCode = data[offset]; 280 | int vsaLen = (data[offset + 1] & 0x000000ff) - 6; 281 | 282 | if (vsaCode != VENDOR_SPECIFIC) 283 | throw new RadiusException("not a Vendor-Specific attribute"); 284 | 285 | // read vendor ID and vendor data 286 | /* 287 | * int vendorId = (data[offset + 2] << 24 | data[offset + 3] << 16 | 288 | * data[offset + 4] << 8 | ((int)data[offset + 5] & 0x000000ff)); 289 | */ 290 | int vendorId = (unsignedByteToInt(data[offset + 2]) << 24 | unsignedByteToInt(data[offset + 3]) << 16 291 | | unsignedByteToInt(data[offset + 4]) << 8 | unsignedByteToInt(data[offset + 5])); 292 | setChildVendorId(vendorId); 293 | 294 | // validate sub-attribute structure 295 | int pos = 0; 296 | int count = 0; 297 | while (pos < vsaLen) { 298 | if (pos + 1 >= vsaLen) 299 | throw new RadiusException("Vendor-Specific attribute malformed"); 300 | // int vsaSubType = data[(offset + 6) + pos] & 0x0ff; 301 | int vsaSubLen = data[(offset + 6) + pos + 1] & 0x0ff; 302 | pos += vsaSubLen; 303 | count++; 304 | } 305 | if (pos != vsaLen) 306 | throw new RadiusException("Vendor-Specific attribute malformed"); 307 | 308 | subAttributes = new ArrayList(count); 309 | pos = 0; 310 | while (pos < vsaLen) { 311 | int subtype = data[(offset + 6) + pos] & 0x0ff; 312 | int sublength = data[(offset + 6) + pos + 1] & 0x0ff; 313 | RadiusAttribute a = createRadiusAttribute(getDictionary(), vendorId, subtype); 314 | a.readAttribute(data, (offset + 6) + pos, sublength); 315 | subAttributes.add(a); 316 | pos += sublength; 317 | } 318 | } 319 | 320 | private static int unsignedByteToInt(byte b) { 321 | return b & 0xFF; 322 | } 323 | 324 | /** 325 | * Returns a string representation for debugging. 326 | * 327 | * @see org.tinyradius.attribute.RadiusAttribute#toString() 328 | */ 329 | public String toString() { 330 | StringBuffer sb = new StringBuffer(); 331 | sb.append("Vendor-Specific: "); 332 | int vendorId = getChildVendorId(); 333 | String vendorName = getDictionary().getVendorName(vendorId); 334 | if (vendorName != null) { 335 | sb.append(vendorName); 336 | sb.append(" ("); 337 | sb.append(vendorId); 338 | sb.append(")"); 339 | } 340 | else { 341 | sb.append("vendor ID "); 342 | sb.append(vendorId); 343 | } 344 | for (Iterator i = getSubAttributes().iterator(); i.hasNext();) { 345 | RadiusAttribute attr = (RadiusAttribute) i.next(); 346 | sb.append("\n"); 347 | sb.append(attr.toString()); 348 | } 349 | return sb.toString(); 350 | } 351 | 352 | /** 353 | * Sub attributes. Only set if isRawData == false. 354 | */ 355 | private List subAttributes = new ArrayList(); 356 | 357 | /** 358 | * Vendor ID of sub-attributes. 359 | */ 360 | private int childVendorId; 361 | } 362 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/attribute/package.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

This package contains classes for Radius attributes and Radius 6 | attribute types.

7 | 8 |

The classes RadiusAttribute, StringAttribute, IntegerAttribute and 9 | IpAttribute implement general Radius attribute data types and manage 10 | the storage of raw data, strings, integers and IP numbers.

11 | 12 |

Vendor-specific attributes are supported by the class 13 | VendorSpecificAttribute.

14 | 15 | 16 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/dictionary/AttributeType.java: -------------------------------------------------------------------------------- 1 | /** 2 | * $Id: AttributeType.java,v 1.3 2005/09/06 18:06:33 wuttke Exp $ 3 | * 4 | * @author Matthias Wuttke 5 | * @version $Revision: 1.3 $ 6 | */ 7 | package org.tinyradius.dictionary; 8 | 9 | import java.util.HashMap; 10 | import java.util.Iterator; 11 | import java.util.Map; 12 | import org.tinyradius.attribute.RadiusAttribute; 13 | 14 | /** 15 | * Represents a Radius attribute type. 16 | */ 17 | public class AttributeType { 18 | 19 | /** 20 | * Create a new attribute type. 21 | * 22 | * @param code 23 | * Radius attribute type code 24 | * @param name 25 | * Attribute type name 26 | * @param type 27 | * RadiusAttribute descendant who handles 28 | * attributes of this type 29 | */ 30 | public AttributeType(int code, String name, Class type) { 31 | setTypeCode(code); 32 | setName(name); 33 | setAttributeClass(type); 34 | } 35 | 36 | /** 37 | * Constructs a Vendor-Specific sub-attribute type. 38 | * 39 | * @param vendor 40 | * vendor ID 41 | * @param code 42 | * sub-attribute type code 43 | * @param name 44 | * sub-attribute name 45 | * @param type 46 | * sub-attribute class 47 | */ 48 | public AttributeType(int vendor, int code, String name, Class type) { 49 | setTypeCode(code); 50 | setName(name); 51 | setAttributeClass(type); 52 | setVendorId(vendor); 53 | } 54 | 55 | /** 56 | * Retrieves the Radius type code for this attribute type. 57 | * 58 | * @return Radius type code 59 | */ 60 | public int getTypeCode() { 61 | return typeCode; 62 | } 63 | 64 | /** 65 | * Sets the Radius type code for this attribute type. 66 | * 67 | * @param code 68 | * type code, 1-255 69 | */ 70 | public void setTypeCode(int code) { 71 | if (code < 1 || code > 255) 72 | throw new IllegalArgumentException("code out of bounds"); 73 | this.typeCode = code; 74 | } 75 | 76 | /** 77 | * Retrieves the name of this type. 78 | * 79 | * @return name 80 | */ 81 | public String getName() { 82 | return name; 83 | } 84 | 85 | /** 86 | * Sets the name of this type. 87 | * 88 | * @param name 89 | * type name 90 | */ 91 | public void setName(String name) { 92 | if (name == null || name.length() == 0) 93 | throw new IllegalArgumentException("name is empty"); 94 | this.name = name; 95 | } 96 | 97 | /** 98 | * Retrieves the RadiusAttribute descendant class which represents 99 | * attributes of this type. 100 | * 101 | * @return class 102 | */ 103 | public Class getAttributeClass() { 104 | return attributeClass; 105 | } 106 | 107 | /** 108 | * Sets the RadiusAttribute descendant class which represents 109 | * attributes of this type. 110 | */ 111 | public void setAttributeClass(Class type) { 112 | if (type == null) 113 | throw new NullPointerException("type is null"); 114 | if (!RadiusAttribute.class.isAssignableFrom(type)) 115 | throw new IllegalArgumentException("type is not a RadiusAttribute descendant"); 116 | this.attributeClass = type; 117 | } 118 | 119 | /** 120 | * Returns the vendor ID. 121 | * No vendor specific attribute = -1 122 | * 123 | * @return vendor ID 124 | */ 125 | public int getVendorId() { 126 | return vendorId; 127 | } 128 | 129 | /** 130 | * Sets the vendor ID. 131 | * 132 | * @param vendorId 133 | * vendor ID 134 | */ 135 | public void setVendorId(int vendorId) { 136 | this.vendorId = vendorId; 137 | } 138 | 139 | /** 140 | * Returns the name of the given integer value if this attribute 141 | * is an enumeration, or null if it is not or if the integer value 142 | * is unknown. 143 | * 144 | * @return name 145 | */ 146 | public String getEnumeration(int value) { 147 | if (enumeration != null) { 148 | return (String) enumeration.get(new Integer(value)); 149 | } 150 | return null; 151 | } 152 | 153 | /** 154 | * Returns the number of the given string value if this attribute is 155 | * an enumeration, or null if it is not or if the string value is unknown. 156 | * 157 | * @param value 158 | * string value 159 | * @return Integer or null 160 | */ 161 | public Integer getEnumeration(String value) { 162 | if (value == null || value.length() == 0) 163 | throw new IllegalArgumentException("value is empty"); 164 | if (enumeration == null) 165 | return null; 166 | for (Iterator i = enumeration.entrySet().iterator(); i.hasNext();) { 167 | Map.Entry e = (Map.Entry) i.next(); 168 | if (e.getValue().equals(value)) 169 | return (Integer) e.getKey(); 170 | } 171 | return null; 172 | } 173 | 174 | /** 175 | * Adds a name for an integer value of this attribute. 176 | * 177 | * @param num 178 | * number that shall get a name 179 | * @param name 180 | * the name for this number 181 | */ 182 | public void addEnumerationValue(int num, String name) { 183 | if (name == null || name.length() == 0) 184 | throw new IllegalArgumentException("name is empty"); 185 | if (enumeration == null) 186 | enumeration = new HashMap(); 187 | enumeration.put(new Integer(num), name); 188 | } 189 | 190 | /** 191 | * String representation of AttributeType object 192 | * for debugging purposes. 193 | * 194 | * @return string 195 | * @see java.lang.Object#toString() 196 | */ 197 | public String toString() { 198 | String s = getTypeCode() + "/" + getName() + ": " + attributeClass.getName(); 199 | if (getVendorId() != -1) 200 | s += " (vendor " + getVendorId() + ")"; 201 | return s; 202 | } 203 | 204 | private int vendorId = -1; 205 | private int typeCode; 206 | private String name; 207 | private Class attributeClass; 208 | private Map enumeration = null; 209 | 210 | } 211 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/dictionary/DefaultDictionary.java: -------------------------------------------------------------------------------- 1 | /** 2 | * $Id: DefaultDictionary.java,v 1.1 2005/09/04 22:11:00 wuttke Exp $ 3 | * Created on 28.08.2005 4 | * @author mw 5 | * @version $Revision: 1.1 $ 6 | */ 7 | package org.tinyradius.dictionary; 8 | 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | 12 | /** 13 | * The default dictionary is a singleton object containing 14 | * a dictionary in the memory that is filled on application 15 | * startup using the default dictionary file from the 16 | * classpath resource 17 | * org.tinyradius.dictionary.default_dictionary. 18 | */ 19 | public class DefaultDictionary 20 | extends MemoryDictionary{ 21 | 22 | /** 23 | * Returns the singleton instance of this object. 24 | * @return DefaultDictionary instance 25 | */ 26 | public static Dictionary getDefaultDictionary() { 27 | return instance; 28 | } 29 | 30 | /** 31 | * Make constructor private so that a DefaultDictionary 32 | * cannot be constructed by other classes. 33 | */ 34 | private DefaultDictionary() { 35 | } 36 | 37 | private static final String DICTIONARY_RESOURCE = "org/tinyradius/dictionary/default_dictionary"; 38 | private static DefaultDictionary instance = null; 39 | 40 | /** 41 | * Creates the singleton instance of this object 42 | * and parses the classpath ressource. 43 | */ 44 | static { 45 | try { 46 | instance = new DefaultDictionary(); 47 | ClassLoader classLoader = DefaultDictionary.class.getClassLoader(); 48 | InputStream source = classLoader.getResourceAsStream("tinyradius_dictionary"); 49 | if (source == null) { 50 | source = classLoader.getResourceAsStream(DICTIONARY_RESOURCE); 51 | } 52 | DictionaryParser.parseDictionary(source, instance); 53 | } catch (IOException e) { 54 | throw new RuntimeException("default dictionary unavailable", e); 55 | } 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/dictionary/Dictionary.java: -------------------------------------------------------------------------------- 1 | /** 2 | * $Id: Dictionary.java,v 1.1 2005/09/04 22:11:00 wuttke Exp $ 3 | * Created on 28.08.2005 4 | * @author mw 5 | * @version $Revision: 1.1 $ 6 | */ 7 | package org.tinyradius.dictionary; 8 | 9 | /** 10 | * A dictionary retrieves AttributeType objects by name or 11 | * type code. 12 | */ 13 | public interface Dictionary { 14 | 15 | /** 16 | * Retrieves an attribute type by name. This includes 17 | * vendor-specific attribute types whose name is prefixed 18 | * by the vendor name. 19 | * @param typeName name of the attribute type 20 | * @return AttributeType object or null 21 | */ 22 | public AttributeType getAttributeTypeByName(String typeName); 23 | 24 | /** 25 | * Retrieves an attribute type by type code. This method 26 | * does not retrieve vendor specific attribute types. 27 | * @param typeCode type code, 1-255 28 | * @return AttributeType object or null 29 | */ 30 | public AttributeType getAttributeTypeByCode(int typeCode); 31 | 32 | /** 33 | * Retrieves an attribute type for a vendor-specific 34 | * attribute. 35 | * @param vendorId vendor ID 36 | * @param typeCode type code, 1-255 37 | * @return AttributeType object or null 38 | */ 39 | public AttributeType getAttributeTypeByCode(int vendorId, int typeCode); 40 | 41 | /** 42 | * Retrieves the name of the vendor with the given 43 | * vendor code. 44 | * @param vendorId vendor number 45 | * @return vendor name or null 46 | */ 47 | public String getVendorName(int vendorId); 48 | 49 | /** 50 | * Retrieves the ID of the vendor with the given 51 | * name. 52 | * @param vendorName name of the vendor 53 | * @return vendor ID or -1 54 | */ 55 | public int getVendorId(String vendorName); 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/dictionary/DictionaryParser.java: -------------------------------------------------------------------------------- 1 | /** 2 | * $Id: DictionaryParser.java,v 1.2 2005/09/06 16:38:40 wuttke Exp $ 3 | * Created on 28.08.2005 4 | * 5 | * @author mw 6 | * @version $Revision: 1.2 $ 7 | */ 8 | package org.tinyradius.dictionary; 9 | 10 | import java.io.BufferedReader; 11 | import java.io.File; 12 | import java.io.FileInputStream; 13 | import java.io.IOException; 14 | import java.io.InputStream; 15 | import java.io.InputStreamReader; 16 | import java.util.StringTokenizer; 17 | import org.tinyradius.attribute.IntegerAttribute; 18 | import org.tinyradius.attribute.IpAttribute; 19 | import org.tinyradius.attribute.Ipv6Attribute; 20 | import org.tinyradius.attribute.Ipv6PrefixAttribute; 21 | import org.tinyradius.attribute.RadiusAttribute; 22 | import org.tinyradius.attribute.StringAttribute; 23 | import org.tinyradius.attribute.VendorSpecificAttribute; 24 | 25 | /** 26 | * Parses a dictionary in "Radiator format" and fills a 27 | * WritableDictionary. 28 | */ 29 | public class DictionaryParser { 30 | 31 | /** 32 | * Returns a new dictionary filled with the contents 33 | * from the given input stream. 34 | * 35 | * @param source 36 | * input stream 37 | * @return dictionary object 38 | * @throws IOException 39 | */ 40 | public static Dictionary parseDictionary(InputStream source) throws IOException { 41 | WritableDictionary d = new MemoryDictionary(); 42 | parseDictionary(source, d); 43 | return d; 44 | } 45 | 46 | /** 47 | * Parses the dictionary from the specified InputStream. 48 | * 49 | * @param source 50 | * input stream 51 | * @param dictionary 52 | * dictionary data is written to 53 | * @throws IOException 54 | * syntax errors 55 | * @throws RuntimeException 56 | * syntax errors 57 | */ 58 | public static void parseDictionary(InputStream source, WritableDictionary dictionary) throws IOException { 59 | // read each line separately 60 | BufferedReader in = new BufferedReader(new InputStreamReader(source)); 61 | 62 | String line; 63 | int lineNum = -1; 64 | while ((line = in.readLine()) != null) { 65 | // ignore comments 66 | lineNum++; 67 | line = line.trim(); 68 | if (line.startsWith("#") || line.length() == 0) 69 | continue; 70 | 71 | // tokenize line by whitespace 72 | StringTokenizer tok = new StringTokenizer(line); 73 | if (!tok.hasMoreTokens()) 74 | continue; 75 | 76 | String lineType = tok.nextToken().trim(); 77 | if (lineType.equalsIgnoreCase("ATTRIBUTE")) 78 | parseAttributeLine(dictionary, tok, lineNum); 79 | else if (lineType.equalsIgnoreCase("VALUE")) 80 | parseValueLine(dictionary, tok, lineNum); 81 | else if (lineType.equalsIgnoreCase("$INCLUDE")) 82 | includeDictionaryFile(dictionary, tok, lineNum); 83 | else if (lineType.equalsIgnoreCase("VENDORATTR")) 84 | parseVendorAttributeLine(dictionary, tok, lineNum); 85 | else if (lineType.equals("VENDOR")) 86 | parseVendorLine(dictionary, tok, lineNum); 87 | else 88 | throw new IOException("unknown line type: " + lineType + " line: " + lineNum); 89 | } 90 | } 91 | 92 | /** 93 | * Parse a line that declares an attribute. 94 | */ 95 | private static void parseAttributeLine(WritableDictionary dictionary, StringTokenizer tok, int lineNum) throws IOException { 96 | if (tok.countTokens() != 3) 97 | throw new IOException("syntax error on line " + lineNum); 98 | 99 | // read name, code, type 100 | String name = tok.nextToken().trim(); 101 | int code = Integer.parseInt(tok.nextToken()); 102 | String typeStr = tok.nextToken().trim(); 103 | 104 | // translate type to class 105 | Class type; 106 | if (code == VendorSpecificAttribute.VENDOR_SPECIFIC) 107 | type = VendorSpecificAttribute.class; 108 | else 109 | type = getAttributeTypeClass(code, typeStr); 110 | 111 | // create and cache object 112 | dictionary.addAttributeType(new AttributeType(code, name, type)); 113 | } 114 | 115 | /** 116 | * Parses a VALUE line containing an enumeration value. 117 | */ 118 | private static void parseValueLine(WritableDictionary dictionary, StringTokenizer tok, int lineNum) throws IOException { 119 | if (tok.countTokens() != 3) 120 | throw new IOException("syntax error on line " + lineNum); 121 | 122 | String typeName = tok.nextToken().trim(); 123 | String enumName = tok.nextToken().trim(); 124 | String valStr = tok.nextToken().trim(); 125 | 126 | AttributeType at = dictionary.getAttributeTypeByName(typeName); 127 | if (at == null) { 128 | throw new IOException("unknown attribute type: " + typeName + ", line: " + lineNum); 129 | } 130 | 131 | at.addEnumerationValue(Integer.parseInt(valStr), enumName); 132 | } 133 | 134 | /** 135 | * Parses a line that declares a Vendor-Specific attribute. 136 | */ 137 | private static void parseVendorAttributeLine(WritableDictionary dictionary, StringTokenizer tok, int lineNum) throws IOException { 138 | if (tok.countTokens() != 4) 139 | throw new IOException("syntax error on line " + lineNum); 140 | 141 | String vendor = tok.nextToken().trim(); 142 | String name = tok.nextToken().trim(); 143 | int code = Integer.parseInt(tok.nextToken().trim()); 144 | String typeStr = tok.nextToken().trim(); 145 | 146 | Class type = getAttributeTypeClass(code, typeStr); 147 | AttributeType at = new AttributeType(Integer.parseInt(vendor), code, name, type); 148 | dictionary.addAttributeType(at); 149 | } 150 | 151 | /** 152 | * Parses a line containing a vendor declaration. 153 | */ 154 | private static void parseVendorLine(WritableDictionary dictionary, StringTokenizer tok, int lineNum) throws IOException { 155 | if (tok.countTokens() != 2) 156 | throw new IOException("syntax error on line " + lineNum); 157 | 158 | int vendorId = Integer.parseInt(tok.nextToken().trim()); 159 | String vendorName = tok.nextToken().trim(); 160 | 161 | dictionary.addVendor(vendorId, vendorName); 162 | } 163 | 164 | /** 165 | * Includes a dictionary file. 166 | */ 167 | private static void includeDictionaryFile(WritableDictionary dictionary, StringTokenizer tok, int lineNum) throws IOException { 168 | if (tok.countTokens() != 1) 169 | throw new IOException("syntax error on line " + lineNum); 170 | String includeFile = tok.nextToken(); 171 | 172 | File incf = new File(includeFile); 173 | if (!incf.exists()) 174 | throw new IOException("inclueded file '" + includeFile + "' not found, line " + lineNum); 175 | 176 | FileInputStream fis = new FileInputStream(incf); 177 | parseDictionary(fis, dictionary); 178 | 179 | // line numbers begin with 0 again, but file name is 180 | // not mentioned in exceptions 181 | // furthermore, this method does not allow to include 182 | // classpath resources 183 | } 184 | 185 | /** 186 | * Returns the RadiusAttribute descendant class for the given 187 | * attribute type. 188 | * 189 | * @param attributeType 190 | * 191 | * @param typeStr 192 | * string|octets|integer|date|ipaddr|ipv6addr|ipv6prefix 193 | * @return RadiusAttribute class or descendant 194 | */ 195 | private static Class getAttributeTypeClass(int attributeType, String typeStr) { 196 | Class type = RadiusAttribute.class; 197 | if (typeStr.equalsIgnoreCase("string")) 198 | type = StringAttribute.class; 199 | else if (typeStr.equalsIgnoreCase("octets")) 200 | type = RadiusAttribute.class; 201 | else if (typeStr.equalsIgnoreCase("integer") || typeStr.equalsIgnoreCase("date")) 202 | type = IntegerAttribute.class; 203 | else if (typeStr.equalsIgnoreCase("ipaddr")) 204 | type = IpAttribute.class; 205 | else if (typeStr.equalsIgnoreCase("ipv6addr")) 206 | type = Ipv6Attribute.class; 207 | else if (typeStr.equalsIgnoreCase("ipv6prefix")) 208 | type = Ipv6PrefixAttribute.class; 209 | return type; 210 | } 211 | 212 | } 213 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/dictionary/MemoryDictionary.java: -------------------------------------------------------------------------------- 1 | /** 2 | * $Id: MemoryDictionary.java,v 1.2 2006/09/24 10:06:38 wuttke Exp $ 3 | * Created on 28.08.2005 4 | * 5 | * @author mw 6 | * @version $Revision: 1.2 $ 7 | */ 8 | package org.tinyradius.dictionary; 9 | 10 | import java.util.HashMap; 11 | import java.util.Iterator; 12 | import java.util.Map; 13 | 14 | /** 15 | * A dictionary that keeps the values and names in hash maps 16 | * in the memory. The dictionary has to be filled using the 17 | * methods addAttributeType and addVendor. 18 | * 19 | * @see #addAttributeType(AttributeType) 20 | * @see #addVendor(int, String) 21 | * @see org.tinyradius.dictionary.Dictionary 22 | * @see org.tinyradius.dictionary.WritableDictionary 23 | */ 24 | public class MemoryDictionary implements WritableDictionary { 25 | 26 | /** 27 | * Returns the AttributeType for the vendor -1 from the 28 | * cache. 29 | * 30 | * @param typeCode 31 | * attribute type code 32 | * @return AttributeType or null 33 | * @see org.tinyradius.dictionary.Dictionary#getAttributeTypeByCode(int) 34 | */ 35 | public AttributeType getAttributeTypeByCode(int typeCode) { 36 | return getAttributeTypeByCode(-1, typeCode); 37 | } 38 | 39 | /** 40 | * Returns the specified AttributeType object. 41 | * 42 | * @param vendorCode 43 | * vendor ID or -1 for "no vendor" 44 | * @param typeCode 45 | * attribute type code 46 | * @return AttributeType or null 47 | * @see org.tinyradius.dictionary.Dictionary#getAttributeTypeByCode(int, int) 48 | */ 49 | public AttributeType getAttributeTypeByCode(int vendorCode, int typeCode) { 50 | Map vendorAttributes = (Map) attributesByCode.get(new Integer(vendorCode)); 51 | if (vendorAttributes == null) { 52 | return null; 53 | } 54 | return (AttributeType) vendorAttributes.get(new Integer(typeCode)); 55 | } 56 | 57 | /** 58 | * Retrieves the attribute type with the given name. 59 | * 60 | * @param typeName 61 | * name of the attribute type 62 | * @return AttributeType or null 63 | * @see org.tinyradius.dictionary.Dictionary#getAttributeTypeByName(java.lang.String) 64 | */ 65 | public AttributeType getAttributeTypeByName(String typeName) { 66 | return (AttributeType) attributesByName.get(typeName); 67 | } 68 | 69 | /** 70 | * Searches the vendor with the given name and returns its 71 | * code. This method is seldomly used. 72 | * 73 | * @param vendorName 74 | * vendor name 75 | * @return vendor code or -1 76 | * @see org.tinyradius.dictionary.Dictionary#getVendorId(java.lang.String) 77 | */ 78 | public int getVendorId(String vendorName) { 79 | for (Iterator i = vendorsByCode.entrySet().iterator(); i.hasNext();) { 80 | Map.Entry e = (Map.Entry) i.next(); 81 | if (e.getValue().equals(vendorName)) 82 | return ((Integer) e.getKey()).intValue(); 83 | } 84 | return -1; 85 | } 86 | 87 | /** 88 | * Retrieves the name of the vendor with the given code from 89 | * the cache. 90 | * 91 | * @param vendorId 92 | * vendor number 93 | * @return vendor name or null 94 | * @see org.tinyradius.dictionary.Dictionary#getVendorName(int) 95 | */ 96 | public String getVendorName(int vendorId) { 97 | return (String) vendorsByCode.get(new Integer(vendorId)); 98 | } 99 | 100 | /** 101 | * Adds the given vendor to the cache. 102 | * 103 | * @param vendorId 104 | * vendor ID 105 | * @param vendorName 106 | * name of the vendor 107 | * @exception IllegalArgumentException 108 | * empty vendor name, invalid vendor ID 109 | */ 110 | public void addVendor(int vendorId, String vendorName) { 111 | if (vendorId < 0) 112 | throw new IllegalArgumentException("vendor ID must be positive"); 113 | if (getVendorName(vendorId) != null) 114 | throw new IllegalArgumentException("duplicate vendor code"); 115 | if (vendorName == null || vendorName.length() == 0) 116 | throw new IllegalArgumentException("vendor name empty"); 117 | vendorsByCode.put(new Integer(vendorId), vendorName); 118 | } 119 | 120 | /** 121 | * Adds an AttributeType object to the cache. 122 | * 123 | * @param attributeType 124 | * AttributeType object 125 | * @exception IllegalArgumentException 126 | * duplicate attribute name/type code 127 | */ 128 | public void addAttributeType(AttributeType attributeType) { 129 | if (attributeType == null) 130 | throw new IllegalArgumentException("attribute type must not be null"); 131 | 132 | Integer vendorId = new Integer(attributeType.getVendorId()); 133 | Integer typeCode = new Integer(attributeType.getTypeCode()); 134 | String attributeName = attributeType.getName(); 135 | 136 | if (attributesByName.containsKey(attributeName)) 137 | throw new IllegalArgumentException("duplicate attribute name: " + attributeName); 138 | 139 | Map vendorAttributes = (Map) attributesByCode.get(vendorId); 140 | if (vendorAttributes == null) { 141 | vendorAttributes = new HashMap(); 142 | attributesByCode.put(vendorId, vendorAttributes); 143 | } 144 | if (vendorAttributes.containsKey(typeCode)) 145 | throw new IllegalArgumentException("duplicate type code: " + typeCode); 146 | 147 | attributesByName.put(attributeName, attributeType); 148 | vendorAttributes.put(typeCode, attributeType); 149 | } 150 | 151 | private Map vendorsByCode = new HashMap(); // 152 | private Map attributesByCode = new HashMap(); // > 153 | private Map attributesByName = new HashMap(); // 154 | 155 | } 156 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/dictionary/WritableDictionary.java: -------------------------------------------------------------------------------- 1 | /** 2 | * $Id: WritableDictionary.java,v 1.1 2005/09/04 22:11:00 wuttke Exp $ 3 | * Created on 28.08.2005 4 | * @author mw 5 | * @version $Revision: 1.1 $ 6 | */ 7 | package org.tinyradius.dictionary; 8 | 9 | /** 10 | * A dictionary that is not read-only. Provides methods 11 | * to add entries to the dictionary. 12 | */ 13 | public interface WritableDictionary 14 | extends Dictionary { 15 | 16 | /** 17 | * Adds the given vendor to the dictionary. 18 | * @param vendorId vendor ID 19 | * @param vendorName name of the vendor 20 | */ 21 | public void addVendor(int vendorId, String vendorName); 22 | 23 | /** 24 | * Adds an AttributeType object to the dictionary. 25 | * @param attributeType AttributeType object 26 | */ 27 | public void addAttributeType(AttributeType attributeType); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/dictionary/dictionary.3gpp: -------------------------------------------------------------------------------- 1 | # -*- text -*- 2 | # Copyright (C) 2015 The FreeRADIUS Server project and contributors 3 | # 4 | # 3GPP stuff. 5 | # 6 | # http://www.3gpp.org/ftp/Specs/archive/29_series/29.061/29061-3a0.zip 7 | # 8 | # $Id$ 9 | # 10 | VENDOR 10415 3GPP 11 | 12 | 13 | 14 | 15 | # 16 | # Most of the 'string' attributes are UTF-8 encoded text. 17 | # Maybe we want a UTF-8 'type' in the server... 18 | # 19 | VENDORATTR 10415 3GPP-IMSI 1 string 20 | VENDORATTR 10415 3GPP-Charging-ID 2 integer 21 | VENDORATTR 10415 3GPP-PDP-Type 3 integer 22 | VENDORATTR 10415 3GPP-Charging-Gateway-Address 4 ipaddr 23 | VENDORATTR 10415 3GPP-GPRS-Negotiated-QoS-profile 5 string 24 | VENDORATTR 10415 3GPP-SGSN-Address 6 ipaddr 25 | VENDORATTR 10415 3GPP-GGSN-Address 7 ipaddr 26 | VENDORATTR 10415 3GPP-IMSI-MCC-MNC 8 string 27 | VENDORATTR 10415 3GPP-GGSN-MCC-MNC 9 string 28 | VENDORATTR 10415 3GPP-NSAPI 10 string 29 | VENDORATTR 10415 3GPP-Session-Stop-Indicator 11 byte 30 | VENDORATTR 10415 3GPP-Selection-Mode 12 string 31 | VENDORATTR 10415 3GPP-Charging-Characteristics 13 string 32 | VENDORATTR 10415 3GPP-Charging-Gateway-IPv6-Address 14 ipv6addr 33 | VENDORATTR 10415 3GPP-SGSN-IPv6-Address 15 ipv6addr 34 | VENDORATTR 10415 3GPP-GGSN-IPv6-Address 16 ipv6addr 35 | 36 | # 37 | # This attribute is really an array of IPv6 addresses. 38 | # Why the heck couldn't they just send multiple attributes? 39 | # 40 | VENDORATTR 10415 3GPP-IPv6-DNS-Servers 17 octets 41 | 42 | VENDORATTR 10415 3GPP-SGSN-MCC-MNC 18 string 43 | VENDORATTR 10415 3GPP-Teardown-Indicator 19 byte 44 | VENDORATTR 10415 3GPP-IMEISV 20 string 45 | VENDORATTR 10415 3GPP-RAT-Type 21 byte 46 | VENDORATTR 10415 3GPP-User-Location-Info 22 octets 47 | VENDORATTR 10415 3GPP-MS-Time-Zone 23 octets[2] 48 | VENDORATTR 10415 3GPP-Camel-Charging-Info 24 octets 49 | VENDORATTR 10415 3GPP-Packet-Filter 25 octets 50 | VENDORATTR 10415 3GPP-Negotiated-DSCP 26 byte 51 | VENDORATTR 10415 3GPP-Allocate-IP-Type 27 byte 52 | 53 | VALUE 3GPP-RAT-Type UTRAN 1 54 | VALUE 3GPP-RAT-Type GERAN 2 55 | VALUE 3GPP-RAT-Type WLAN 3 56 | VALUE 3GPP-RAT-Type GAN 4 57 | VALUE 3GPP-RAT-Type HSPA-Evolution 5 58 | VALUE 3GPP-RAT-Type EUTRAN 6 59 | VALUE 3GPP-RAT-Type Virtual 7 60 | VALUE 3GPP-RAT-Type EUTRAN-NB-IoT 8 61 | VALUE 3GPP-RAT-Type LTE-M 9 62 | VALUE 3GPP-RAT-Type 5G-New-Radio 10 63 | VALUE 3GPP-RAT-Type NG-RAN 51 64 | VALUE 3GPP-RAT-Type IEEE-802.16e 101 65 | VALUE 3GPP-RAT-Type 3GPP2-eHRPD 102 66 | VALUE 3GPP-RAT-Type 3GPP2-HRPD 103 67 | VALUE 3GPP-RAT-Type 3GPP2-1xRTT 104 68 | 69 | VALUE 3GPP-Allocate-IP-Type Do-Not-Allocate 0 70 | VALUE 3GPP-Allocate-IP-Type Allocate-IPv4-Address 1 71 | VALUE 3GPP-Allocate-IP-Type Allocate-IPv6-Prefix 2 72 | VALUE 3GPP-Allocate-IP-Type Allocate-IPv4-and-IPv6 3 73 | 74 | 75 | VALUE 3GPP-PDP-Type IPv4 0 76 | VALUE 3GPP-PDP-Type PPP 1 77 | VALUE 3GPP-PDP-Type IPv6 2 78 | VALUE 3GPP-PDP-Type IPv4v6 3 79 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/dictionary/dictionary.fortinet: -------------------------------------------------------------------------------- 1 | # -*- text -*- 2 | # Copyright (C) 2015 The FreeRADIUS Server project and contributors 3 | ############################################################################## 4 | # 5 | # Fortinet's VSA's 6 | # As posted to the list by Richie Lee. 7 | # 8 | # http://kb.fortinet.com/kb/viewContent.do?externalId=FD36919&sliceId=1 9 | # 10 | # $Id$ 11 | # 12 | ############################################################################## 13 | 14 | # 15 | # Fortinet's VSA's 16 | # 17 | 18 | VENDOR 12356 Fortinet 19 | 20 | VENDORATTR 12356 Fortinet-Group-Name 1 string 21 | VENDORATTR 12356 Fortinet-Client-IP-Address 2 ipaddr 22 | VENDORATTR 12356 Fortinet-Vdom-Name 3 string 23 | VENDORATTR 12356 Fortinet-Client-IPv6-Address 4 octets 24 | VENDORATTR 12356 Fortinet-Interface-Name 5 string 25 | VENDORATTR 12356 Fortinet-Access-Profile 6 string 26 | VENDORATTR 12356 Fortinet-FAC-Auth-Status 11 string 27 | VENDORATTR 12356 Fortinet-FAC-Token-ID 12 string 28 | VENDORATTR 12356 Fortinet-FAC-Challenge-Code 15 string 29 | VENDORATTR 12356 Fortinet-Webfilter-Category-Allow 16 octets 30 | VENDORATTR 12356 Fortinet-Webfilter-Category-Block 17 octets 31 | VENDORATTR 12356 Fortinet-Webfilter-Category-Monitor 18 octets 32 | VENDORATTR 12356 Fortinet-AppCtrl-Category-Allow 19 octets 33 | VENDORATTR 12356 Fortinet-AppCtrl-Category-Block 20 octets 34 | VENDORATTR 12356 Fortinet-AppCtrl-Risk-Allow 21 octets 35 | VENDORATTR 12356 Fortinet-AppCtrl-Risk-Block 22 octets 36 | VENDORATTR 12356 Fortinet-WirelessController-Device-MAC 23 ether 37 | VENDORATTR 12356 Fortinet-WirelessController-WTP-ID 24 string 38 | VENDORATTR 12356 Fortinet-WirelessController-Assoc-Time 25 date 39 | VENDORATTR 12356 Fortinet-FWN-AVPair 26 string 40 | 41 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/dictionary/package.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

This package contains interfaces and classes that manage 6 | a dictionary of Radius attribute types.

7 | 8 |

Each attribute type is represented by an instance of the 9 | object AttributeType. This class stores the 10 | type code, the type name and the vendor ID for each 11 | attribute type.

12 | 13 |

The interface Dictionary declares the methods necessary 14 | to access a dictionary. Every class that implements this 15 | interface can be used to resolve AttributeType objects.

16 | 17 |

Dictionaries that can be changed after construction should 18 | implement the interface WritableDictionary. 19 | You can use the class DictionaryParser 20 | to populate WritableDictionary objects with 21 | data from a dictionary file in the well-known Radiator 22 | dictionary file format.

23 | 24 |

MemoryDictionary is the default implementation 25 | of WritableDictionary. It manages a dictionary held 26 | in the RAM. This class is used together with the 27 | DictionaryParser by the class DefaultDictionary 28 | which is a singleton object containing the default dictionary 29 | that is used if there is no dictionary explicitly specified. 30 | The DefaultDictionary class is initialised from the 31 | classpath resource 32 | org.tinyradius.dictionary.default_dictionary. 33 |

34 | 35 | 36 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/packet/AccessRequest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * $Id: AccessRequest.java,v 1.5 2010/03/03 09:27:07 wuttke Exp $ 3 | * Created on 08.04.2005 4 | * 5 | * @author Matthias Wuttke 6 | * @version $Revision: 1.5 $ 7 | */ 8 | package org.tinyradius.packet; 9 | 10 | import java.security.MessageDigest; 11 | import java.security.SecureRandom; 12 | import java.util.Arrays; 13 | import java.util.HashSet; 14 | import java.util.List; 15 | import java.util.Set; 16 | 17 | import org.apache.commons.logging.Log; 18 | import org.apache.commons.logging.LogFactory; 19 | import org.tinyradius.attribute.RadiusAttribute; 20 | import org.tinyradius.attribute.StringAttribute; 21 | import org.tinyradius.util.RadiusException; 22 | import org.tinyradius.util.RadiusUtil; 23 | 24 | /** 25 | * This class represents an Access-Request Radius packet. 26 | */ 27 | public class AccessRequest extends RadiusPacket { 28 | 29 | /** 30 | * Passphrase Authentication Protocol 31 | */ 32 | public static final String AUTH_PAP = "pap"; 33 | 34 | /** 35 | * Challenged Handshake Authentication Protocol 36 | */ 37 | public static final String AUTH_CHAP = "chap"; 38 | 39 | /** 40 | * Microsoft Challenged Handshake Authentication Protocol V2 41 | */ 42 | public static final String AUTH_MS_CHAP_V2 = "mschapv2"; 43 | /** 44 | * Extended Authentication Protocol 45 | */ 46 | public static final String AUTH_EAP = "eap"; 47 | 48 | public static final Set AUTH_PROTOCOLS = new HashSet(Arrays.asList(AUTH_PAP, AUTH_CHAP, AUTH_MS_CHAP_V2, AUTH_EAP)); 49 | 50 | /** 51 | * Constructs an empty Access-Request packet. 52 | */ 53 | public AccessRequest() { 54 | super(ACCESS_REQUEST); 55 | } 56 | 57 | /** 58 | * Constructs an Access-Request packet, sets the 59 | * code, identifier and adds an User-Name and an 60 | * User-Password attribute (PAP). 61 | * 62 | * @param userName 63 | * user name 64 | * @param userPassword 65 | * user password 66 | */ 67 | public AccessRequest(String userName, String userPassword) { 68 | super(ACCESS_REQUEST, getNextPacketIdentifier()); 69 | setUserName(userName); 70 | setUserPassword(userPassword); 71 | } 72 | 73 | /** 74 | * Sets the User-Name attribute of this Access-Request. 75 | * 76 | * @param userName 77 | * user name to set 78 | */ 79 | public void setUserName(String userName) { 80 | if (userName == null) 81 | throw new NullPointerException("user name not set"); 82 | if (userName.length() == 0) 83 | throw new IllegalArgumentException("empty user name not allowed"); 84 | 85 | removeAttributes(USER_NAME); 86 | addAttribute(new StringAttribute(USER_NAME, userName)); 87 | } 88 | 89 | /** 90 | * Sets the plain-text user password. 91 | * 92 | * @param userPassword 93 | * user password to set 94 | */ 95 | public void setUserPassword(String userPassword) { 96 | if (userPassword == null || userPassword.length() == 0) 97 | throw new IllegalArgumentException("password is empty"); 98 | this.password = userPassword; 99 | } 100 | 101 | /** 102 | * Retrieves the plain-text user password. 103 | * Returns null for CHAP - use verifyPassword(). 104 | * 105 | * @see #verifyPassword(String) 106 | * @return user password 107 | */ 108 | public String getUserPassword() { 109 | return password; 110 | } 111 | 112 | /** 113 | * Retrieves the user name from the User-Name attribute. 114 | * 115 | * @return user name 116 | */ 117 | public String getUserName() { 118 | List attrs = getAttributes(USER_NAME); 119 | if (attrs.size() < 1 || attrs.size() > 1) 120 | throw new RuntimeException("exactly one User-Name attribute required"); 121 | 122 | RadiusAttribute ra = (RadiusAttribute) attrs.get(0); 123 | return ((StringAttribute) ra).getAttributeValue(); 124 | } 125 | 126 | /** 127 | * Returns the protocol used for encrypting the passphrase. 128 | * 129 | * @return AUTH_PAP or AUTH_CHAP 130 | */ 131 | public String getAuthProtocol() { 132 | return authProtocol; 133 | } 134 | 135 | /** 136 | * Selects the protocol to use for encrypting the passphrase when 137 | * encoding this Radius packet. 138 | * 139 | * @param authProtocol 140 | * AUTH_PAP or AUTH_CHAP 141 | */ 142 | public void setAuthProtocol(String authProtocol) { 143 | if (authProtocol != null && AUTH_PROTOCOLS.contains(authProtocol)) 144 | this.authProtocol = authProtocol; 145 | else 146 | throw new IllegalArgumentException("protocol must be in " + AUTH_PROTOCOLS); 147 | } 148 | 149 | /** 150 | * Verifies that the passed plain-text password matches the password 151 | * (hash) send with this Access-Request packet. Works with both PAP 152 | * and CHAP. 153 | * 154 | * @param plaintext 155 | * @return true if the password is valid, false otherwise 156 | */ 157 | public boolean verifyPassword(String plaintext) throws RadiusException { 158 | if (plaintext == null || plaintext.length() == 0) 159 | throw new IllegalArgumentException("password is empty"); 160 | if (getAuthProtocol().equals(AUTH_CHAP)) 161 | return verifyChapPassword(plaintext); 162 | if (getAuthProtocol().equals(AUTH_MS_CHAP_V2)) 163 | throw new RadiusException(AUTH_MS_CHAP_V2 + " verification not supported yet"); 164 | if (getAuthProtocol().equals(AUTH_EAP)) 165 | throw new RadiusException(AUTH_EAP + " verification not supported yet"); 166 | return getUserPassword().equals(plaintext); 167 | } 168 | 169 | /** 170 | * Decrypts the User-Password attribute. 171 | * 172 | * @see org.tinyradius.packet.RadiusPacket#decodeRequestAttributes(java.lang.String) 173 | */ 174 | protected void decodeRequestAttributes(String sharedSecret) throws RadiusException { 175 | // detect auth protocol 176 | RadiusAttribute userPassword = getAttribute(USER_PASSWORD); 177 | RadiusAttribute chapPassword = getAttribute(CHAP_PASSWORD); 178 | RadiusAttribute chapChallenge = getAttribute(CHAP_CHALLENGE); 179 | RadiusAttribute msChapChallenge = getAttribute(MICROSOFT, MS_CHAP_CHALLENGE); 180 | RadiusAttribute msChap2Response = getAttribute(MICROSOFT, MS_CHAP2_RESPONSE); 181 | List eapMessage = getAttributes(EAP_MESSAGE); 182 | if (userPassword != null) { 183 | setAuthProtocol(AUTH_PAP); 184 | this.password = decodePapPassword(userPassword.getAttributeData(), RadiusUtil.getUtf8Bytes(sharedSecret)); 185 | // copy truncated data 186 | userPassword.setAttributeData(RadiusUtil.getUtf8Bytes(this.password)); 187 | } 188 | else if (chapPassword != null && chapChallenge != null) { 189 | setAuthProtocol(AUTH_CHAP); 190 | this.chapPassword = chapPassword.getAttributeData(); 191 | this.chapChallenge = chapChallenge.getAttributeData(); 192 | } 193 | else if (chapPassword != null && getAuthenticator().length == 16) { 194 | // thanks to Guillaume Tartayre 195 | setAuthProtocol(AUTH_CHAP); 196 | this.chapPassword = chapPassword.getAttributeData(); 197 | this.chapChallenge = getAuthenticator(); 198 | } 199 | else if (msChapChallenge != null && msChap2Response != null) { 200 | setAuthProtocol(AUTH_MS_CHAP_V2); 201 | this.chapPassword = msChap2Response.getAttributeData(); 202 | this.chapChallenge = msChapChallenge.getAttributeData(); 203 | } 204 | else if (eapMessage.size() > 0) { 205 | setAuthProtocol(AUTH_EAP); 206 | } 207 | else 208 | throw new RadiusException("Access-Request: User-Password or CHAP-Password/CHAP-Challenge missing"); 209 | } 210 | 211 | /** 212 | * Sets and encrypts the User-Password attribute. 213 | * 214 | * @see org.tinyradius.packet.RadiusPacket#encodeRequestAttributes(java.lang.String) 215 | */ 216 | protected void encodeRequestAttributes(String sharedSecret) { 217 | if (password == null || password.length() == 0) 218 | return; 219 | // ok for proxied packets whose CHAP password is already encrypted 220 | // throw new RuntimeException("no password set"); 221 | 222 | if (getAuthProtocol().equals(AUTH_PAP)) { 223 | byte[] pass = encodePapPassword(RadiusUtil.getUtf8Bytes(this.password), RadiusUtil.getUtf8Bytes(sharedSecret)); 224 | removeAttributes(USER_PASSWORD); 225 | addAttribute(new RadiusAttribute(USER_PASSWORD, pass)); 226 | } 227 | else if (getAuthProtocol().equals(AUTH_CHAP)) { 228 | byte[] challenge = createChapChallenge(); 229 | byte[] pass = encodeChapPassword(password, challenge); 230 | removeAttributes(CHAP_PASSWORD); 231 | removeAttributes(CHAP_CHALLENGE); 232 | addAttribute(new RadiusAttribute(CHAP_PASSWORD, pass)); 233 | addAttribute(new RadiusAttribute(CHAP_CHALLENGE, challenge)); 234 | } 235 | else if (getAuthProtocol().equals(AUTH_MS_CHAP_V2)) { 236 | throw new RuntimeException("encoding not supported for " + AUTH_MS_CHAP_V2); 237 | } 238 | else if (getAuthProtocol().equals(AUTH_EAP)) { 239 | throw new RuntimeException("encoding not supported for " + AUTH_EAP); 240 | } 241 | } 242 | 243 | /** 244 | * This method encodes the plaintext user password according to RFC 2865. 245 | * 246 | * @param userPass 247 | * the password to encrypt 248 | * @param sharedSecret 249 | * shared secret 250 | * @return the byte array containing the encrypted password 251 | */ 252 | private byte[] encodePapPassword(final byte[] userPass, byte[] sharedSecret) { 253 | // the password must be a multiple of 16 bytes and less than or equal 254 | // to 128 bytes. If it isn't a multiple of 16 bytes fill it out with zeroes 255 | // to make it a multiple of 16 bytes. If it is greater than 128 bytes 256 | // truncate it at 128. 257 | byte[] userPassBytes = null; 258 | if (userPass.length > 128) { 259 | userPassBytes = new byte[128]; 260 | System.arraycopy(userPass, 0, userPassBytes, 0, 128); 261 | } 262 | else { 263 | userPassBytes = userPass; 264 | } 265 | 266 | // declare the byte array to hold the final product 267 | byte[] encryptedPass = null; 268 | if (userPassBytes.length < 128) { 269 | if (userPassBytes.length % 16 == 0) { 270 | // tt is already a multiple of 16 bytes 271 | encryptedPass = new byte[userPassBytes.length]; 272 | } 273 | else { 274 | // make it a multiple of 16 bytes 275 | encryptedPass = new byte[((userPassBytes.length / 16) * 16) + 16]; 276 | } 277 | } 278 | else { 279 | // the encrypted password must be between 16 and 128 bytes 280 | encryptedPass = new byte[128]; 281 | } 282 | 283 | // copy the userPass into the encrypted pass and then fill it out with zeroes by default. 284 | System.arraycopy(userPassBytes, 0, encryptedPass, 0, userPassBytes.length); 285 | 286 | // digest shared secret and authenticator 287 | MessageDigest md5 = getMd5Digest(); 288 | 289 | // According to section-5.2 in RFC 2865, when the password is longer than 16 290 | // characters: c(i) = pi xor (MD5(S + c(i-1))) 291 | for (int i = 0; i < encryptedPass.length; i += 16) { 292 | md5.reset(); 293 | md5.update(sharedSecret); 294 | if (i == 0) { 295 | md5.update(getAuthenticator()); 296 | } else { 297 | md5.update(encryptedPass, i - 16, 16); 298 | } 299 | 300 | byte bn[] = md5.digest(); 301 | 302 | // perform the XOR as specified by RFC 2865. 303 | for (int j = 0; j < 16; j++) 304 | encryptedPass[i + j] = (byte) (bn[j] ^ encryptedPass[i + j]); 305 | } 306 | return encryptedPass; 307 | } 308 | 309 | /** 310 | * Decodes the passed encrypted password and returns the clear-text form. 311 | * 312 | * @param encryptedPass 313 | * encrypted password 314 | * @param sharedSecret 315 | * shared secret 316 | * @return decrypted password 317 | */ 318 | private String decodePapPassword(byte[] encryptedPass, byte[] sharedSecret) throws RadiusException { 319 | if (encryptedPass == null || encryptedPass.length < 16) { 320 | // PAP passwords require at least 16 bytes 321 | logger.warn("invalid Radius packet: User-Password attribute with malformed PAP password, length = " 322 | + (encryptedPass == null ? 0 : encryptedPass.length) + ", but length must be greater than 15"); 323 | throw new RadiusException("malformed User-Password attribute"); 324 | } 325 | 326 | MessageDigest md5 = getMd5Digest(); 327 | byte[] lastBlock = new byte[16]; 328 | 329 | for (int i = 0; i < encryptedPass.length; i += 16) { 330 | md5.reset(); 331 | md5.update(sharedSecret); 332 | md5.update(i == 0 ? getAuthenticator() : lastBlock); 333 | byte bn[] = md5.digest(); 334 | 335 | System.arraycopy(encryptedPass, i, lastBlock, 0, 16); 336 | 337 | // perform the XOR as specified by RFC 2865. 338 | for (int j = 0; j < 16; j++) 339 | encryptedPass[i + j] = (byte) (bn[j] ^ encryptedPass[i + j]); 340 | } 341 | 342 | // remove trailing zeros 343 | int len = encryptedPass.length; 344 | while (len > 0 && encryptedPass[len - 1] == 0) 345 | len--; 346 | byte[] passtrunc = new byte[len]; 347 | System.arraycopy(encryptedPass, 0, passtrunc, 0, len); 348 | 349 | // convert to string 350 | return RadiusUtil.getStringFromUtf8(passtrunc); 351 | } 352 | 353 | /** 354 | * Creates a random CHAP challenge using a secure random algorithm. 355 | * 356 | * @return 16 byte CHAP challenge 357 | */ 358 | private byte[] createChapChallenge() { 359 | byte[] challenge = new byte[16]; 360 | random.nextBytes(challenge); 361 | return challenge; 362 | } 363 | 364 | /** 365 | * Encodes a plain-text password using the given CHAP challenge. 366 | * 367 | * @param plaintext 368 | * plain-text password 369 | * @param chapChallenge 370 | * CHAP challenge 371 | * @return CHAP-encoded password 372 | */ 373 | private byte[] encodeChapPassword(String plaintext, byte[] chapChallenge) { 374 | // see RFC 2865 section 2.2 375 | byte chapIdentifier = (byte) random.nextInt(256); 376 | byte[] chapPassword = new byte[17]; 377 | chapPassword[0] = chapIdentifier; 378 | 379 | MessageDigest md5 = getMd5Digest(); 380 | md5.reset(); 381 | md5.update(chapIdentifier); 382 | md5.update(RadiusUtil.getUtf8Bytes(plaintext)); 383 | byte[] chapHash = md5.digest(chapChallenge); 384 | 385 | System.arraycopy(chapHash, 0, chapPassword, 1, 16); 386 | return chapPassword; 387 | } 388 | 389 | /** 390 | * Verifies a CHAP password against the given plaintext password. 391 | * 392 | * @return plain-text password 393 | */ 394 | private boolean verifyChapPassword(String plaintext) throws RadiusException { 395 | if (plaintext == null || plaintext.length() == 0) 396 | throw new IllegalArgumentException("plaintext must not be empty"); 397 | if (chapChallenge == null || chapChallenge.length != 16) 398 | throw new RadiusException("CHAP challenge must be 16 bytes"); 399 | if (chapPassword == null || chapPassword.length != 17) 400 | throw new RadiusException("CHAP password must be 17 bytes"); 401 | 402 | byte chapIdentifier = chapPassword[0]; 403 | MessageDigest md5 = getMd5Digest(); 404 | md5.reset(); 405 | md5.update(chapIdentifier); 406 | md5.update(RadiusUtil.getUtf8Bytes(plaintext)); 407 | byte[] chapHash = md5.digest(chapChallenge); 408 | 409 | // compare 410 | for (int i = 0; i < 16; i++) 411 | if (chapHash[i] != chapPassword[i + 1]) 412 | return false; 413 | return true; 414 | } 415 | 416 | /** 417 | * Temporary storage for the unencrypted User-Password 418 | * attribute. 419 | */ 420 | private String password; 421 | 422 | /** 423 | * Authentication protocol for this access request. 424 | */ 425 | private String authProtocol = AUTH_PAP; 426 | 427 | /** 428 | * CHAP password from a decoded CHAP Access-Request. 429 | */ 430 | private byte[] chapPassword; 431 | 432 | /** 433 | * CHAP challenge from a decoded CHAP Access-Request. 434 | */ 435 | private byte[] chapChallenge; 436 | 437 | /** 438 | * Random generator 439 | */ 440 | private static SecureRandom random = new SecureRandom(); 441 | 442 | /** 443 | * Radius type code for Radius attribute User-Name 444 | */ 445 | private static final int USER_NAME = 1; 446 | 447 | /** 448 | * Radius attribute type for User-Password attribute. 449 | */ 450 | private static final int USER_PASSWORD = 2; 451 | 452 | /** 453 | * Radius attribute type for CHAP-Password attribute. 454 | */ 455 | private static final int CHAP_PASSWORD = 3; 456 | 457 | /** 458 | * Radius attribute type for CHAP-Challenge attribute. 459 | */ 460 | private static final int CHAP_CHALLENGE = 60; 461 | /** 462 | * Radius attribute type for EAP-Message attribute. 463 | */ 464 | private static final int EAP_MESSAGE = 79; 465 | /** 466 | * Vendor id for Microsoft 467 | */ 468 | private static final int MICROSOFT = 311; 469 | /** 470 | * Radius attribute type for MS-CHAP-Challenge attribute. 471 | */ 472 | private static final int MS_CHAP_CHALLENGE = 11; 473 | /** 474 | * Radius attribute type for MS-CHAP-Challenge attribute. 475 | */ 476 | private static final int MS_CHAP2_RESPONSE = 25; 477 | 478 | /** 479 | * Logger for logging information about malformed packets 480 | */ 481 | private static Log logger = LogFactory.getLog(AccessRequest.class); 482 | 483 | } 484 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/packet/AccountingRequest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * $Id: AccountingRequest.java,v 1.2 2006/02/17 18:14:54 wuttke Exp $ 3 | * Created on 09.04.2005 4 | * 5 | * @author Matthias Wuttke 6 | * @version $Revision: 1.2 $ 7 | */ 8 | package org.tinyradius.packet; 9 | 10 | import java.security.MessageDigest; 11 | import java.util.List; 12 | import org.tinyradius.attribute.IntegerAttribute; 13 | import org.tinyradius.attribute.RadiusAttribute; 14 | import org.tinyradius.attribute.StringAttribute; 15 | import org.tinyradius.util.RadiusException; 16 | import org.tinyradius.util.RadiusUtil; 17 | 18 | /** 19 | * This class represents a Radius packet of the type 20 | * "Accounting-Request". 21 | */ 22 | public class AccountingRequest extends RadiusPacket { 23 | 24 | /** 25 | * Acct-Status-Type: Start 26 | */ 27 | public static final int ACCT_STATUS_TYPE_START = 1; 28 | 29 | /** 30 | * Acct-Status-Type: Stop 31 | */ 32 | public static final int ACCT_STATUS_TYPE_STOP = 2; 33 | 34 | /** 35 | * Acct-Status-Type: Interim Update/Alive 36 | */ 37 | public static final int ACCT_STATUS_TYPE_INTERIM_UPDATE = 3; 38 | 39 | /** 40 | * Acct-Status-Type: Accounting-On 41 | */ 42 | public static final int ACCT_STATUS_TYPE_ACCOUNTING_ON = 7; 43 | 44 | /** 45 | * Acct-Status-Type: Accounting-Off 46 | */ 47 | public static final int ACCT_STATUS_TYPE_ACCOUNTING_OFF = 8; 48 | 49 | /** 50 | * Constructs an Accounting-Request packet to be sent to a Radius server. 51 | * 52 | * @param userName 53 | * user name 54 | * @param acctStatusType 55 | * ACCT_STATUS_TYPE_* 56 | */ 57 | public AccountingRequest(String userName, int acctStatusType) { 58 | super(ACCOUNTING_REQUEST, getNextPacketIdentifier()); 59 | setUserName(userName); 60 | setAcctStatusType(acctStatusType); 61 | } 62 | 63 | /** 64 | * Constructs an empty Accounting-Request to be received by a 65 | * Radius client. 66 | */ 67 | public AccountingRequest() { 68 | super(ACCOUNTING_REQUEST); 69 | } 70 | 71 | /** 72 | * Sets the User-Name attribute of this Accountnig-Request. 73 | * 74 | * @param userName 75 | * user name to set 76 | */ 77 | public void setUserName(String userName) { 78 | if (userName == null) 79 | throw new NullPointerException("user name not set"); 80 | if (userName.length() == 0) 81 | throw new IllegalArgumentException("empty user name not allowed"); 82 | 83 | removeAttributes(USER_NAME); 84 | addAttribute(new StringAttribute(USER_NAME, userName)); 85 | } 86 | 87 | /** 88 | * Retrieves the user name from the User-Name attribute. 89 | * 90 | * @return user name 91 | * @throws RadiusException 92 | */ 93 | public String getUserName() throws RadiusException { 94 | List attrs = getAttributes(USER_NAME); 95 | if (attrs.size() < 1 || attrs.size() > 1) 96 | throw new RuntimeException("exactly one User-Name attribute required"); 97 | 98 | RadiusAttribute ra = (RadiusAttribute) attrs.get(0); 99 | return ((StringAttribute) ra).getAttributeValue(); 100 | } 101 | 102 | /** 103 | * Sets the Acct-Status-Type attribute of this Accountnig-Request. 104 | * 105 | * @param acctStatusType 106 | * ACCT_STATUS_TYPE_* to set 107 | */ 108 | public void setAcctStatusType(int acctStatusType) { 109 | if (acctStatusType < 1 || acctStatusType > 15) 110 | throw new IllegalArgumentException("bad Acct-Status-Type"); 111 | removeAttributes(ACCT_STATUS_TYPE); 112 | addAttribute(new IntegerAttribute(ACCT_STATUS_TYPE, acctStatusType)); 113 | } 114 | 115 | /** 116 | * Retrieves the user name from the User-Name attribute. 117 | * 118 | * @return user name 119 | * @throws RadiusException 120 | */ 121 | public int getAcctStatusType() throws RadiusException { 122 | RadiusAttribute ra = getAttribute(ACCT_STATUS_TYPE); 123 | if (ra == null) { 124 | return -1; 125 | } 126 | return ((IntegerAttribute) ra).getAttributeValueInt(); 127 | } 128 | 129 | /** 130 | * Calculates the request authenticator as specified by RFC 2866. 131 | * 132 | * @see org.tinyradius.packet.RadiusPacket#updateRequestAuthenticator(java.lang.String, int, byte[]) 133 | */ 134 | protected byte[] updateRequestAuthenticator(String sharedSecret, int packetLength, byte[] attributes) { 135 | byte[] authenticator = new byte[16]; 136 | for (int i = 0; i < 16; i++) 137 | authenticator[i] = 0; 138 | 139 | MessageDigest md5 = getMd5Digest(); 140 | md5.reset(); 141 | md5.update((byte) getPacketType()); 142 | md5.update((byte) getPacketIdentifier()); 143 | md5.update((byte) (packetLength >> 8)); 144 | md5.update((byte) (packetLength & 0xff)); 145 | md5.update(authenticator, 0, authenticator.length); 146 | md5.update(attributes, 0, attributes.length); 147 | md5.update(RadiusUtil.getUtf8Bytes(sharedSecret)); 148 | return md5.digest(); 149 | } 150 | 151 | /** 152 | * Checks the received request authenticator as specified by RFC 2866. 153 | */ 154 | protected void checkRequestAuthenticator(String sharedSecret, int packetLength, byte[] attributes) throws RadiusException { 155 | byte[] expectedAuthenticator = updateRequestAuthenticator(sharedSecret, packetLength, attributes); 156 | byte[] receivedAuth = getAuthenticator(); 157 | for (int i = 0; i < 16; i++) 158 | if (expectedAuthenticator[i] != receivedAuth[i]) 159 | throw new RadiusException("request authenticator invalid"); 160 | } 161 | 162 | /** 163 | * Radius User-Name attribute type 164 | */ 165 | private static final int USER_NAME = 1; 166 | 167 | /** 168 | * Radius Acct-Status-Type attribute type 169 | */ 170 | private static final int ACCT_STATUS_TYPE = 40; 171 | 172 | } 173 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/packet/CoaRequest.java: -------------------------------------------------------------------------------- 1 | package org.tinyradius.packet; 2 | 3 | import java.security.MessageDigest; 4 | 5 | import org.tinyradius.util.RadiusUtil; 6 | 7 | /** 8 | * CoA packet. Thanks to Michael Krastev. 9 | * @author Michael Krastev 10 | */ 11 | public class CoaRequest extends RadiusPacket { 12 | 13 | public CoaRequest() { 14 | this(COA_REQUEST); 15 | } 16 | public CoaRequest(final int type) { 17 | super(type, getNextPacketIdentifier()); 18 | } 19 | 20 | /** 21 | * @see AccountingRequest#updateRequestAuthenticator(String, int, byte[]) 22 | */ 23 | protected byte[] updateRequestAuthenticator(String sharedSecret, 24 | int packetLength, byte[] attributes) { 25 | byte[] authenticator = new byte[16]; 26 | for (int i = 0; i < 16; i++) 27 | authenticator[i] = 0; 28 | MessageDigest md5 = getMd5Digest(); 29 | md5.reset(); 30 | md5.update((byte) getPacketType()); 31 | md5.update((byte) getPacketIdentifier()); 32 | md5.update((byte) (packetLength >> 8)); 33 | md5.update((byte) (packetLength & 0xff)); 34 | md5.update(authenticator, 0, authenticator.length); 35 | md5.update(attributes, 0, attributes.length); 36 | md5.update(RadiusUtil.getUtf8Bytes(sharedSecret)); 37 | return md5.digest(); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/packet/package.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

This package contains classes for encoding and decoding Radius packets.

6 | 7 |

The base class RadiusPacket can read and write every Radius packet, the 8 | subclasses AccessRequest and AccountingRequest provide convenience 9 | methods to ease the access of special packet attributes.

10 | 11 |

The class AccessRequest manages the encryption of 12 | passwords in the attributes User-Password (PAP) or CHAP-Password and 13 | CHAP-Challenge (CHAP).

14 | 15 |

Each Radius packet has an associated dictionary of attribute 16 | types that allows the convenient access to packet attributes 17 | and sub-attributes.

18 | 19 | 20 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/proxy/RadiusProxy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * $Id: RadiusProxy.java,v 1.1 2005/09/07 22:19:01 wuttke Exp $ 3 | * Created on 07.09.2005 4 | * 5 | * @author glanz, Matthias Wuttke 6 | * @version $Revision: 1.1 $ 7 | */ 8 | package org.tinyradius.proxy; 9 | 10 | import java.io.ByteArrayOutputStream; 11 | import java.io.IOException; 12 | import java.net.DatagramPacket; 13 | import java.net.DatagramSocket; 14 | import java.net.InetAddress; 15 | import java.net.InetSocketAddress; 16 | import java.net.SocketException; 17 | import java.util.HashMap; 18 | import java.util.List; 19 | import java.util.Map; 20 | import org.apache.commons.logging.Log; 21 | import org.apache.commons.logging.LogFactory; 22 | import org.tinyradius.attribute.RadiusAttribute; 23 | import org.tinyradius.packet.RadiusPacket; 24 | import org.tinyradius.util.RadiusEndpoint; 25 | import org.tinyradius.util.RadiusException; 26 | import org.tinyradius.util.RadiusServer; 27 | 28 | /** 29 | * This class implements a Radius proxy that receives Radius packets 30 | * and forwards them to a Radius server. 31 | * You have to override the method getRadiusProxyConnection() which 32 | * identifies the Radius proxy connection a Radius packet belongs to. 33 | */ 34 | public abstract class RadiusProxy extends RadiusServer { 35 | 36 | /** 37 | * Starts the Radius proxy. Listens on the proxy port. 38 | */ 39 | public void start(boolean listenAuth, boolean listenAcct, boolean listenProxy) { 40 | super.start(listenAuth, listenAcct); 41 | if (listenProxy) { 42 | new Thread() { 43 | public void run() { 44 | setName("Radius Proxy Listener"); 45 | try { 46 | logger.info("starting RadiusProxyListener on port " + getProxyPort()); 47 | listen(getProxySocket()); 48 | } 49 | catch (Exception e) { 50 | e.printStackTrace(); 51 | } 52 | } 53 | }.start(); 54 | } 55 | } 56 | 57 | /** 58 | * Stops the proxy and closes the socket. 59 | */ 60 | public void stop() { 61 | logger.info("stopping Radius proxy"); 62 | if (proxySocket != null) 63 | proxySocket.close(); 64 | super.stop(); 65 | } 66 | 67 | /** 68 | * This method must be implemented to return a RadiusEndpoint 69 | * if the given packet is to be proxied. The endpoint represents the 70 | * Radius server the packet should be proxied to. 71 | * 72 | * @param packet 73 | * the packet in question 74 | * @param client 75 | * the client endpoint the packet originated from 76 | * (containing the address, port number and shared secret) 77 | * @return a RadiusEndpoint or null if the packet should not be 78 | * proxied 79 | */ 80 | public abstract RadiusEndpoint getProxyServer(RadiusPacket packet, RadiusEndpoint client); 81 | 82 | /** 83 | * Returns the proxy port this server listens to. 84 | * Defaults to 1814. 85 | * 86 | * @return proxy port 87 | */ 88 | public int getProxyPort() { 89 | return proxyPort; 90 | } 91 | 92 | /** 93 | * Sets the proxy port this server listens to. 94 | * Please call before start(). 95 | * 96 | * @param proxyPort 97 | * proxy port 98 | */ 99 | public void setProxyPort(int proxyPort) { 100 | this.proxyPort = proxyPort; 101 | this.proxySocket = null; 102 | } 103 | 104 | /** 105 | * Sets the socket timeout. 106 | * 107 | * @param socketTimeout 108 | * @throws SocketException when socket timeout, >0 ms 109 | */ 110 | public void setSocketTimeout(int socketTimeout) throws SocketException { 111 | super.setSocketTimeout(socketTimeout); 112 | if (proxySocket != null) 113 | proxySocket.setSoTimeout(socketTimeout); 114 | } 115 | 116 | /** 117 | * Returns a socket bound to the proxy port. 118 | * 119 | * @return socket 120 | * @throws SocketException 121 | */ 122 | protected DatagramSocket getProxySocket() throws SocketException { 123 | if (proxySocket == null) { 124 | if (getListenAddress() == null) 125 | proxySocket = new DatagramSocket(getProxyPort()); 126 | else 127 | proxySocket = new DatagramSocket(getProxyPort(), getListenAddress()); 128 | proxySocket.setSoTimeout(getSocketTimeout()); 129 | } 130 | return proxySocket; 131 | } 132 | 133 | /** 134 | * Handles packets coming in on the proxy port. Decides whether 135 | * packets coming in on Auth/Acct ports should be proxied. 136 | */ 137 | protected RadiusPacket handlePacket(InetSocketAddress localAddress, InetSocketAddress remoteAddress, RadiusPacket request, String sharedSecret) 138 | throws RadiusException, IOException { 139 | // handle incoming proxy packet 140 | if (localAddress.getPort() == getProxyPort()) { 141 | proxyPacketReceived(request, remoteAddress); 142 | return null; 143 | } 144 | 145 | // handle auth/acct packet 146 | RadiusEndpoint radiusClient = new RadiusEndpoint(remoteAddress, sharedSecret); 147 | RadiusEndpoint radiusServer = getProxyServer(request, radiusClient); 148 | if (radiusServer != null) { 149 | // proxy incoming packet to other radius server 150 | RadiusProxyConnection proxyConnection = new RadiusProxyConnection(radiusServer, radiusClient, request, localAddress.getPort()); 151 | logger.info("proxy packet to " + proxyConnection); 152 | proxyPacket(request, proxyConnection); 153 | return null; 154 | } 155 | // normal processing 156 | return super.handlePacket(localAddress, remoteAddress, request, sharedSecret); 157 | } 158 | 159 | /** 160 | * Sends an answer to a proxied packet back to the original host. 161 | * Retrieves the RadiusProxyConnection object from the cache employing 162 | * the Proxy-State attribute. 163 | * 164 | * @param packet 165 | * packet to be sent back 166 | * @param remote 167 | * the server the packet arrived from 168 | * @throws IOException 169 | */ 170 | protected void proxyPacketReceived(RadiusPacket packet, InetSocketAddress remote) throws IOException, RadiusException { 171 | // retrieve my Proxy-State attribute (the last) 172 | List proxyStates = packet.getAttributes(33); 173 | if (proxyStates == null || proxyStates.size() == 0) 174 | throw new RadiusException("proxy packet without Proxy-State attribute"); 175 | RadiusAttribute proxyState = (RadiusAttribute) proxyStates.get(proxyStates.size() - 1); 176 | 177 | // retrieve proxy connection from cache 178 | String state = new String(proxyState.getAttributeData()); 179 | RadiusProxyConnection proxyConnection = (RadiusProxyConnection) proxyConnections.remove(state); 180 | if (proxyConnection == null) { 181 | logger.warn("received packet on proxy port without saved proxy connection - duplicate?"); 182 | return; 183 | } 184 | 185 | // retrieve client 186 | RadiusEndpoint client = proxyConnection.getRadiusClient(); 187 | if (logger.isInfoEnabled()) { 188 | logger.info("received proxy packet: " + packet); 189 | logger.info("forward packet to " + client.getEndpointAddress().toString() + " with secret " + client.getSharedSecret()); 190 | } 191 | 192 | // remove only own Proxy-State (last attribute) 193 | packet.removeLastAttribute(33); 194 | 195 | // re-encode answer packet with authenticator of the original packet 196 | RadiusPacket answer = new RadiusPacket(packet.getPacketType(), packet.getPacketIdentifier(), packet.getAttributes()); 197 | DatagramPacket datagram = makeDatagramPacket(answer, client.getSharedSecret(), client.getEndpointAddress().getAddress(), client 198 | .getEndpointAddress().getPort(), proxyConnection.getPacket()); 199 | 200 | // send back using correct socket 201 | DatagramSocket socket; 202 | if (proxyConnection.getPort() == getAuthPort()) 203 | socket = getAuthSocket(); 204 | else 205 | socket = getAcctSocket(); 206 | socket.send(datagram); 207 | } 208 | 209 | /** 210 | * Proxies the given packet to the server given in the proxy connection. 211 | * Stores the proxy connection object in the cache with a key that 212 | * is added to the packet in the "Proxy-State" attribute. 213 | * 214 | * @param packet 215 | * the packet to proxy 216 | * @param proxyConnection 217 | * the RadiusProxyConnection for this packet 218 | * @throws IOException 219 | */ 220 | protected void proxyPacket(RadiusPacket packet, RadiusProxyConnection proxyConnection) throws IOException { 221 | synchronized (RadiusProxy.class) { 222 | // add Proxy-State attribute 223 | proxyIndex++; 224 | String proxyIndexStr = Integer.toString(proxyIndex); 225 | packet.addAttribute(new RadiusAttribute(33, proxyIndexStr.getBytes())); 226 | 227 | // store RadiusProxyConnection object 228 | proxyConnections.put(proxyIndexStr, proxyConnection); 229 | } 230 | 231 | // get server address 232 | InetAddress serverAddress = proxyConnection.getRadiusServer().getEndpointAddress().getAddress(); 233 | int serverPort = proxyConnection.getRadiusServer().getEndpointAddress().getPort(); 234 | String serverSecret = proxyConnection.getRadiusServer().getSharedSecret(); 235 | 236 | // save request authenticator (will be calculated new) 237 | byte[] auth = packet.getAuthenticator(); 238 | 239 | // encode new packet (with new authenticator) 240 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 241 | packet.encodeRequestPacket(bos, serverSecret); 242 | byte[] data = bos.toByteArray(); 243 | DatagramPacket datagram = new DatagramPacket(data, data.length, serverAddress, serverPort); 244 | 245 | // restore original authenticator 246 | packet.setAuthenticator(auth); 247 | 248 | // send packet 249 | DatagramSocket proxySocket = getProxySocket(); 250 | proxySocket.send(datagram); 251 | } 252 | 253 | /** 254 | * Index for Proxy-State attribute. 255 | */ 256 | private int proxyIndex = 1; 257 | 258 | /** 259 | * Cache for Radius proxy connections belonging to sent packets 260 | * without a received response. 261 | * Key: Proxy Index (String), Value: RadiusProxyConnection 262 | */ 263 | private Map proxyConnections = new HashMap(); 264 | 265 | private int proxyPort = 1814; 266 | private DatagramSocket proxySocket = null; 267 | private static Log logger = LogFactory.getLog(RadiusProxy.class); 268 | 269 | } 270 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/proxy/RadiusProxyConnection.java: -------------------------------------------------------------------------------- 1 | /** 2 | * $Id: RadiusProxyConnection.java,v 1.2 2005/10/11 14:18:27 wuttke Exp $ 3 | * Created on 07.09.2005 4 | * @author glanz, Matthias Wuttke 5 | * @version $Revision: 1.2 $ 6 | */ 7 | package org.tinyradius.proxy; 8 | 9 | import org.tinyradius.packet.RadiusPacket; 10 | import org.tinyradius.util.RadiusEndpoint; 11 | 12 | /** 13 | * This class stores information about a proxied packet. 14 | * It contains two RadiusEndpoint objects representing the Radius client 15 | * and server, the port number the proxied packet arrived 16 | * at originally and the proxied packet itself. 17 | */ 18 | public class RadiusProxyConnection { 19 | 20 | /** 21 | * Creates a RadiusProxyConnection object. 22 | * @param radiusServer server endpoint 23 | * @param radiusClient client endpoint 24 | * @param port port the proxied packet arrived at originally 25 | */ 26 | public RadiusProxyConnection(RadiusEndpoint radiusServer, RadiusEndpoint radiusClient, RadiusPacket packet, int port) { 27 | this.radiusServer = radiusServer; 28 | this.radiusClient = radiusClient; 29 | this.packet = packet; 30 | this.port = port; 31 | } 32 | 33 | /** 34 | * Returns the Radius endpoint of the client. 35 | * @return endpoint 36 | */ 37 | public RadiusEndpoint getRadiusClient() { 38 | return radiusClient; 39 | } 40 | 41 | /** 42 | * Returns the Radius endpoint of the server. 43 | * @return endpoint 44 | */ 45 | public RadiusEndpoint getRadiusServer() { 46 | return radiusServer; 47 | } 48 | 49 | /** 50 | * Returns the proxied packet. 51 | * @return packet 52 | */ 53 | public RadiusPacket getPacket() { 54 | return packet; 55 | } 56 | 57 | /** 58 | * Returns the port number the proxied packet arrived at 59 | * originally. 60 | * @return port number 61 | */ 62 | public int getPort() { 63 | return port; 64 | } 65 | 66 | private RadiusEndpoint radiusServer; 67 | private RadiusEndpoint radiusClient; 68 | private int port; 69 | private RadiusPacket packet; 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/proxy/package.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

This package contains classes that can be used to implement 6 | a proxying Radius server using TinyRadius.

7 | 8 |

In addition to the abstract methods from RadiusServer, you 9 | have to override the method getProxyServer() from RadiusProxy 10 | in order to determine which packets shall be proxied and to 11 | which proxy server they will be sent.

12 | 13 | 14 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/test/TestClient.java: -------------------------------------------------------------------------------- 1 | /** 2 | * $Id: TestClient.java,v 1.4 2006/02/17 18:14:54 wuttke Exp $ 3 | * Created on 08.04.2005 4 | * @author Matthias Wuttke 5 | * @version $Revision: 1.4 $ 6 | */ 7 | package org.tinyradius.test; 8 | 9 | import org.tinyradius.packet.AccessRequest; 10 | import org.tinyradius.packet.AccountingRequest; 11 | import org.tinyradius.packet.RadiusPacket; 12 | import org.tinyradius.util.RadiusClient; 13 | 14 | /** 15 | * Simple Radius command-line client. 16 | */ 17 | public class TestClient { 18 | 19 | /** 20 | * Radius command line client. 21 | * 22 | * Usage: TestClient hostName sharedSecret userName password 23 | * @param args arguments 24 | * @throws Exception 25 | */ 26 | public static void main(String[] args) 27 | throws Exception { 28 | if (args.length != 4) { 29 | System.out.println("Usage: TestClient hostName sharedSecret userName password"); 30 | System.exit(1); 31 | } 32 | 33 | String host = args[0]; 34 | String shared = args[1]; 35 | String user = args[2]; 36 | String pass = args[3]; 37 | 38 | RadiusClient rc = new RadiusClient(host, shared); 39 | 40 | // 1. Send Access-Request 41 | AccessRequest ar = new AccessRequest(user, pass); 42 | ar.setAuthProtocol(AccessRequest.AUTH_PAP); // or AUTH_CHAP 43 | ar.addAttribute("NAS-Identifier", "this.is.my.nas-identifier.de"); 44 | ar.addAttribute("NAS-IP-Address", "192.168.0.100"); 45 | ar.addAttribute("Service-Type", "Login-User"); 46 | ar.addAttribute("WISPr-Redirection-URL", "http://www.sourceforge.net/"); 47 | ar.addAttribute("WISPr-Location-ID", "net.sourceforge.ap1"); 48 | 49 | System.out.println("Packet before it is sent\n" + ar + "\n"); 50 | RadiusPacket response = rc.authenticate(ar); 51 | System.out.println("Packet after it was sent\n" + ar + "\n"); 52 | System.out.println("Response\n" + response + "\n"); 53 | 54 | // 2. Send Accounting-Request 55 | AccountingRequest acc = new AccountingRequest("mw", AccountingRequest.ACCT_STATUS_TYPE_START); 56 | acc.addAttribute("Acct-Session-Id", "1234567890"); 57 | acc.addAttribute("NAS-Identifier", "this.is.my.nas-identifier.de"); 58 | acc.addAttribute("NAS-Port", "0"); 59 | 60 | System.out.println(acc + "\n"); 61 | response = rc.account(acc); 62 | System.out.println("Response: " + response); 63 | 64 | rc.close(); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/test/TestDictionary.java: -------------------------------------------------------------------------------- 1 | /** 2 | * $Id: TestDictionary.java,v 1.1 2005/09/06 16:38:41 wuttke Exp $ 3 | * Created on 06.09.2005 4 | * @author mw 5 | * @version $Revision: 1.1 $ 6 | */ 7 | package org.tinyradius.test; 8 | 9 | import java.io.FileInputStream; 10 | import java.io.InputStream; 11 | 12 | import org.tinyradius.attribute.IpAttribute; 13 | import org.tinyradius.attribute.Ipv6Attribute; 14 | import org.tinyradius.attribute.Ipv6PrefixAttribute; 15 | import org.tinyradius.dictionary.Dictionary; 16 | import org.tinyradius.dictionary.DefaultDictionary; 17 | import org.tinyradius.dictionary.DictionaryParser; 18 | import org.tinyradius.packet.AccessRequest; 19 | 20 | /** 21 | * Shows how to use TinyRadius with a custom dictionary 22 | * loaded from a dictionary file. 23 | * Requires a file "test.dictionary" in the current directory. 24 | */ 25 | public class TestDictionary { 26 | 27 | public static void main(String[] args) 28 | throws Exception { 29 | Dictionary dictionary = DefaultDictionary.getDefaultDictionary(); 30 | AccessRequest ar = new AccessRequest("UserName", "UserPassword"); 31 | ar.setDictionary(dictionary); 32 | ar.addAttribute("WISPr-Location-ID", "LocationID"); 33 | ar.addAttribute(new IpAttribute(8, 1234567)); 34 | ar.addAttribute(new Ipv6Attribute(168, "fe80::")); 35 | ar.addAttribute(new Ipv6PrefixAttribute(97, "fe80::/64")); 36 | ar.addAttribute(new Ipv6PrefixAttribute(97, "fe80::/128")); 37 | System.out.println(ar); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/test/TestProxy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * $Id: TestProxy.java,v 1.1 2005/09/07 22:19:01 wuttke Exp $ 3 | * Created on 07.09.2005 4 | * @author Matthias Wuttke 5 | * @version $Revision: 1.1 $ 6 | */ 7 | package org.tinyradius.test; 8 | 9 | import java.net.InetAddress; 10 | import java.net.InetSocketAddress; 11 | import java.net.UnknownHostException; 12 | 13 | import org.tinyradius.packet.AccountingRequest; 14 | import org.tinyradius.packet.RadiusPacket; 15 | import org.tinyradius.proxy.RadiusProxy; 16 | import org.tinyradius.util.RadiusEndpoint; 17 | 18 | /** 19 | * Test proxy server. 20 | * Listens on localhost:1812 and localhost:1813. Proxies every access request 21 | * to localhost:10000 and every accounting request to localhost:10001. 22 | * You can use TestClient to ask this TestProxy and TestServer 23 | * with the parameters 10000 and 10001 as the target server. 24 | * Uses "testing123" as the shared secret for the communication with the 25 | * target server (localhost:10000/localhost:10001) and "proxytest" as the 26 | * shared secret for the communication with connecting clients. 27 | */ 28 | public class TestProxy extends RadiusProxy { 29 | 30 | public RadiusEndpoint getProxyServer(RadiusPacket packet, 31 | RadiusEndpoint client) { 32 | // always proxy 33 | try { 34 | InetAddress address = InetAddress.getByAddress(new byte[]{127,0,0,1}); 35 | int port = 10000; 36 | if (packet instanceof AccountingRequest) 37 | port = 10001; 38 | return new RadiusEndpoint(new InetSocketAddress(address, port), "testing123"); 39 | } catch (UnknownHostException uhe) { 40 | uhe.printStackTrace(); 41 | return null; 42 | } 43 | } 44 | 45 | public String getSharedSecret(InetSocketAddress client) { 46 | if (client.getPort() == 10000 || client.getPort() == 10001) 47 | return "testing123"; 48 | else if (client.getAddress().getHostAddress().equals("127.0.0.1")) 49 | return "proxytest"; 50 | else 51 | return null; 52 | } 53 | 54 | public String getUserPassword(String userName) { 55 | // not used because every request is proxied 56 | return null; 57 | } 58 | 59 | public static void main(String[] args) { 60 | new TestProxy().start(true, true, true); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/test/TestServer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * $Id: TestServer.java,v 1.6 2006/02/17 18:14:54 wuttke Exp $ 3 | * Created on 08.04.2005 4 | * 5 | * @author Matthias Wuttke 6 | * @version $Revision: 1.6 $ 7 | */ 8 | package org.tinyradius.test; 9 | 10 | import java.io.IOException; 11 | import java.net.InetSocketAddress; 12 | import org.tinyradius.packet.AccessRequest; 13 | import org.tinyradius.packet.RadiusPacket; 14 | import org.tinyradius.util.RadiusException; 15 | import org.tinyradius.util.RadiusServer; 16 | 17 | /** 18 | * Test server which terminates after 30 s. 19 | * Knows only the client "localhost" with secret "testing123" and 20 | * the user "mw" with the password "test". 21 | */ 22 | public class TestServer { 23 | 24 | /** 25 | * @throws IOException 26 | */ 27 | public static void main(String[] args) throws IOException, Exception { 28 | RadiusServer server = new RadiusServer() { 29 | // Authorize localhost/testing123 30 | public String getSharedSecret(InetSocketAddress client) { 31 | if (client.getAddress().getHostAddress().equals("127.0.0.1")) { 32 | return "testing123"; 33 | } 34 | return null; 35 | } 36 | 37 | // Authenticate mw 38 | public String getUserPassword(String userName) { 39 | if (userName.equals("mw")) { 40 | return "test"; 41 | } 42 | return null; 43 | } 44 | 45 | // Adds an attribute to the Access-Accept packet 46 | public RadiusPacket accessRequestReceived(AccessRequest accessRequest, InetSocketAddress client) throws RadiusException { 47 | System.out.println("Received Access-Request:\n" + accessRequest); 48 | RadiusPacket packet = super.accessRequestReceived(accessRequest, client); 49 | if (packet == null) { 50 | System.out.println("Ignore packet."); 51 | } 52 | else if (packet.getPacketType() == RadiusPacket.ACCESS_ACCEPT) { 53 | packet.addAttribute("Reply-Message", "Welcome " + accessRequest.getUserName() + "!"); 54 | } 55 | else { 56 | System.out.println("Answer:\n" + packet); 57 | } 58 | return packet; 59 | } 60 | }; 61 | if (args.length >= 1) 62 | server.setAuthPort(Integer.parseInt(args[0])); 63 | if (args.length >= 2) 64 | server.setAcctPort(Integer.parseInt(args[1])); 65 | 66 | server.start(true, true); 67 | 68 | System.out.println("Server started."); 69 | 70 | Thread.sleep(1000 * 60 * 30); 71 | System.out.println("Stop server"); 72 | server.stop(); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/test/package.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

This package contains sample applications that 7 | show how to use TinyRadius.

8 | 9 |

    10 |
  • TestClient shows how to send Radius Access-Request and 11 | Accounting-Request packets.
  • 12 |
  • TestServer can answer both to Access-Request and Access-Response 13 | packets with Access-Accept/Reject or Accounting-Response, respectively.
  • 14 |
  • TestProxy shows how to implement a proxy radius server. You can use 15 | this class together with TestClient and TestServer.
  • 16 |
  • TestDictionary demonstrates the use of custom dictionaries.
  • 17 |

18 | 19 | 20 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/util/RadiusClient.java: -------------------------------------------------------------------------------- 1 | /** 2 | * $Id: RadiusClient.java,v 1.7 2005/11/10 10:20:21 wuttke Exp $ 3 | * Created on 09.04.2005 4 | * 5 | * @author Matthias Wuttke 6 | * @version $Revision: 1.7 $ 7 | */ 8 | package org.tinyradius.util; 9 | 10 | import java.io.ByteArrayInputStream; 11 | import java.io.ByteArrayOutputStream; 12 | import java.io.IOException; 13 | import java.net.DatagramPacket; 14 | import java.net.DatagramSocket; 15 | import java.net.InetAddress; 16 | import java.net.SocketException; 17 | import java.net.SocketTimeoutException; 18 | import org.apache.commons.logging.Log; 19 | import org.apache.commons.logging.LogFactory; 20 | import org.tinyradius.packet.AccessRequest; 21 | import org.tinyradius.packet.AccountingRequest; 22 | import org.tinyradius.packet.RadiusPacket; 23 | 24 | /** 25 | * This object represents a simple Radius client which communicates with 26 | * a specified Radius server. You can use a single instance of this object 27 | * to authenticate or account different users with the same Radius server 28 | * as long as you authenticate/account one user after the other. This object 29 | * is thread safe, but only opens a single socket so operations using this 30 | * socket are synchronized to avoid confusion with the mapping of request 31 | * and result packets. 32 | */ 33 | public class RadiusClient { 34 | 35 | /** 36 | * Creates a new Radius client object for a special Radius server. 37 | * 38 | * @param hostName 39 | * host name of the Radius server 40 | * @param sharedSecret 41 | * shared secret used to secure the communication 42 | */ 43 | public RadiusClient(String hostName, String sharedSecret) { 44 | setHostName(hostName); 45 | setSharedSecret(sharedSecret); 46 | } 47 | 48 | /** 49 | * Constructs a Radius client for the given Radius endpoint. 50 | * 51 | * @param client 52 | * Radius endpoint 53 | */ 54 | public RadiusClient(RadiusEndpoint client) { 55 | this(client.getEndpointAddress().getAddress().getHostAddress(), client.getSharedSecret()); 56 | } 57 | 58 | /** 59 | * Authenticates a user via PAP. 60 | * 61 | * @param userName 62 | * user name 63 | * @param password 64 | * password 65 | * @return true if authentication is successful, false otherwise 66 | * @exception RadiusException 67 | * malformed packet 68 | * @exception IOException 69 | * communication error (after getRetryCount() 70 | * retries) 71 | */ 72 | public synchronized boolean authenticate(String userName, String password) throws IOException, RadiusException { 73 | return authenticate(userName, password, authProtocol); 74 | } 75 | 76 | /** 77 | * Authenticates a user. 78 | * 79 | * @param userName 80 | * user name 81 | * @param password 82 | * password 83 | * @param protocol 84 | * either {@link AccessRequest#AUTH_PAP} or {@link AccessRequest#AUTH_CHAP} 85 | * @return true if authentication is successful, false otherwise 86 | * @exception RadiusException 87 | * malformed packet 88 | * @exception IOException 89 | * communication error (after getRetryCount() 90 | * retries) 91 | */ 92 | public synchronized boolean authenticate(String userName, String password, String protocol) throws IOException, RadiusException { 93 | AccessRequest request = new AccessRequest(userName, password); 94 | request.setAuthProtocol(protocol); 95 | RadiusPacket response = authenticate(request); 96 | return response.getPacketType() == RadiusPacket.ACCESS_ACCEPT; 97 | } 98 | 99 | /** 100 | * Sends an Access-Request packet and receives a response 101 | * packet. 102 | * 103 | * @param request 104 | * request packet 105 | * @return Radius response packet 106 | * @exception RadiusException 107 | * malformed packet 108 | * @exception IOException 109 | * communication error (after getRetryCount() 110 | * retries) 111 | */ 112 | public synchronized RadiusPacket authenticate(AccessRequest request) throws IOException, RadiusException { 113 | if (logger.isInfoEnabled()) 114 | logger.info("send Access-Request packet: " + request); 115 | 116 | RadiusPacket response = communicate(request, getAuthPort()); 117 | if (logger.isInfoEnabled()) 118 | logger.info("received packet: " + response); 119 | 120 | return response; 121 | } 122 | 123 | /** 124 | * Sends an Accounting-Request packet and receives a response 125 | * packet. 126 | * 127 | * @param request 128 | * request packet 129 | * @return Radius response packet 130 | * @exception RadiusException 131 | * malformed packet 132 | * @exception IOException 133 | * communication error (after getRetryCount() 134 | * retries) 135 | */ 136 | public synchronized RadiusPacket account(AccountingRequest request) throws IOException, RadiusException { 137 | if (logger.isInfoEnabled()) 138 | logger.info("send Accounting-Request packet: " + request); 139 | 140 | RadiusPacket response = communicate(request, getAcctPort()); 141 | if (logger.isInfoEnabled()) 142 | logger.info("received packet: " + response); 143 | 144 | return response; 145 | } 146 | 147 | /** 148 | * Closes the socket of this client. 149 | */ 150 | public void close() { 151 | if (serverSocket != null) 152 | serverSocket.close(); 153 | } 154 | 155 | /** 156 | * Returns the Radius server auth port. 157 | * 158 | * @return auth port 159 | */ 160 | public int getAuthPort() { 161 | return authPort; 162 | } 163 | 164 | /** 165 | * Sets the auth port of the Radius server. 166 | * 167 | * @param authPort 168 | * auth port, 1-65535 169 | */ 170 | public void setAuthPort(int authPort) { 171 | if (authPort < 1 || authPort > 65535) 172 | throw new IllegalArgumentException("bad port number"); 173 | this.authPort = authPort; 174 | } 175 | 176 | /** 177 | * Returns the host name of the Radius server. 178 | * 179 | * @return host name 180 | */ 181 | public String getHostName() { 182 | return hostName; 183 | } 184 | 185 | /** 186 | * Sets the host name of the Radius server. 187 | * 188 | * @param hostName 189 | * host name 190 | */ 191 | public void setHostName(String hostName) { 192 | if (hostName == null || hostName.length() == 0) 193 | throw new IllegalArgumentException("host name must not be empty"); 194 | this.hostName = hostName; 195 | } 196 | 197 | /** 198 | * Returns the retry count for failed transmissions. 199 | * 200 | * @return retry count 201 | */ 202 | public int getRetryCount() { 203 | return retryCount; 204 | } 205 | 206 | /** 207 | * Sets the retry count for failed transmissions. 208 | * 209 | * @param retryCount 210 | * @throws IllegalArgumentException when retry count is not positive 211 | */ 212 | public void setRetryCount(int retryCount) { 213 | if (retryCount < 1) 214 | throw new IllegalArgumentException("retry count must be positive"); 215 | this.retryCount = retryCount; 216 | } 217 | 218 | /** 219 | * Returns the secret shared between server and client. 220 | * 221 | * @return shared secret 222 | */ 223 | public String getSharedSecret() { 224 | return sharedSecret; 225 | } 226 | 227 | /** 228 | * Sets the secret shared between server and client. 229 | * 230 | * @param sharedSecret 231 | * shared secret 232 | */ 233 | public void setSharedSecret(String sharedSecret) { 234 | if (sharedSecret == null || sharedSecret.length() == 0) 235 | throw new IllegalArgumentException("shared secret must not be empty"); 236 | this.sharedSecret = sharedSecret; 237 | } 238 | 239 | /** 240 | * Returns the socket timeout. 241 | * 242 | * @return socket timeout, ms 243 | */ 244 | public int getSocketTimeout() { 245 | return socketTimeout; 246 | } 247 | 248 | /** 249 | * Sets the socket timeout 250 | * 251 | * @param socketTimeout 252 | * timeout, ms, >0 253 | * @throws SocketException 254 | */ 255 | public void setSocketTimeout(int socketTimeout) throws SocketException { 256 | if (socketTimeout < 1) 257 | throw new IllegalArgumentException("socket tiemout must be positive"); 258 | this.socketTimeout = socketTimeout; 259 | if (serverSocket != null) 260 | serverSocket.setSoTimeout(socketTimeout); 261 | } 262 | 263 | /** 264 | * Sets the Radius server accounting port. 265 | * 266 | * @param acctPort 267 | * acct port, 1-65535 268 | */ 269 | public void setAcctPort(int acctPort) { 270 | if (acctPort < 1 || acctPort > 65535) 271 | throw new IllegalArgumentException("bad port number"); 272 | this.acctPort = acctPort; 273 | } 274 | 275 | /** 276 | * Returns the Radius server accounting port. 277 | * 278 | * @return acct port 279 | */ 280 | public int getAcctPort() { 281 | return acctPort; 282 | } 283 | 284 | /** 285 | * Set the Radius authentication protocol. 286 | * 287 | * @see AccessRequest#AUTH_CHAP 288 | * @see AccessRequest#AUTH_PAP 289 | * 290 | * @param protocol 291 | * the protocol, PAP or CHAP 292 | */ 293 | public void setAuthProtocol(String protocol) { 294 | this.authProtocol = protocol; 295 | } 296 | 297 | /** 298 | * Sends a Radius packet to the server and awaits an answer. 299 | * 300 | * @param request 301 | * packet to be sent 302 | * @param port 303 | * server port number 304 | * @return response Radius packet 305 | * @exception RadiusException 306 | * malformed packet 307 | * @exception IOException 308 | * communication error (after getRetryCount() 309 | * retries) 310 | */ 311 | public RadiusPacket communicate(RadiusPacket request, int port) throws IOException, RadiusException { 312 | DatagramPacket packetIn = new DatagramPacket(new byte[RadiusPacket.MAX_PACKET_LENGTH], RadiusPacket.MAX_PACKET_LENGTH); 313 | DatagramPacket packetOut = makeDatagramPacket(request, port); 314 | 315 | DatagramSocket socket = getSocket(); 316 | try { 317 | for (int i = 1; i <= getRetryCount(); i++) { 318 | try { 319 | socket.send(packetOut); 320 | socket.receive(packetIn); 321 | return makeRadiusPacket(packetIn, request); 322 | } catch (IOException ioex) { 323 | if (i == getRetryCount()) { 324 | if (logger.isErrorEnabled()) { 325 | if (ioex instanceof SocketTimeoutException) 326 | logger.error("communication failure (timeout), no more retries"); 327 | else 328 | logger.error("communication failure, no more retries", ioex); 329 | } 330 | throw ioex; 331 | } 332 | if (logger.isInfoEnabled()) 333 | logger.info("communication failure, retry " + i); 334 | // TODO increase Acct-Delay-Time by getSocketTimeout()/1000 335 | // this changes the packet authenticator and requires packetOut to be 336 | // calculated again (call makeDatagramPacket) 337 | } 338 | } 339 | }finally { 340 | socket.close(); 341 | } 342 | 343 | return null; 344 | } 345 | 346 | /** 347 | * Sends the specified packet to the specified Radius server endpoint. 348 | * 349 | * @param remoteServer 350 | * Radius endpoint consisting of server address, 351 | * port number and shared secret 352 | * @param request 353 | * Radius packet to be sent 354 | * @return received response packet 355 | * @throws RadiusException 356 | * malformed packet 357 | * @throws IOException 358 | * error while communication 359 | */ 360 | public static RadiusPacket communicate(RadiusEndpoint remoteServer, RadiusPacket request) throws RadiusException, IOException { 361 | RadiusClient rc = new RadiusClient(remoteServer); 362 | return rc.communicate(request, remoteServer.getEndpointAddress().getPort()); 363 | } 364 | 365 | /** 366 | * Returns the socket used for the server communication. It is 367 | * bound to an arbitrary free local port number. 368 | * 369 | * @return local socket 370 | * @throws SocketException 371 | */ 372 | protected DatagramSocket getSocket() throws SocketException { 373 | if (serverSocket == null || serverSocket.isClosed()) { 374 | serverSocket = new DatagramSocket(); 375 | serverSocket.setSoTimeout(getSocketTimeout()); 376 | } 377 | return serverSocket; 378 | } 379 | 380 | /** 381 | * Creates a datagram packet from a RadiusPacket to be send. 382 | * 383 | * @param packet 384 | * RadiusPacket 385 | * @param port 386 | * destination port number 387 | * @return new datagram packet 388 | * @throws IOException 389 | */ 390 | protected DatagramPacket makeDatagramPacket(RadiusPacket packet, int port) throws IOException { 391 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 392 | packet.encodeRequestPacket(bos, getSharedSecret()); 393 | byte[] data = bos.toByteArray(); 394 | 395 | InetAddress address = InetAddress.getByName(getHostName()); 396 | DatagramPacket datagram = new DatagramPacket(data, data.length, address, port); 397 | return datagram; 398 | } 399 | 400 | /** 401 | * Creates a RadiusPacket from a received datagram packet. 402 | * 403 | * @param packet 404 | * received datagram 405 | * @param request 406 | * Radius request packet 407 | * @return RadiusPacket object 408 | */ 409 | protected RadiusPacket makeRadiusPacket(DatagramPacket packet, RadiusPacket request) throws IOException, RadiusException { 410 | ByteArrayInputStream in = new ByteArrayInputStream(packet.getData()); 411 | return RadiusPacket.decodeResponsePacket(in, getSharedSecret(), request); 412 | } 413 | 414 | private int authPort = 1812; 415 | private int acctPort = 1813; 416 | private String hostName = null; 417 | private String sharedSecret = null; 418 | private DatagramSocket serverSocket = null; 419 | private int retryCount = 3; 420 | private int socketTimeout = 3000; 421 | private String authProtocol = AccessRequest.AUTH_PAP; 422 | private static Log logger = LogFactory.getLog(RadiusClient.class); 423 | 424 | } 425 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/util/RadiusEndpoint.java: -------------------------------------------------------------------------------- 1 | /** 2 | * $Id: RadiusEndpoint.java,v 1.1 2005/09/07 22:19:01 wuttke Exp $ 3 | * Created on 07.09.2005 4 | * @author glanz, Matthias Wuttke 5 | * @version $Revision: 1.1 $ 6 | */ 7 | package org.tinyradius.util; 8 | 9 | import java.net.InetSocketAddress; 10 | 11 | /** 12 | * This class stores information about a Radius endpoint. 13 | * This includes the address of the remote endpoint and the shared secret 14 | * used for securing the communication. 15 | */ 16 | public class RadiusEndpoint { 17 | 18 | /** 19 | * Constructs a RadiusEndpoint object. 20 | * @param remoteAddress remote address (ip and port number) 21 | * @param sharedSecret shared secret 22 | */ 23 | public RadiusEndpoint(InetSocketAddress remoteAddress, String sharedSecret) { 24 | this.endpointAddress = remoteAddress; 25 | this.sharedSecret = sharedSecret; 26 | } 27 | 28 | /** 29 | * Returns the remote address. 30 | * @return remote address 31 | */ 32 | public InetSocketAddress getEndpointAddress() { 33 | return endpointAddress; 34 | } 35 | 36 | /** 37 | * Returns the shared secret. 38 | * @return shared secret 39 | */ 40 | public String getSharedSecret() { 41 | return sharedSecret; 42 | } 43 | 44 | private InetSocketAddress endpointAddress; 45 | private String sharedSecret; 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/util/RadiusException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * $Id: RadiusException.java,v 1.2 2005/10/15 11:35:30 wuttke Exp $ 3 | * Created on 10.04.2005 4 | * @author Matthias Wuttke 5 | * @version $Revision: 1.2 $ 6 | */ 7 | package org.tinyradius.util; 8 | 9 | /** 10 | * An exception which occurs on Radius protocol errors like 11 | * invalid packets or malformed attributes. 12 | */ 13 | public class RadiusException extends Exception { 14 | 15 | /** 16 | * Constructs a RadiusException with a message. 17 | * @param message error message 18 | */ 19 | public RadiusException(String message) { 20 | super(message); 21 | } 22 | 23 | private static final long serialVersionUID = 2201204523946051388L; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/util/RadiusServer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * $Id: RadiusServer.java,v 1.11 2008/04/24 05:22:50 wuttke Exp $ 3 | * Created on 09.04.2005 4 | * 5 | * @author Matthias Wuttke 6 | * @version $Revision: 1.11 $ 7 | */ 8 | package org.tinyradius.util; 9 | 10 | import java.io.ByteArrayInputStream; 11 | import java.io.ByteArrayOutputStream; 12 | import java.io.IOException; 13 | import java.net.DatagramPacket; 14 | import java.net.DatagramSocket; 15 | import java.net.InetAddress; 16 | import java.net.InetSocketAddress; 17 | import java.net.SocketException; 18 | import java.net.SocketTimeoutException; 19 | import java.util.Arrays; 20 | import java.util.Iterator; 21 | import java.util.List; 22 | import java.util.Map; 23 | import java.util.HashMap; 24 | import java.util.concurrent.ExecutorService; 25 | import org.apache.commons.logging.Log; 26 | import org.apache.commons.logging.LogFactory; 27 | import org.tinyradius.attribute.RadiusAttribute; 28 | import org.tinyradius.packet.AccessRequest; 29 | import org.tinyradius.packet.AccountingRequest; 30 | import org.tinyradius.packet.RadiusPacket; 31 | 32 | /** 33 | * Implements a simple Radius server. This class must be subclassed to 34 | * provide an implementation for getSharedSecret() and getUserPassword(). 35 | * If the server supports accounting, it must override 36 | * accountingRequestReceived(). 37 | */ 38 | public abstract class RadiusServer { 39 | 40 | /** 41 | * Define this executor in child class to make packet processing be made in separate threads 42 | */ 43 | protected ExecutorService executor = null; 44 | 45 | /** 46 | * Returns the shared secret used to communicate with the client with the 47 | * passed IP address or null if the client is not allowed at this server. 48 | * 49 | * @param client 50 | * IP address and port number of client 51 | * @return shared secret or null 52 | */ 53 | public abstract String getSharedSecret(InetSocketAddress client); 54 | 55 | /** 56 | * Returns the shared secret used to communicate with the client with the 57 | * passed IP address and the received packet data or null if the client 58 | * is not allowed at this server. 59 | * 60 | * for compatiblity this standard implementation just call the getSharedSecret(InetSocketAddress) method 61 | * and should be overrived when necessary 62 | * 63 | * @param client 64 | * IP address and port number of client 65 | * @param packet 66 | * packet received from client, the packettype comes as RESERVED, 67 | * because for some packets the secret is necessary for decoding 68 | * @return shared secret or null 69 | */ 70 | public String getSharedSecret(InetSocketAddress client, RadiusPacket packet) { 71 | return getSharedSecret(client); 72 | } 73 | 74 | /** 75 | * Returns the password of the passed user. Either this 76 | * method or accessRequestReceived() should be overriden. 77 | * 78 | * @param userName 79 | * user name 80 | * @return plain-text password or null if user unknown 81 | */ 82 | public abstract String getUserPassword(String userName); 83 | 84 | /** 85 | * Constructs an answer for an Access-Request packet. Either this 86 | * method or isUserAuthenticated should be overriden. 87 | * 88 | * @param accessRequest 89 | * Radius request packet 90 | * @param client 91 | * address of Radius client 92 | * @return response packet or null if no packet shall be sent 93 | * @exception RadiusException 94 | * malformed request packet; if this 95 | * exception is thrown, no answer will be sent 96 | */ 97 | public RadiusPacket accessRequestReceived(AccessRequest accessRequest, InetSocketAddress client) throws RadiusException { 98 | String plaintext = getUserPassword(accessRequest.getUserName()); 99 | int type = RadiusPacket.ACCESS_REJECT; 100 | if (plaintext != null && accessRequest.verifyPassword(plaintext)) 101 | type = RadiusPacket.ACCESS_ACCEPT; 102 | 103 | RadiusPacket answer = new RadiusPacket(type, accessRequest.getPacketIdentifier()); 104 | copyProxyState(accessRequest, answer); 105 | return answer; 106 | } 107 | 108 | /** 109 | * Constructs an answer for an Accounting-Request packet. This method 110 | * should be overriden if accounting is supported. 111 | * 112 | * @param accountingRequest 113 | * Radius request packet 114 | * @param client 115 | * address of Radius client 116 | * @return response packet or null if no packet shall be sent 117 | * @exception RadiusException 118 | * malformed request packet; if this 119 | * exception is thrown, no answer will be sent 120 | */ 121 | public RadiusPacket accountingRequestReceived(AccountingRequest accountingRequest, InetSocketAddress client) throws RadiusException { 122 | RadiusPacket answer = new RadiusPacket(RadiusPacket.ACCOUNTING_RESPONSE, accountingRequest.getPacketIdentifier()); 123 | copyProxyState(accountingRequest, answer); 124 | return answer; 125 | } 126 | 127 | /** 128 | * Starts the Radius server. 129 | * 130 | * @param listenAuth 131 | * open auth port? 132 | * @param listenAcct 133 | * open acct port? 134 | */ 135 | public void start(boolean listenAuth, boolean listenAcct) { 136 | if (listenAuth) { 137 | new Thread() { 138 | public void run() { 139 | setName("Radius Auth Listener"); 140 | try { 141 | logger.info("starting RadiusAuthListener on port " + getAuthPort()); 142 | listenAuth(); 143 | logger.info("RadiusAuthListener is being terminated"); 144 | } 145 | catch (Exception e) { 146 | e.printStackTrace(); 147 | logger.fatal("auth thread stopped by exception", e); 148 | } 149 | finally { 150 | authSocket.close(); 151 | logger.debug("auth socket closed"); 152 | } 153 | } 154 | }.start(); 155 | } 156 | 157 | if (listenAcct) { 158 | new Thread() { 159 | public void run() { 160 | setName("Radius Acct Listener"); 161 | try { 162 | logger.info("starting RadiusAcctListener on port " + getAcctPort()); 163 | listenAcct(); 164 | logger.info("RadiusAcctListener is being terminated"); 165 | } 166 | catch (Exception e) { 167 | e.printStackTrace(); 168 | logger.fatal("acct thread stopped by exception", e); 169 | } 170 | finally { 171 | acctSocket.close(); 172 | logger.debug("acct socket closed"); 173 | } 174 | } 175 | }.start(); 176 | } 177 | } 178 | 179 | /** 180 | * Stops the server and closes the sockets. 181 | */ 182 | public void stop() { 183 | logger.info("stopping Radius server"); 184 | closing = true; 185 | if (executor != null) 186 | executor.shutdown(); 187 | if (authSocket != null) 188 | authSocket.close(); 189 | if (acctSocket != null) 190 | acctSocket.close(); 191 | } 192 | 193 | /** 194 | * Returns the auth port the server will listen on. 195 | * 196 | * @return auth port 197 | */ 198 | public int getAuthPort() { 199 | return authPort; 200 | } 201 | 202 | /** 203 | * Sets the auth port the server will listen on. 204 | * 205 | * @param authPort 206 | * auth port, 1-65535 207 | */ 208 | public void setAuthPort(int authPort) { 209 | if (authPort < 1 || authPort > 65535) 210 | throw new IllegalArgumentException("bad port number"); 211 | this.authPort = authPort; 212 | this.authSocket = null; 213 | } 214 | 215 | /** 216 | * Returns the socket timeout (ms). 217 | * 218 | * @return socket timeout 219 | */ 220 | public int getSocketTimeout() { 221 | return socketTimeout; 222 | } 223 | 224 | /** 225 | * Sets the socket timeout. 226 | * 227 | * @param socketTimeout when socket timeout, >0 ms 228 | * @throws SocketException when socketTime is not positive 229 | */ 230 | public void setSocketTimeout(int socketTimeout) throws SocketException { 231 | if (socketTimeout < 1) 232 | throw new IllegalArgumentException("socket timeout must be positive"); 233 | this.socketTimeout = socketTimeout; 234 | if (authSocket != null) 235 | authSocket.setSoTimeout(socketTimeout); 236 | if (acctSocket != null) 237 | acctSocket.setSoTimeout(socketTimeout); 238 | } 239 | 240 | /** 241 | * Sets the acct port the server will listen on. 242 | * 243 | * @param acctPort 244 | * acct port 1-65535 245 | */ 246 | public void setAcctPort(int acctPort) { 247 | if (acctPort < 1 || acctPort > 65535) 248 | throw new IllegalArgumentException("bad port number"); 249 | this.acctPort = acctPort; 250 | this.acctSocket = null; 251 | } 252 | 253 | /** 254 | * Returns the acct port the server will listen on. 255 | * 256 | * @return acct port 257 | */ 258 | public int getAcctPort() { 259 | return acctPort; 260 | } 261 | 262 | /** 263 | * Returns the duplicate interval in ms. 264 | * A packet is discarded as a duplicate if in the duplicate interval 265 | * there was another packet with the same identifier originating from the 266 | * same address. 267 | * 268 | * @return duplicate interval (ms) 269 | */ 270 | public long getDuplicateInterval() { 271 | return duplicateInterval; 272 | } 273 | 274 | /** 275 | * Sets the duplicate interval in ms. 276 | * A packet is discarded as a duplicate if in the duplicate interval 277 | * there was another packet with the same identifier originating from the 278 | * same address. 279 | * 280 | * @param duplicateInterval 281 | * @throws IllegalArgumentException when duplicateInterval (ms), >0 282 | */ 283 | public void setDuplicateInterval(long duplicateInterval) { 284 | if (duplicateInterval <= 0) 285 | throw new IllegalArgumentException("duplicate interval must be positive"); 286 | this.duplicateInterval = duplicateInterval; 287 | } 288 | 289 | /** 290 | * Returns a map containing received packets 291 | * 292 | * @return list of received packets 293 | */ 294 | public Map getReceivedPackets() { 295 | return receivedPackets; 296 | } 297 | 298 | /** 299 | * Returns the IP address the server listens on. 300 | * Returns null if listening on the wildcard address. 301 | * 302 | * @return listen address or null 303 | */ 304 | public InetAddress getListenAddress() { 305 | return listenAddress; 306 | } 307 | 308 | /** 309 | * Sets the address the server listens on. 310 | * Must be called before start(). 311 | * Defaults to null, meaning listen on every 312 | * local address (wildcard address). 313 | * 314 | * @param listenAddress 315 | * listen address or null 316 | */ 317 | public void setListenAddress(InetAddress listenAddress) { 318 | this.listenAddress = listenAddress; 319 | } 320 | 321 | /** 322 | * Copies all Proxy-State attributes from the request 323 | * packet to the response packet. 324 | * 325 | * @param request 326 | * request packet 327 | * @param answer 328 | * response packet 329 | */ 330 | protected void copyProxyState(RadiusPacket request, RadiusPacket answer) { 331 | List proxyStateAttrs = request.getAttributes(33); 332 | for (Iterator i = proxyStateAttrs.iterator(); i.hasNext();) { 333 | RadiusAttribute proxyStateAttr = (RadiusAttribute) i.next(); 334 | answer.addAttribute(proxyStateAttr); 335 | } 336 | } 337 | 338 | /** 339 | * Listens on the auth port (blocks the current thread). 340 | * Returns when stop() is called. 341 | * 342 | * @throws SocketException 343 | */ 344 | protected void listenAuth() throws SocketException { 345 | listen(getAuthSocket()); 346 | } 347 | 348 | /** 349 | * Listens on the acct port (blocks the current thread). 350 | * Returns when stop() is called. 351 | * 352 | * @throws SocketException 353 | */ 354 | protected void listenAcct() throws SocketException { 355 | listen(getAcctSocket()); 356 | } 357 | 358 | /** 359 | * Listens on the passed socket, blocks until stop() is called. 360 | * 361 | * @param s 362 | * socket to listen on 363 | */ 364 | protected void listen(final DatagramSocket s) { 365 | while (true) { 366 | try { 367 | final DatagramPacket packetIn = new DatagramPacket(new byte[RadiusPacket.MAX_PACKET_LENGTH], RadiusPacket.MAX_PACKET_LENGTH); 368 | // receive packet 369 | try { 370 | logger.trace("about to call socket.receive()"); 371 | s.receive(packetIn); 372 | if (logger.isDebugEnabled()) 373 | logger.debug("receive buffer size = " + s.getReceiveBufferSize()); 374 | } 375 | catch (SocketException se) { 376 | if (closing) { 377 | // end thread 378 | logger.info("got closing signal - end listen thread"); 379 | return; 380 | } 381 | // retry s.receive() 382 | logger.error("SocketException during s.receive() -> retry", se); 383 | continue; 384 | } 385 | 386 | if (executor == null) { 387 | processRequest(s, packetIn); 388 | } 389 | else { 390 | executor.submit(new Runnable() { 391 | 392 | @Override 393 | public void run() { 394 | processRequest(s, packetIn); 395 | } 396 | 397 | }); 398 | } 399 | } 400 | catch (SocketTimeoutException ste) { 401 | // this is expected behaviour 402 | logger.trace("normal socket timeout"); 403 | } 404 | catch (IOException ioe) { 405 | // error while reading/writing socket 406 | logger.error("communication error", ioe); 407 | } 408 | } 409 | } 410 | 411 | 412 | /** 413 | * Process a single received request 414 | * 415 | * @param s 416 | * socket to send response on 417 | * @param packetIn 418 | * data packet 419 | */ 420 | protected void processRequest(final DatagramSocket s, final DatagramPacket packetIn) { 421 | try { 422 | // check client 423 | final InetSocketAddress localAddress = (InetSocketAddress) s.getLocalSocketAddress(); 424 | final InetSocketAddress remoteAddress = new InetSocketAddress(packetIn.getAddress(), packetIn.getPort()); 425 | final String secret = getSharedSecret(remoteAddress, makeRadiusPacket(packetIn, "1234567890", RadiusPacket.RESERVED)); 426 | if (secret == null) { 427 | if (logger.isInfoEnabled()) 428 | logger.info("ignoring packet from unknown client " + remoteAddress + " received on local address " + localAddress); 429 | return; 430 | } 431 | 432 | // parse packet 433 | final RadiusPacket request = makeRadiusPacket(packetIn, secret, RadiusPacket.UNDEFINED); 434 | if (logger.isInfoEnabled()) 435 | logger.info("received packet from " + remoteAddress + " on local address " + localAddress + ": " + request); 436 | 437 | // handle packet 438 | logger.trace("about to call RadiusServer.handlePacket()"); 439 | final RadiusPacket response = handlePacket(localAddress, remoteAddress, request, secret); 440 | 441 | // send response 442 | if (response != null) { 443 | if (logger.isInfoEnabled()) 444 | logger.info("send response: " + response); 445 | final DatagramPacket packetOut = makeDatagramPacket(response, secret, remoteAddress.getAddress(), packetIn.getPort(), request); 446 | s.send(packetOut); 447 | } 448 | else 449 | logger.info("no response sent"); 450 | } 451 | catch (IOException ioe) { 452 | // error while reading/writing socket 453 | logger.error("communication error", ioe); 454 | } 455 | catch (RadiusException re) { 456 | // malformed packet 457 | logger.error("malformed Radius packet", re); 458 | } 459 | } 460 | 461 | /** 462 | * Handles the received Radius packet and constructs a response. 463 | * 464 | * @param localAddress 465 | * local address the packet was received on 466 | * @param remoteAddress 467 | * remote address the packet was sent by 468 | * @param request 469 | * the packet 470 | * @param sharedSecret 471 | * @return response packet or null for no response 472 | * @throws RadiusException 473 | * @throws IOException 474 | */ 475 | protected RadiusPacket handlePacket(InetSocketAddress localAddress, InetSocketAddress remoteAddress, RadiusPacket request, String sharedSecret) 476 | throws RadiusException, IOException { 477 | RadiusPacket response = null; 478 | 479 | // check for duplicates 480 | if (!isPacketDuplicate(request, remoteAddress)) { 481 | if (localAddress.getPort() == getAuthPort()) { 482 | // handle packets on auth port 483 | if (request instanceof AccessRequest) 484 | response = accessRequestReceived((AccessRequest) request, remoteAddress); 485 | else 486 | logger.error("unknown Radius packet type: " + request.getPacketType()); 487 | } 488 | else if (localAddress.getPort() == getAcctPort()) { 489 | // handle packets on acct port 490 | if (request instanceof AccountingRequest) 491 | response = accountingRequestReceived((AccountingRequest) request, remoteAddress); 492 | else 493 | logger.error("unknown Radius packet type: " + request.getPacketType()); 494 | } 495 | else { 496 | // ignore packet on unknown port 497 | } 498 | } 499 | else 500 | logger.info("ignore duplicate packet"); 501 | 502 | return response; 503 | } 504 | 505 | /** 506 | * Returns a socket bound to the auth port. 507 | * 508 | * @return socket 509 | * @throws SocketException 510 | */ 511 | protected DatagramSocket getAuthSocket() throws SocketException { 512 | if (authSocket == null) { 513 | if (getListenAddress() == null) 514 | authSocket = new DatagramSocket(getAuthPort()); 515 | else 516 | authSocket = new DatagramSocket(getAuthPort(), getListenAddress()); 517 | authSocket.setSoTimeout(getSocketTimeout()); 518 | } 519 | return authSocket; 520 | } 521 | 522 | /** 523 | * Returns a socket bound to the acct port. 524 | * 525 | * @return socket 526 | * @throws SocketException 527 | */ 528 | protected DatagramSocket getAcctSocket() throws SocketException { 529 | if (acctSocket == null) { 530 | if (getListenAddress() == null) 531 | acctSocket = new DatagramSocket(getAcctPort()); 532 | else 533 | acctSocket = new DatagramSocket(getAcctPort(), getListenAddress()); 534 | acctSocket.setSoTimeout(getSocketTimeout()); 535 | } 536 | return acctSocket; 537 | } 538 | 539 | /** 540 | * Creates a Radius response datagram packet from a RadiusPacket to be send. 541 | * 542 | * @param packet 543 | * RadiusPacket 544 | * @param secret 545 | * shared secret to encode packet 546 | * @param address 547 | * where to send the packet 548 | * @param port 549 | * destination port 550 | * @param request 551 | * request packet 552 | * @return new datagram packet 553 | * @throws IOException 554 | */ 555 | protected DatagramPacket makeDatagramPacket(RadiusPacket packet, String secret, InetAddress address, int port, RadiusPacket request) 556 | throws IOException { 557 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 558 | packet.encodeResponsePacket(bos, secret, request); 559 | byte[] data = bos.toByteArray(); 560 | 561 | DatagramPacket datagram = new DatagramPacket(data, data.length, address, port); 562 | return datagram; 563 | } 564 | 565 | /** 566 | * Creates a RadiusPacket for a Radius request from a received 567 | * datagram packet. 568 | * 569 | * @param packet 570 | * received datagram 571 | * @return RadiusPacket object 572 | * @exception RadiusException 573 | * malformed packet 574 | * @exception IOException 575 | * communication error (after getRetryCount() 576 | * retries) 577 | */ 578 | protected RadiusPacket makeRadiusPacket(DatagramPacket packet, String sharedSecret, int forceType) throws IOException, RadiusException { 579 | ByteArrayInputStream in = new ByteArrayInputStream(packet.getData()); 580 | return RadiusPacket.decodeRequestPacket(in, sharedSecret, forceType); 581 | } 582 | 583 | /** 584 | * Checks whether the passed packet is a duplicate. 585 | * A packet is duplicate if another packet with the same identifier 586 | * has been sent from the same host in the last time. 587 | * 588 | * @param packet 589 | * packet in question 590 | * @param address 591 | * client address 592 | * @return true if it is duplicate 593 | */ 594 | protected boolean isPacketDuplicate(RadiusPacket packet, InetSocketAddress address) { 595 | long now = System.currentTimeMillis(); 596 | long intervalStart = now - getDuplicateInterval(); 597 | 598 | byte[] authenticator = packet.getAuthenticator(); 599 | 600 | String uniqueKey = address.getAddress().getHostAddress()+ 601 | packet.getPacketIdentifier() + 602 | Arrays.toString(packet.getAuthenticator()); 603 | 604 | synchronized (receivedPackets) { 605 | if (lastClean == 0 || lastClean < now - getDuplicateInterval()) { 606 | lastClean = now; 607 | for (Iterator> i = receivedPackets.entrySet().iterator(); i.hasNext(); ) { 608 | Long receiveTime = i.next().getValue(); 609 | if (receiveTime < intervalStart) { 610 | // packet is older than duplicate interval 611 | i.remove(); 612 | } 613 | } 614 | } 615 | 616 | Long receiveTime = receivedPackets.get(uniqueKey); 617 | if (receiveTime == null) { 618 | receivedPackets.put(uniqueKey, System.currentTimeMillis()); 619 | return false; 620 | } else { 621 | return !(receiveTime < intervalStart); 622 | } 623 | } 624 | } 625 | 626 | private InetAddress listenAddress = null; 627 | private int authPort = 1812; 628 | private int acctPort = 1813; 629 | private DatagramSocket authSocket = null; 630 | private DatagramSocket acctSocket = null; 631 | private int socketTimeout = 3000; 632 | private HashMap receivedPackets = new HashMap<>(); 633 | private long lastClean; 634 | private long duplicateInterval = 30000; // 30 s 635 | protected transient boolean closing = false; 636 | private static Log logger = LogFactory.getLog(RadiusServer.class); 637 | 638 | } 639 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/util/RadiusUtil.java: -------------------------------------------------------------------------------- 1 | /** 2 | * $Id: RadiusUtil.java,v 1.2 2006/11/06 19:32:06 wuttke Exp $ 3 | * Created on 09.04.2005 4 | * @author Matthias Wuttke 5 | * @version $Revision: 1.2 $ 6 | */ 7 | package org.tinyradius.util; 8 | 9 | import java.io.UnsupportedEncodingException; 10 | 11 | /** 12 | * This class contains miscellaneous static utility functions. 13 | */ 14 | public class RadiusUtil { 15 | 16 | /** 17 | * Returns the passed string as a byte array containing the 18 | * string in UTF-8 representation. 19 | * @param str Java string 20 | * @return UTF-8 byte array 21 | */ 22 | public static byte[] getUtf8Bytes(String str) { 23 | try { 24 | return str.getBytes("UTF-8"); 25 | } catch (UnsupportedEncodingException uee) { 26 | return str.getBytes(); 27 | } 28 | } 29 | 30 | /** 31 | * Creates a string from the passed byte array containing the 32 | * string in UTF-8 representation. 33 | * @param utf8 UTF-8 byte array 34 | * @return Java string 35 | */ 36 | public static String getStringFromUtf8(byte[] utf8) { 37 | try { 38 | return new String(utf8, "UTF-8"); 39 | } catch (UnsupportedEncodingException uee) { 40 | return new String(utf8); 41 | } 42 | } 43 | 44 | /** 45 | * Returns the byte array as a hex string in the format 46 | * "0x1234". 47 | * @param data byte array 48 | * @return hex string 49 | */ 50 | public static String getHexString(byte[] data) { 51 | StringBuffer hex = new StringBuffer("0x"); 52 | if (data != null) 53 | for (int i = 0; i < data.length; i++) { 54 | String digit = Integer.toString(data[i] & 0x0ff, 16); 55 | if (digit.length() < 2) 56 | hex.append('0'); 57 | hex.append(digit); 58 | } 59 | return hex.toString(); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/org/tinyradius/util/package.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

This package contains helper classes for implementing Radius clients 6 | and servers.

7 | 8 |

The class RadiusClient can be used to send Radius packets to 9 | a Radius server.

10 | 11 |

The class RadiusServer must be subclassed in order to implement 12 | a Radius server.

13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main/resources/org/tinyradius/dictionary/default_dictionary: -------------------------------------------------------------------------------- 1 | ################################################ 2 | # Attributes 3 | ################################################ 4 | 5 | ATTRIBUTE User-Name 1 string 6 | ATTRIBUTE User-Password 2 octets 7 | ATTRIBUTE CHAP-Password 3 octets 8 | ATTRIBUTE NAS-IP-Address 4 ipaddr 9 | ATTRIBUTE NAS-Port 5 integer 10 | ATTRIBUTE Service-Type 6 integer 11 | ATTRIBUTE Framed-Protocol 7 integer 12 | ATTRIBUTE Framed-IP-Address 8 ipaddr 13 | ATTRIBUTE Framed-IP-Netmask 9 ipaddr 14 | ATTRIBUTE Framed-Routing 10 integer 15 | ATTRIBUTE Filter-Id 11 string 16 | ATTRIBUTE Framed-MTU 12 integer 17 | ATTRIBUTE Framed-Compression 13 integer 18 | ATTRIBUTE Login-IP-Host 14 ipaddr 19 | ATTRIBUTE Login-Service 15 integer 20 | ATTRIBUTE Login-TCP-Port 16 integer 21 | ATTRIBUTE Reply-Message 18 string 22 | ATTRIBUTE Callback-Number 19 string 23 | ATTRIBUTE Callback-Id 20 string 24 | ATTRIBUTE Framed-Route 22 string 25 | ATTRIBUTE Framed-IPX-Network 23 ipaddr 26 | ATTRIBUTE State 24 octets 27 | ATTRIBUTE Class 25 string 28 | ATTRIBUTE Vendor-Specific 26 octets 29 | ATTRIBUTE Session-Timeout 27 integer 30 | ATTRIBUTE Idle-Timeout 28 integer 31 | ATTRIBUTE Termination-Action 29 integer 32 | ATTRIBUTE Called-Station-Id 30 string 33 | ATTRIBUTE Calling-Station-Id 31 string 34 | ATTRIBUTE NAS-Identifier 32 string 35 | ATTRIBUTE Proxy-State 33 octets 36 | ATTRIBUTE Login-LAT-Service 34 string 37 | ATTRIBUTE Login-LAT-Node 35 string 38 | ATTRIBUTE Login-LAT-Group 36 octets 39 | ATTRIBUTE Framed-AppleTalk-Link 37 integer 40 | ATTRIBUTE Framed-AppleTalk-Network 38 integer 41 | ATTRIBUTE Framed-AppleTalk-Zone 39 string 42 | ATTRIBUTE Acct-Status-Type 40 integer 43 | ATTRIBUTE Acct-Delay-Time 41 integer 44 | ATTRIBUTE Acct-Input-Octets 42 integer 45 | ATTRIBUTE Acct-Output-Octets 43 integer 46 | ATTRIBUTE Acct-Session-Id 44 string 47 | ATTRIBUTE Acct-Authentic 45 integer 48 | ATTRIBUTE Acct-Session-Time 46 integer 49 | ATTRIBUTE Acct-Input-Packets 47 integer 50 | ATTRIBUTE Acct-Output-Packets 48 integer 51 | ATTRIBUTE Acct-Terminate-Cause 49 integer 52 | ATTRIBUTE Acct-Multi-Session-Id 50 string 53 | ATTRIBUTE Acct-Link-Count 51 integer 54 | ATTRIBUTE Acct-Input-Gigawords 52 integer 55 | ATTRIBUTE Acct-Output-Gigawords 53 integer 56 | ATTRIBUTE Event-Timestamp 55 date 57 | ATTRIBUTE CHAP-Challenge 60 octets 58 | ATTRIBUTE NAS-Port-Type 61 integer 59 | ATTRIBUTE Port-Limit 62 integer 60 | ATTRIBUTE Login-LAT-Port 63 integer 61 | ATTRIBUTE Acct-Tunnel-Connection 68 string 62 | ATTRIBUTE ARAP-Password 70 string 63 | ATTRIBUTE ARAP-Features 71 string 64 | ATTRIBUTE ARAP-Zone-Access 72 integer 65 | ATTRIBUTE ARAP-Security 73 integer 66 | ATTRIBUTE ARAP-Security-Data 74 string 67 | ATTRIBUTE Password-Retry 75 integer 68 | ATTRIBUTE Prompt 76 integer 69 | ATTRIBUTE Connect-Info 77 string 70 | ATTRIBUTE Configuration-Token 78 string 71 | ATTRIBUTE EAP-Message 79 octets 72 | ATTRIBUTE Message-Authenticator 80 octets 73 | ATTRIBUTE ARAP-Challenge-Response 84 string 74 | ATTRIBUTE Acct-Interim-Interval 85 integer 75 | ATTRIBUTE NAS-Port-Id 87 string 76 | ATTRIBUTE Framed-Pool 88 string 77 | ATTRIBUTE NAS-IPv6-Address 95 ipv6addr 78 | ATTRIBUTE Framed-Interface-Id 96 octets 79 | ATTRIBUTE Framed-IPv6-Prefix 97 ipv6prefix 80 | ATTRIBUTE Login-IPv6-Host 98 octets 81 | ATTRIBUTE Framed-IPv6-Route 99 string 82 | ATTRIBUTE Framed-IPv6-Pool 100 string 83 | ATTRIBUTE Error-Cause 101 integer 84 | ATTRIBUTE Delegated-IPv6-Prefix 123 ipv6prefix 85 | ATTRIBUTE Framed-IPv6-Address 168 ipv6addr 86 | ATTRIBUTE DNS-Server-IPv6-Address 169 ipv6addr 87 | ATTRIBUTE Route-IPv6-Information 170 octets 88 | ATTRIBUTE Delegated-IPv6-Prefix-Pool 171 string 89 | ATTRIBUTE Stateful-IPv6-Address-Pool 172 string 90 | ATTRIBUTE Digest-Response 206 string 91 | ATTRIBUTE Digest-Attributes 207 octets 92 | 93 | ################################################ 94 | # Attribute values 95 | ################################################ 96 | 97 | # User Types 98 | 99 | VALUE Service-Type Login-User 1 100 | VALUE Service-Type Framed-User 2 101 | VALUE Service-Type Callback-Login-User 3 102 | VALUE Service-Type Callback-Framed-User 4 103 | VALUE Service-Type Outbound-User 5 104 | VALUE Service-Type Administrative-User 6 105 | VALUE Service-Type NAS-Prompt-User 7 106 | VALUE Service-Type Authenticate-Only 8 107 | VALUE Service-Type Callback-NAS-Prompt 9 108 | VALUE Service-Type Call-Check 10 109 | VALUE Service-Type Callback-Administrative 11 110 | VALUE Service-Type Voice 12 111 | VALUE Service-Type Fax 13 112 | VALUE Service-Type Modem-Relay 14 113 | VALUE Service-Type IAPP-Register 15 114 | VALUE Service-Type IAPP-AP-Check 16 115 | 116 | # Framed Protocols 117 | 118 | VALUE Framed-Protocol PPP 1 119 | VALUE Framed-Protocol SLIP 2 120 | VALUE Framed-Protocol ARAP 3 121 | VALUE Framed-Protocol Gandalf-SLML 4 122 | VALUE Framed-Protocol Xylogics-IPX-SLIP 5 123 | VALUE Framed-Protocol X.75-Synchronous 6 124 | VALUE Framed-Protocol GPRS-PDP-Context 7 125 | 126 | # Framed Routing Values 127 | 128 | VALUE Framed-Routing None 0 129 | VALUE Framed-Routing Broadcast 1 130 | VALUE Framed-Routing Listen 2 131 | VALUE Framed-Routing Broadcast-Listen 3 132 | 133 | # Framed Compression Types 134 | 135 | VALUE Framed-Compression None 0 136 | VALUE Framed-Compression Van-Jacobson-TCP-IP 1 137 | VALUE Framed-Compression IPX-Header-Compression 2 138 | VALUE Framed-Compression Stac-LZS 3 139 | 140 | # Login Services 141 | 142 | VALUE Login-Service Telnet 0 143 | VALUE Login-Service Rlogin 1 144 | VALUE Login-Service TCP-Clear 2 145 | VALUE Login-Service PortMaster 3 146 | VALUE Login-Service LAT 4 147 | VALUE Login-Service X25-PAD 5 148 | VALUE Login-Service X25-T3POS 6 149 | VALUE Login-Service TCP-Clear-Quiet 7 150 | 151 | # Status Types 152 | 153 | VALUE Acct-Status-Type Start 1 154 | VALUE Acct-Status-Type Stop 2 155 | VALUE Acct-Status-Type Interim-Update 3 156 | VALUE Acct-Status-Type Alive 3 157 | VALUE Acct-Status-Type Accounting-On 7 158 | VALUE Acct-Status-Type Accounting-Off 8 159 | # RFC 2867 Additional Status-Type Values 160 | VALUE Acct-Status-Type Tunnel-Start 9 161 | VALUE Acct-Status-Type Tunnel-Stop 10 162 | VALUE Acct-Status-Type Tunnel-Reject 11 163 | VALUE Acct-Status-Type Tunnel-Link-Start 12 164 | VALUE Acct-Status-Type Tunnel-Link-Stop 13 165 | VALUE Acct-Status-Type Tunnel-Link-Reject 14 166 | VALUE Acct-Status-Type Failed 15 167 | 168 | # Authentication Types 169 | 170 | VALUE Acct-Authentic RADIUS 1 171 | VALUE Acct-Authentic Local 2 172 | VALUE Acct-Authentic Remote 3 173 | VALUE Acct-Authentic Diameter 4 174 | 175 | # Termination Options 176 | 177 | VALUE Termination-Action Default 0 178 | VALUE Termination-Action RADIUS-Request 1 179 | 180 | # NAS Port Types 181 | 182 | VALUE NAS-Port-Type Async 0 183 | VALUE NAS-Port-Type Sync 1 184 | VALUE NAS-Port-Type ISDN 2 185 | VALUE NAS-Port-Type ISDN-V120 3 186 | VALUE NAS-Port-Type ISDN-V110 4 187 | VALUE NAS-Port-Type Virtual 5 188 | VALUE NAS-Port-Type PIAFS 6 189 | VALUE NAS-Port-Type HDLC-Clear-Channel 7 190 | VALUE NAS-Port-Type X.25 8 191 | VALUE NAS-Port-Type X.75 9 192 | VALUE NAS-Port-Type G.3-Fax 10 193 | VALUE NAS-Port-Type SDSL 11 194 | VALUE NAS-Port-Type ADSL-CAP 12 195 | VALUE NAS-Port-Type ADSL-DMT 13 196 | VALUE NAS-Port-Type IDSL 14 197 | VALUE NAS-Port-Type Ethernet 15 198 | VALUE NAS-Port-Type xDSL 16 199 | VALUE NAS-Port-Type Cable 17 200 | VALUE NAS-Port-Type Wireless-Other 18 201 | VALUE NAS-Port-Type Wireless-802.11 19 202 | VALUE NAS-Port-Type Token-Ring 20 203 | VALUE NAS-Port-Type FDDI 21 204 | VALUE NAS-Port-Type Wireless-CDMA2000 22 205 | VALUE NAS-Port-Type Wireless-UMTS 23 206 | VALUE NAS-Port-Type Wireless-1X-EV 24 207 | VALUE NAS-Port-Type IAPP 25 208 | 209 | # Acct Terminate Causes, available in 3.3.2 and later 210 | 211 | VALUE Acct-Terminate-Cause User-Request 1 212 | VALUE Acct-Terminate-Cause Lost-Carrier 2 213 | VALUE Acct-Terminate-Cause Lost-Service 3 214 | VALUE Acct-Terminate-Cause Idle-Timeout 4 215 | VALUE Acct-Terminate-Cause Session-Timeout 5 216 | VALUE Acct-Terminate-Cause Admin-Reset 6 217 | VALUE Acct-Terminate-Cause Admin-Reboot 7 218 | VALUE Acct-Terminate-Cause Port-Error 8 219 | VALUE Acct-Terminate-Cause NAS-Error 9 220 | VALUE Acct-Terminate-Cause NAS-Request 10 221 | VALUE Acct-Terminate-Cause NAS-Reboot 11 222 | VALUE Acct-Terminate-Cause Port-Unneeded 12 223 | VALUE Acct-Terminate-Cause Port-Preempted 13 224 | VALUE Acct-Terminate-Cause Port-Suspended 14 225 | VALUE Acct-Terminate-Cause Service-Unavailable 15 226 | VALUE Acct-Terminate-Cause Callback 16 227 | VALUE Acct-Terminate-Cause User-Error 17 228 | VALUE Acct-Terminate-Cause Host-Request 18 229 | VALUE Acct-Terminate-Cause Supplicant-Restart 19 230 | VALUE Acct-Terminate-Cause Reauthentication-Failure 20 231 | VALUE Acct-Terminate-Cause Port-Reinit 21 232 | VALUE Acct-Terminate-Cause Port-Disabled 22 233 | 234 | # Prompt 235 | 236 | VALUE Prompt No-Echo 0 237 | VALUE Prompt Echo 1 238 | 239 | 240 | VALUE Error-Cause Residual-Context-Removed 201 241 | VALUE Error-Cause Invalid-EAP-Packet 202 242 | VALUE Error-Cause Unsupported-Attribute 401 243 | VALUE Error-Cause Missing-Attribute 402 244 | VALUE Error-Cause NAS-Identification-Mismatch 403 245 | VALUE Error-Cause Invalid-Request 404 246 | VALUE Error-Cause Unsupported-Service 405 247 | VALUE Error-Cause Unsupported-Extension 406 248 | VALUE Error-Cause Invalid-Attribute-Value 407 249 | VALUE Error-Cause Administratively-Prohibited 501 250 | VALUE Error-Cause Proxy-Request-Not-Routable 502 251 | VALUE Error-Cause Session-Context-Not-Found 503 252 | VALUE Error-Cause Session-Context-Not-Removable 504 253 | VALUE Error-Cause Proxy-Processing-Error 505 254 | VALUE Error-Cause Resources-Unavailable 506 255 | VALUE Error-Cause Request-Initiated 507 256 | VALUE Error-Cause Multiple-Session-Selection-Unsupported 508 257 | VALUE Error-Cause Location-Info-Required 509 258 | VALUE Error-Cause Response-Too-Big 601 259 | 260 | 261 | # Wi-Fi Alliance - Wireless ISP Roaming - Best Current Practices v1, 262 | # Feb 2003, p 14 263 | # http://www.weca.net/OpenSection/downloads/WISPr_V1.0.pdf 264 | 265 | VENDOR 14122 WISPr 266 | 267 | VENDORATTR 14122 WISPr-Location-ID 1 string 268 | VENDORATTR 14122 WISPr-Location-Name 2 string 269 | VENDORATTR 14122 WISPr-Logoff-URL 3 string 270 | VENDORATTR 14122 WISPr-Redirection-URL 4 string 271 | VENDORATTR 14122 WISPr-Bandwidth-Min-Up 5 integer 272 | VENDORATTR 14122 WISPr-Bandwidth-Min-Down 6 integer 273 | VENDORATTR 14122 WISPr-Bandwidth-Max-Up 7 integer 274 | VENDORATTR 14122 WISPr-Bandwidth-Max-Down 8 integer 275 | VENDORATTR 14122 WISPr-Session-Terminate-Time 9 string 276 | VENDORATTR 14122 WISPr-Session-Terminate-End-Of-Day 10 integer 277 | VENDORATTR 14122 WISPr-Billing-Class-Of-Service 11 string 278 | 279 | -------------------------------------------------------------------------------- /src/test/java/org/tinyradius/attribute/IntegerAttributeTest.java: -------------------------------------------------------------------------------- 1 | package org.tinyradius.attribute; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import org.junit.Test; 6 | 7 | public class IntegerAttributeTest { 8 | 9 | @Test 10 | public void test() { 11 | final IntegerAttribute intAttr = new IntegerAttribute(27, 0); 12 | final long bigValue = 0xffffffffl; // big value with highest bit set 13 | System.err.println((int)bigValue); 14 | System.err.println(bigValue); 15 | final String bigValueSt = Long.toString(bigValue); 16 | intAttr.setAttributeValue(bigValueSt); 17 | assertEquals(bigValueSt, intAttr.getAttributeValue()); 18 | } 19 | 20 | } 21 | --------------------------------------------------------------------------------