├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── Dockerfile ├── LICENSE ├── README.md ├── client └── src │ ├── Arguments.cpp │ ├── Arguments.h │ ├── CMakeLists.txt │ └── main.cpp ├── coap ├── fuzzing │ ├── CMakeLists.txt │ └── fuzzer.cpp ├── include │ ├── Client.h │ ├── CoAP.h │ ├── Code.h │ ├── IMessaging.h │ ├── Logging.h │ ├── MClient.h │ ├── Notifications.h │ ├── Observable.h │ ├── Optional.h │ ├── Parameters.h │ ├── Path.h │ ├── PathPattern.h │ ├── RequestHandler.h │ ├── RequestHandlers.h │ ├── RestResponse.h │ ├── StringHelpers.h │ ├── URI.h │ └── json.h ├── src │ ├── CMakeLists.txt │ ├── Client.cpp │ ├── ClientImpl.cpp │ ├── ClientImpl.h │ ├── CoAP.cpp │ ├── Code.cpp │ ├── Connection.cpp │ ├── Connection.h │ ├── IConnection.h │ ├── MClient.cpp │ ├── Message.cpp │ ├── Message.h │ ├── Messaging.cpp │ ├── Messaging.h │ ├── NetUtils.cpp │ ├── NetUtils.h │ ├── Path.cpp │ ├── PathPattern.cpp │ ├── RequestHandler.cpp │ ├── RequestHandlers.cpp │ ├── ServerImpl.cpp │ ├── ServerImpl.h │ ├── Telegram.h │ └── URI.cpp └── tests │ ├── CMakeLists.txt │ ├── ClientTests.cpp │ ├── CodeTests.cpp │ ├── ConnectionMock.h │ ├── ConnectionTests.cpp │ ├── MClientTest.cpp │ ├── MessageTests.cpp │ ├── NetUtilsTest.cpp │ ├── ObservableTests.cpp │ ├── OptionalTests.cpp │ ├── PathPatternTests.cpp │ ├── PathTests.cpp │ ├── PerformanceTest.cpp │ ├── RequestHandlerTests.cpp │ ├── ServerTests.cpp │ ├── StringHelpersTests.cpp │ ├── URITests.cpp │ └── jsonTests.cpp ├── server └── src │ ├── CMakeLists.txt │ └── main.cpp └── tests └── tests.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | sudo: required 4 | 5 | dist: trusty 6 | 7 | compiler: 8 | - gcc 9 | 10 | install: 11 | # - sudo apt-get install -qq libgtest-dev 12 | - pushd /usr/src/gtest && sudo cmake CMakeLists.txt && sudo make && sudo cp *.a /usr/lib && popd 13 | 14 | before_script: 15 | - export CC=gcc-6 16 | - export CXX=g++-6 17 | - mkdir build 18 | - cd build 19 | - cmake .. 20 | 21 | script: 22 | - make 23 | - ctest --output-on-error 24 | 25 | env: 26 | global: 27 | # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created 28 | # via the "travis encrypt" command using the project repo's public key 29 | - secure: "c4IY+XZwIcMbMipjYnS9sedUO9nCnBZtbEnZKLuIWH3y626DasL+hy08vG4Y5XnlUMiAet7M1lQdk5C2wZWkPMg4PSWKlfQPvCqBQbacc1IJPEUK3B63v1Ox973h4doPE3p3kAI+e2fsFue+y3BtZ4o7v4XBedVDz8ywP4r5tpMZS31oM0raXxXAj9QEE5yl2tE/o/OOlOt3jssQsfCS/h730uwsDv0U+KOnwCG29dK1F2zcLAMxNTYppWq856jEdltK3BbS8BGnMc5O9hy+Aa2YQYxTGQongybeHiEykdPUEnMPmB0k+qma2WO7rvIFUqP0R8vVm7MRzCS2besO7bkg8iJ6elsXO3bzeGwdDaM8u/m1K05gxPElx5QZIG/TPghFbFtoGSqlc/ffo7f1RW2BBegOJgKMV8MBcb8dE99xpBJh6lGzHyVUd/rZewINJ1/Q5headf7/mpECChM+XzweZc048FO1R3aA4kaUXfmFhCvxRrHLJnUyQlALBRMtS20qQbO62WlriFesCTySjXXs2EPiX1vaPKTB5oFJA54zaCWLLRsf4sPc4yTNUozJzJ93UnoThi2oavAld6/srdq+C//TGUZxMk5kyJd+wdyifSMuPBXNqEekykRLvRS7PJqTMRoo6EfIIHYx/QKw1n+6XOmSbFl5RAN9ah6lk0g=" 30 | 31 | before_install: 32 | - echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca- 33 | 34 | addons: 35 | coverity_scan: 36 | project: 37 | name: "zerom0/CoaPP" 38 | description: "Build submitted via Travis CI" 39 | notification_email: github@martinmosler.de 40 | build_command_prepend: "cov-configure --comptype g++ --compiler g++-6 --template" 41 | build_command: "make -j 4" 42 | branch_pattern: coverity_scan 43 | apt: 44 | sources: 45 | - ubuntu-toolchain-r-test 46 | packages: 47 | - g++-6 48 | - libgtest-dev 49 | - cmake -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.7) 2 | project(CoaP) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall -Wextra -Wpedantic") 5 | 6 | add_subdirectory(coap/src) 7 | 8 | add_subdirectory(client/src) 9 | add_subdirectory(server/src) 10 | 11 | # Locate GTest 12 | find_package(GTest REQUIRED) 13 | 14 | enable_testing() 15 | add_subdirectory(coap/tests) 16 | 17 | #add_subdirectory(coap/fuzzing) 18 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | # Build the docker image with 6 | # docker build --rm -t coapp . 7 | # and run the docker image with 8 | # docker run --rm -v `pwd`:/src coapp 9 | 10 | FROM ubuntu:12.04 11 | 12 | RUN apt-get install -qq python-software-properties 13 | RUN add-apt-repository -y ppa:ubuntu-toolchain-r/test 14 | 15 | # Install CMake and gtest 16 | # 17 | RUN apt-get -qq update && apt-get install -qq cmake make libgtest-dev 18 | RUN cmake --version 19 | 20 | # Install g++ v4.8 21 | # 22 | RUN apt-get install -qq g++-4.8 23 | RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 20 24 | RUN update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 20 25 | RUN update-alternatives --config gcc 26 | RUN update-alternatives --config g++ 27 | RUN g++ --version 28 | 29 | 30 | RUN cd /usr/src/gtest && cmake CMakeLists.txt && make && cp *.a /usr/lib 31 | 32 | VOLUME ["/src"] 33 | 34 | CMD mkdir -p /src/testbuild && cd /src/testbuild && cmake .. && make && ctest --output-on-error 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/zerom0/CoaPP.svg?branch=master)](https://travis-ci.org/zerom0/CoaPP) 2 | [![Coverity Scan Build Status](https://scan.coverity.com/projects/8067/badge.svg)](https://scan.coverity.com/projects/zerom0-coapp) 3 | [![MPLv2 License](https://img.shields.io/badge/license-MPLv2-blue.svg?style=flat-square)](https://www.mozilla.org/MPL/2.0/) 4 | 5 | # CoaPP 6 | 7 | C++11 implementation of the CoAP 8 | 9 | # Running the testsuite 10 | 11 | Install googletest before building with cmake 12 | 13 | git clone https://github.com/google/googletest.git gtest 14 | 15 | Execute the tests with 16 | 17 | ctest --output-on-failure 18 | -------------------------------------------------------------------------------- /client/src/Arguments.cpp: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #include "Arguments.h" 6 | 7 | #include 8 | 9 | namespace { 10 | std::string toLower(const std::string& from) { 11 | auto copy = from; 12 | std::transform(copy.begin(), copy.end(), copy.begin(), tolower); 13 | return copy; 14 | } 15 | } 16 | 17 | Optional Arguments::fromArgv(int argc, const char** argv) { 18 | Arguments arguments; 19 | 20 | if (argc < 3) return Optional(); 21 | 22 | auto arg = 1; 23 | 24 | arguments.setRequest(toLower(argv[arg++])); 25 | auto s = toLower(argv[arg++]); 26 | if (s.compare("-n") == 0) { 27 | arguments.setNonConfirmable(); 28 | s = std::string(argv[arg++]); 29 | } 30 | 31 | auto uri = URI::fromString(s); 32 | if (!uri) return Optional(); 33 | arguments.setUri(std::move(uri.value())); 34 | 35 | if (arg < argc) { 36 | arguments.setPayload(argv[arg++]); 37 | } 38 | 39 | return Optional(arguments); 40 | } 41 | -------------------------------------------------------------------------------- /client/src/Arguments.h: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #pragma once 6 | 7 | #ifndef __Arguments_h 8 | #define __Arguments_h 9 | 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | class Arguments { 16 | public: 17 | static Optional fromArgv(int argc, const char** argv); 18 | 19 | const std::string& getRequest() const { 20 | return request_; 21 | } 22 | 23 | const URI& getUri() const { 24 | return uri_; 25 | } 26 | 27 | const std::string& getPayload() const { 28 | return payload_; 29 | } 30 | 31 | bool isConfirmable() const { 32 | return confirmable_; 33 | } 34 | 35 | private: 36 | void setRequest(const std::string& request) { request_ = request; } 37 | void setUri(URI uri) { uri_ = uri; } 38 | void setPayload(const std::string& payload) { payload_ = payload; } 39 | void setNonConfirmable() { confirmable_ = false; } 40 | 41 | std::string request_; 42 | URI uri_; 43 | std::string payload_; 44 | bool confirmable_{true}; 45 | }; 46 | 47 | 48 | #endif //__Arguments_h 49 | -------------------------------------------------------------------------------- /client/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.7) 2 | project(coap_client) 3 | 4 | file(GLOB_RECURSE SRCS *.cpp) 5 | 6 | include_directories(../../coap/include) 7 | 8 | add_executable(coap_client ${SRCS}) 9 | 10 | target_link_libraries(coap_client coap) 11 | 12 | install(TARGETS coap_client RUNTIME DESTINATION bin) 13 | -------------------------------------------------------------------------------- /client/src/main.cpp: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #include "CoAP.h" 6 | #include "URI.h" 7 | #include "Arguments.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | SETLOGLEVEL(LLWARNING) 14 | 15 | // Sample arguments: 16 | // 17 | // GET -n coap://*:5683/.well-known/core 18 | // GET -n coap://localhost:5683/.well-known/core 19 | // PUT -n coap://localhost:5683/actions/shutdown now 20 | // 21 | 22 | void usage() { 23 | std::cout << "Usage: client [-n] []\n"; 24 | std::cout << " request : get/put/post/delete/observe\n"; 25 | std::cout << " -n : nonconfirmable message\n"; 26 | std::cout << " uri : coap://localhost:5683/.well-known/core\n"; 27 | std::cout << " Use * instead of the servername for multicast\n"; 28 | std::cout << " payload : string with payload\n"; 29 | exit(1); 30 | } 31 | 32 | void printResponse(const CoAP::RestResponse& response) { 33 | std::cout << response.code(); 34 | if (response.hasContentFormat()) { 35 | std::cout << " - ContentFormat: " << response.contentFormat(); 36 | } 37 | std::cout << '\n'; 38 | std::cout << response.payload() << '\n'; 39 | } 40 | 41 | int main(int argc, const char* argv[]) { 42 | const auto arguments = Arguments::fromArgv(argc, argv); 43 | if (!arguments) usage(); 44 | 45 | std::unique_ptr messaging; 46 | uint16_t port = 9999; 47 | while (!messaging && port > 9900) { 48 | try { 49 | messaging = CoAP::newMessaging(port); 50 | } catch (std::exception& e) { 51 | auto triedPort = port--; 52 | std::cout << "Port " << triedPort << " already in use, trying port " << port << " next.\n"; 53 | } 54 | } 55 | ILOG << "Using port " << port << " for client."; 56 | messaging->loopStart(); 57 | bool exit(true); 58 | 59 | const auto uri = arguments.value().getUri(); 60 | const auto requestType = arguments.value().getRequest(); 61 | std::shared_ptr> notifications; 62 | int receivedNotifications = 0; 63 | 64 | if (uri.getServer() == "*") { 65 | // Multicast requests 66 | // 67 | 68 | if (requestType != "get") { 69 | std::cout << "Invalid request type " << requestType << " for multicast requests\n"; 70 | usage(); 71 | } 72 | 73 | auto client = messaging->getMulticastClient(); 74 | 75 | notifications = client.GET(uri.getPath()); 76 | 77 | std::vector responses; 78 | notifications->subscribe([&responses](const CoAP::RestResponse& response){ 79 | responses.push_back(response); 80 | }); 81 | while (responses.empty()); 82 | 83 | auto response = responses.front(); 84 | in_addr addr = {response.fromIp()}; 85 | std::cout << "IP: " << inet_ntoa(addr) << " Port: " << response.fromPort() << '\n'; 86 | std::cout << response.code(); 87 | if (response.hasContentFormat()) { 88 | std::cout << " - ContentFormat: " << response.contentFormat(); 89 | } 90 | std::cout << '\n'; 91 | std::cout << response.payload() << '\n'; 92 | } 93 | else { 94 | // Unicast requests 95 | // 96 | auto client = messaging->getClientFor(uri.getServer().c_str()); 97 | const auto payload = arguments.value().getPayload(); 98 | 99 | Optional response; 100 | 101 | if (requestType == "get") { 102 | response = client.GET(uri.getPath(), arguments.value().isConfirmable()).get(); 103 | } 104 | else if (requestType == "put") { 105 | response = client.PUT(uri.getPath(), payload, arguments.value().isConfirmable()).get(); 106 | } 107 | else if (requestType == "post") { 108 | response = client.POST(uri.getPath(), payload, arguments.value().isConfirmable()).get(); 109 | } 110 | else if (requestType == "delete") { 111 | response = client.DELETE(uri.getPath(), arguments.value().isConfirmable()).get(); 112 | } 113 | else if (requestType == "observe") { 114 | exit = false; 115 | notifications = client.OBSERVE(uri.getPath(), arguments.value().isConfirmable()); 116 | notifications->subscribe([&exit, ¬ifications, &receivedNotifications](const CoAP::RestResponse& response) { 117 | printResponse(response); 118 | if (++receivedNotifications == 10) { 119 | notifications.reset(); 120 | exit = true; 121 | } 122 | }); 123 | } 124 | else { 125 | std::cerr << "Unknown request type: " << requestType << '\n'; 126 | } 127 | 128 | if (response) printResponse(response.value()); 129 | } 130 | 131 | while (!exit) { 132 | timespec t; 133 | t.tv_sec = 0; 134 | t.tv_nsec = 100*1000*1000; 135 | nanosleep(&t, &t); 136 | } 137 | 138 | messaging->loopStop(); 139 | } 140 | -------------------------------------------------------------------------------- /coap/fuzzing/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.7) 2 | project(CoaP) 3 | 4 | set(SRCS 5 | fuzzer.cpp 6 | ../src/Message.cpp 7 | ../src/Path.cpp) 8 | 9 | include_directories( 10 | ../include 11 | ../src 12 | ) 13 | 14 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") 15 | 16 | add_executable(fuzzer ${SRCS}) 17 | 18 | target_link_libraries(fuzzer) -------------------------------------------------------------------------------- /coap/fuzzing/fuzzer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | // afl-clang++ -g -I ../src -I ../include/ --std=c++14 -fsanitize=address ./fuzzer.cpp ../src/Message.cpp ../src/Path.cpp 9 | // afl-fuzz -i testcases -o findings ./a.out @@ 10 | 11 | int main(int argc, char **argv) { 12 | assert (argc > 1); 13 | std::ifstream file(argv[1], std::ios::in | std::ios::binary | std::ios::ate); 14 | std::streamsize size = file.tellg(); 15 | assert (size >= 0); 16 | file.seekg(0, std::ios::beg); 17 | CoAP::Message::Buffer buffer(size); 18 | file.read((char *) buffer.data(), size); 19 | file.close(); 20 | 21 | try { 22 | auto p = Path::fromBuffer(buffer); 23 | for (auto i = 0U; i < p.size(); ++i) { 24 | p.getPart(i); 25 | } 26 | auto b = p.toBuffer(); 27 | assert(b == buffer); 28 | } catch (std::exception &e) { 29 | } 30 | return 0; 31 | } -------------------------------------------------------------------------------- /coap/include/Client.h: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #pragma once 6 | 7 | #ifndef __Client_h 8 | #define __Client_h 9 | 10 | #include "Notifications.h" 11 | #include "RestResponse.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace CoAP { 19 | 20 | class ClientImpl; 21 | class IRequestHandler; 22 | 23 | /* 24 | * Class: Client 25 | * 26 | * Instance of CoAP client connected to a CoAP server. It provides users with the 27 | * ability to send REST requests in an asynchronous way. 28 | * 29 | * Clients can be created with . 30 | */ 31 | class Client { 32 | public: 33 | Client(ClientImpl& impl, std::string server, uint16_t server_port); 34 | 35 | /* 36 | * Method: GET 37 | * 38 | * Sends a GET request to the CoAP server in order to read a resource. 39 | * 40 | * Parameters: 41 | * uri - URI of the ressource to be returned 42 | * confirmable - true: confirmable messaging (default) / 43 | * false: nonconfirmable messaging 44 | * 45 | * Returns: 46 | * A future with the , once it will be received. 47 | */ 48 | std::future GET(std::string uri, bool confirmable = false); 49 | 50 | /* 51 | * Method: PUT 52 | * 53 | * Sends a PUT request to the CoAP server in order to update a resource. 54 | * 55 | * Parameters: 56 | * uri - URI of the ressource to be updated 57 | * payload - Payload of the PUT request being sent 58 | * confirmable - true: confirmable messaging (default) / 59 | * false: nonconfirmable messaging 60 | * 61 | * Returns: 62 | * A future with the , once it will be received. 63 | */ 64 | std::future PUT(std::string uri, std::string payload, bool confirmable = false); 65 | 66 | /* 67 | * Method: POST 68 | * 69 | * Sends a POST request to the CoAP server in order to create a resource. 70 | * 71 | * Parameters: 72 | * uri - URI of the ressource to be created 73 | * payload - Payload of the PUT request being sent 74 | * confirmable - true: confirmable messaging (default) / 75 | * false: nonconfirmable messaging 76 | * 77 | * Returns: 78 | * A future with the , once it will be received. 79 | */ 80 | std::future POST(std::string uri, std::string payload, bool confirmable = false); 81 | 82 | /* 83 | * Method: DELETE 84 | * 85 | * Sends a DELETE request to the CoAP server in order to delete a resource. 86 | * 87 | * Parameters: 88 | * uri - URI of the ressource to be deleted 89 | * confirmable - true: confirmable messaging (default) / 90 | * false: nonconfirmable messaging 91 | * 92 | * Returns: 93 | * A future with the , once it will be received. 94 | */ 95 | std::future DELETE(std::string uri, bool confirmable = false); 96 | 97 | /* 98 | * Method: PING 99 | * 100 | * Sends a PING request to the CoAP server to check if it is available. 101 | * 102 | * Returns: 103 | * A future with the , once it will be received. 104 | */ 105 | std::future PING(); 106 | 107 | /** 108 | * Method: OBSERVE 109 | * 110 | * Sends an Observation request to the CoAP server to receive regular updates 111 | * of the resource. 112 | * 113 | * Parameters: 114 | * uri - URI of the resource to be observed 115 | * confirmable - true: confirmable messaging (default) / 116 | * false: nonconfirmable messaging 117 | * 118 | * Returns: 119 | * Notifications with updated representations of the resource. 120 | */ 121 | std::shared_ptr OBSERVE(std::string uri, bool confirmable = false); 122 | 123 | private: 124 | std::future asFuture(const std::shared_ptr& responses); 125 | 126 | ClientImpl& impl_; 127 | 128 | in_addr_t server_ip_; 129 | uint16_t server_port_; 130 | 131 | // Continuously increasing unique id for promises made by this client 132 | unsigned id_{0}; 133 | 134 | // Promises made by this client 135 | std::map, std::shared_ptr>> promises_; 136 | }; 137 | 138 | } // namespace CoAP 139 | 140 | #endif // __Client_h 141 | -------------------------------------------------------------------------------- /coap/include/CoAP.h: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #pragma once 6 | 7 | #ifndef __CoAP_h 8 | #define __CoAP_h 9 | 10 | #include "Client.h" 11 | #include "RequestHandler.h" 12 | #include "RequestHandlers.h" 13 | #include "IMessaging.h" 14 | 15 | namespace CoAP { 16 | 17 | /* 18 | * Function: newMessaging 19 | * 20 | * This is the entry point into the CoAP client and server and instantiates a 21 | * CoAP messaging system. 22 | * 23 | * Parameters: 24 | * port - UDP port on which the server is listening for requests. 25 | * 26 | * Returns: 27 | * An instance of the messaging system. 28 | * 29 | * See: 30 | * 31 | */ 32 | std::unique_ptr newMessaging(uint16_t port = 5683); 33 | 34 | } // namespace CoAP 35 | 36 | #endif // __CoAP_h 37 | -------------------------------------------------------------------------------- /coap/include/Code.h: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #pragma once 6 | 7 | #ifndef __ResponseCode_h 8 | #define __ResponseCode_h 9 | 10 | #include 11 | 12 | namespace CoAP { 13 | 14 | #define COAP_CODES \ 15 | COAP_CODE(0x00, Empty) \ 16 | COAP_CODE(0x01, GET) \ 17 | COAP_CODE(0x02, POST) \ 18 | COAP_CODE(0x03, PUT) \ 19 | COAP_CODE(0x04, DELETE) \ 20 | COAP_CODE(0x41, Created) /* 2.01 */ \ 21 | COAP_CODE(0x42, Deleted) /* 2.02 */ \ 22 | COAP_CODE(0x43, Valid) /* 2.03 */ \ 23 | COAP_CODE(0x44, Changed) /* 2.04 */ \ 24 | COAP_CODE(0x45, Content) /* 2.05 */ \ 25 | COAP_CODE(0x80, BadRequest) /* 4.00 */ \ 26 | COAP_CODE(0x81, Unauthorized) /* 4.01 */ \ 27 | COAP_CODE(0x82, BadOption) /* 4.02 */ \ 28 | COAP_CODE(0x83, Forbidden) /* 4.03 */ \ 29 | COAP_CODE(0x84, NotFound) /* 4.04 */ \ 30 | COAP_CODE(0x85, MethodNotAllowed) /* 4.05 */ \ 31 | COAP_CODE(0x86, NotAcceptable) /* 4.06 */ \ 32 | COAP_CODE(0x8c, PreconditionFailed) /* 4.12 */ \ 33 | COAP_CODE(0x8d, RequestEntityTooLarge) /* 4.13 */ \ 34 | COAP_CODE(0x8f, UnsupportedContentFormat) /* 4.15 */ \ 35 | COAP_CODE(0xa0, InternalServerError) /* 5.00 */ \ 36 | COAP_CODE(0xa1, NotImplemented) /* 5.01 */ \ 37 | COAP_CODE(0xa2, BadGateway) /* 5.02 */ \ 38 | COAP_CODE(0xa3, ServiceUnavailable) /* 5.03 */ \ 39 | COAP_CODE(0xa4, GatewayTimeout) /* 5.04 */ \ 40 | COAP_CODE(0xa5, ProxyingNotSupported) /* 5.05 */ 41 | 42 | 43 | /* 44 | * Enum: Code 45 | * 46 | * Enumeration of the request and response codes. 47 | * 48 | * Requests: 49 | * - GET 50 | * - PUT 51 | * - POST 52 | * - DELETE 53 | * 54 | * Responses: 55 | * - Created 56 | * - Deleted 57 | * - Valid 58 | * - Changed 59 | * - Content 60 | * - BadRequest 61 | * - Unauthorized 62 | * - BadOption 63 | * - Forbidden 64 | * - NotFound 65 | * - MethodNotAllowed 66 | * - NotAcceptable 67 | * - PreconditionFailed 68 | * - RequestEntityTooLarge 69 | * - UnsupportedContentFormat 70 | * - InternalServerError 71 | * - NotImplemented 72 | * - BadGateway 73 | * - ServiceUnavailable 74 | * - GatewayTimeout 75 | * - ProxyingNotSupported 76 | * 77 | */ 78 | enum class Code { 79 | #define COAP_CODE(V, N) N = V, 80 | COAP_CODES 81 | #undef COAP_CODE 82 | }; 83 | 84 | 85 | std::ostream& operator<<(std::ostream& os, Code rhs); 86 | 87 | } // namespace CoAP 88 | 89 | #endif // __ResponseCode_h 90 | -------------------------------------------------------------------------------- /coap/include/IMessaging.h: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #pragma once 6 | 7 | #ifndef __IMessaging_h 8 | #define __IMessaging_h 9 | 10 | #include "Client.h" 11 | #include "MClient.h" 12 | 13 | namespace CoAP { 14 | 15 | class RequestHandlers; 16 | 17 | /* 18 | * Class: IMessaging 19 | * 20 | * The core messaging system for the CoAP client and server. 21 | * It can be created with . 22 | */ 23 | class IMessaging { 24 | public: 25 | virtual ~IMessaging() = default; 26 | 27 | /* 28 | * Method: loopOnce 29 | * 30 | * Executes the message processing loop one time. This is to be used in a 31 | * simple event loop if no separate thread shall be spawned. 32 | */ 33 | virtual void loopOnce() = 0; 34 | 35 | /* 36 | * Method: loopStart 37 | * 38 | * Starts a thread that runs the message processing loop until has been called. 39 | */ 40 | virtual void loopStart() = 0; 41 | 42 | /* 43 | * Method: loopStop 44 | * 45 | * Stops the message processing loop and terminates the thread created with . 46 | */ 47 | virtual void loopStop() = 0; 48 | 49 | /* 50 | * Method: requestHandler 51 | * 52 | * Returns: 53 | * The requestHandler for configuration purposes. 54 | */ 55 | virtual RequestHandlers& requestHandler() = 0; 56 | 57 | /* 58 | * Method: getClientFor 59 | * 60 | * Request a client for sending requests to a specific server. 61 | * 62 | * Parameters: 63 | * server - URI of the server (FQDN or IP address) 64 | * server_port - UDP port of the server to connect to 65 | * 66 | * Returns: 67 | * A CoAP for the communication with the server. 68 | */ 69 | virtual Client getClientFor(const char* server, uint16_t server_port = 5683) = 0; 70 | 71 | /* 72 | * Method: getMulticastClient 73 | * 74 | * Returns a client for issuing multicast requests to the specified port. 75 | * 76 | * Parameters: 77 | * server_port - UDP port of the server to connect to 78 | * 79 | * Returns: 80 | * A CoAP client for the communication with the server. 81 | */ 82 | virtual MClient getMulticastClient(uint16_t server_port = 5683) = 0; 83 | }; 84 | 85 | } 86 | 87 | #endif //__IMessaging_h 88 | -------------------------------------------------------------------------------- /coap/include/Logging.h: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #pragma once 6 | 7 | #ifndef __Logging_h 8 | #define __Logging_h 9 | 10 | #include 11 | 12 | enum LogLevel { 13 | LLTRACE = 0, 14 | LLDEBUG = 1, 15 | LLINFO = 2, 16 | LLWARNING = 3, 17 | LLERROR = 4 18 | }; 19 | 20 | #define SETLOGLEVEL(LEVEL) static const LogLevel FILE_LOGLEVEL = LEVEL; 21 | 22 | #define LOG(LEVEL, PREFIX) (LEVEL >= FILE_LOGLEVEL) && std::cout << __FILE__ << ':' << __LINE__ << ' ' << PREFIX << ' ' 23 | 24 | #define TLOG LOG(LLTRACE, "(TRACE)") 25 | #define DLOG LOG(LLDEBUG, "(DEBUG)") 26 | #define ILOG LOG(LLINFO, "(INFO)") 27 | #define WLOG LOG(LLWARNING, "(WARNING)") 28 | #define ELOG LOG(LLERROR, "(ERROR)") 29 | 30 | #endif //__Logging_h 31 | -------------------------------------------------------------------------------- /coap/include/MClient.h: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #pragma once 6 | 7 | #ifndef __MClient_h 8 | #define __MClient_h 9 | 10 | #include "Notifications.h" 11 | 12 | #include 13 | #include 14 | 15 | namespace CoAP { 16 | 17 | class ClientImpl; 18 | 19 | class MClient { 20 | public: 21 | MClient(ClientImpl& impl, uint16_t server_port); 22 | 23 | /** 24 | * Send a nonconfirmable GET request to multicast address 25 | * 26 | * Returns a collection of RestResponses along with their server addresses, the collection will 27 | * be extended with incoming responses over time. When the Responses object is being closed or 28 | * destroyed further incoming responses will be ignored. 29 | */ 30 | std::shared_ptr GET(std::string uri); 31 | 32 | private: 33 | ClientImpl& impl_; 34 | 35 | in_addr_t multicast_ip_; 36 | uint16_t server_port_; 37 | }; 38 | 39 | } // namespace CoAP 40 | 41 | #endif //__MClient_h 42 | -------------------------------------------------------------------------------- /coap/include/Notifications.h: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #pragma once 6 | 7 | #ifndef COAP_NOTIFICATIONS_H 8 | #define COAP_NOTIFICATIONS_H 9 | 10 | #include "Observable.h" 11 | 12 | namespace CoAP { 13 | 14 | class RestResponse; 15 | 16 | using Notifications = Observable; 17 | 18 | } // namespace CoAP 19 | 20 | #endif // COAP_NOTIFICATIONS_H 21 | -------------------------------------------------------------------------------- /coap/include/Observable.h: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #pragma once 6 | 7 | #ifndef COAP_OBSERVABLE_H 8 | #define COAP_OBSERVABLE_H 9 | 10 | #include 11 | 12 | /* 13 | * Class: Observable 14 | * 15 | * An Observable represents a set of future values that are going to be received. 16 | */ 17 | template class Observable { 18 | public: 19 | using Callback = std::function; 20 | 21 | explicit Observable(std::function onDelete = nullptr) : onDelete_(onDelete) { } 22 | 23 | ~Observable() { if (onDelete_) onDelete_(); } 24 | 25 | /* 26 | * Method: onNext 27 | * 28 | * onNext is being called when a new value is available. 29 | * 30 | * Parameters: 31 | * value - The new value 32 | */ 33 | void onNext(const T& value) const { if (callback_) callback_(value); } 34 | 35 | /* 36 | * Method: onClose 37 | * 38 | * onClose is being called when no further values will be available. 39 | */ 40 | void onClose(); 41 | 42 | /* 43 | * Method: onError 44 | * 45 | * onError is being called when the Observable has a problem. For example 46 | * when the Observable could not be instantiated. 47 | */ 48 | void onError(); 49 | 50 | /* 51 | * Method: subscribe 52 | * 53 | * Clients can register an Observer for new values. 54 | * 55 | * Parameters: 56 | * callback - A Callback function that is to be called for each new value. 57 | */ 58 | void subscribe(Callback callback) { callback_ = callback; } 59 | 60 | private: 61 | Callback callback_{nullptr}; 62 | std::function onDelete_; 63 | }; 64 | 65 | #endif // COAP_OBSERVABLE_H 66 | -------------------------------------------------------------------------------- /coap/include/Optional.h: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #pragma once 6 | 7 | #ifndef __Optional_h 8 | #define __Optional_h 9 | 10 | #include 11 | #include 12 | 13 | template 14 | class Optional { 15 | public: 16 | /** 17 | * Creation without value 18 | */ 19 | Optional() = default; 20 | 21 | /** 22 | * Creation with value (copy) 23 | */ 24 | explicit Optional(const T& value) : value_(value), valueSet_(true) {} 25 | 26 | /** 27 | * Creation with value (move) 28 | */ 29 | explicit Optional(T&& value) : value_(std::forward(value)), valueSet_(true) {} 30 | 31 | /** 32 | * Inplace creation of an optional value 33 | */ 34 | template Optional(Args&& ...args) : value_(std::forward(args)...), valueSet_(true) {} 35 | 36 | /** 37 | * Sets the optional value 38 | */ 39 | Optional& operator=(const T& value) { 40 | value_ = value; 41 | valueSet_ = true; 42 | return *this; 43 | } 44 | 45 | /** 46 | * Sets the optional value 47 | */ 48 | Optional& operator=(T&& value) { 49 | value_ = std::forward(value); 50 | valueSet_ = true; 51 | return *this; 52 | } 53 | 54 | /** 55 | * Returns whether the value has been set 56 | */ 57 | operator bool() const { return valueSet_; } 58 | 59 | /** 60 | * Returns the value, if set. 61 | * If the value was not set an exception is thrown. 62 | */ 63 | const T& value() const { 64 | if (valueSet_) 65 | return value_; 66 | else 67 | throw std::runtime_error("Optional value not set"); 68 | } 69 | 70 | /** 71 | * Returns the value, if set. Otherwise it returns the default value from the parameter. 72 | */ 73 | const T& valueOr(const T& v) const { 74 | if (valueSet_) 75 | return value_; 76 | else 77 | return v; 78 | } 79 | 80 | private: 81 | T value_{T()}; 82 | bool valueSet_{false}; 83 | }; 84 | 85 | 86 | /** 87 | * Takes a function that transforms a value of type T in a value of type U and 88 | * returns a function that transforms a value of Optional in to a value of Optional. 89 | */ 90 | template 91 | auto lift(std::function f) { 92 | return [f](Optional const & ot){ 93 | return ot ? Optional(f(ot.value())) 94 | : Optional(); 95 | }; 96 | } 97 | 98 | #endif //__Optional_h 99 | -------------------------------------------------------------------------------- /coap/include/Parameters.h: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #pragma once 6 | 7 | #ifndef __Parameters_h 8 | #define __Parameters_h 9 | 10 | #include 11 | 12 | namespace CoAP { 13 | 14 | const auto ACK_TIMEOUT = std::chrono::milliseconds(1000); 15 | const auto ACK_RANDOM_NUMBER = unsigned(150); // in Percent 16 | const auto MAX_RETRANSMITS = unsigned(4); 17 | const auto NSTART = unsigned(1); 18 | const auto DEFAULT_LEASURE = double(5); 19 | const auto PROBING_RATE = double(1); 20 | 21 | } // namespace CoAP 22 | 23 | #endif //__Parameters_h 24 | -------------------------------------------------------------------------------- /coap/include/Path.h: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #pragma once 6 | 7 | #ifndef __Path_h 8 | #define __Path_h 9 | 10 | #include 11 | #include 12 | 13 | /* 14 | * Class: Path 15 | * 16 | * Representation of the parts of the URI path separated by forward slashes. 17 | */ 18 | class Path { 19 | public: 20 | using Buffer = std::vector; 21 | 22 | /* 23 | * Constructor 24 | * 25 | * Parameters: 26 | * from - String representation of the path. 27 | */ 28 | explicit Path(std::string from); 29 | 30 | /* 31 | * Method: size 32 | * 33 | * Returns: 34 | * The number of parts the path consists of. 35 | */ 36 | size_t size() const; 37 | 38 | /* 39 | * Method: getPart 40 | * 41 | * Returns: 42 | * The n-th part of the path. 43 | * 44 | * Throws: 45 | * std::range_error if n is greater than the number of parts. 46 | */ 47 | std::string getPart(unsigned index) const; 48 | 49 | /* 50 | * Method: toBuffer 51 | * 52 | * Encode the path into a buffer suitable to be sent as part of a CoAP message. 53 | * 54 | * Returns: 55 | * The path as buffer. 56 | */ 57 | Buffer toBuffer() const; 58 | 59 | /* 60 | * Method: fromBuffer 61 | * 62 | * Decode the path from a buffer that was part of a CoAP message. 63 | * 64 | * Parameters: 65 | * buffer - Buffer to be decoded 66 | * 67 | * Returns: 68 | * Decoded path object 69 | */ 70 | static Path fromBuffer(const Buffer& buffer); 71 | 72 | /* 73 | * Method: toString 74 | * 75 | * Returns: 76 | * String representation of the path omitting trailing slashes. 77 | */ 78 | std::string toString() const; 79 | 80 | private: 81 | Path() = default; 82 | 83 | std::string path_; 84 | }; 85 | 86 | #endif // __Path_h 87 | 88 | -------------------------------------------------------------------------------- /coap/include/PathPattern.h: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #pragma once 6 | 7 | #ifndef __PathPattern_h 8 | #define __PathPattern_h 9 | 10 | #include "Path.h" 11 | 12 | #include 13 | 14 | /* 15 | * Class: PathPattern 16 | * 17 | * Pattern to match a variety of paths 18 | * Accepted wildcards are 19 | * ? - accepts one level 20 | * * - accepts any number of levels (must be last level in the pattern) 21 | */ 22 | // Examples: 23 | // /addressbook/?/phone 24 | // /addressbook/smith/* 25 | class PathPattern { 26 | public: 27 | /* 28 | * Constructor 29 | * 30 | * Parameters: 31 | * pattern - Pattern to be matched 32 | */ 33 | explicit PathPattern(Path pattern) : _pattern(pattern) { } 34 | 35 | /* 36 | * Constructor 37 | * 38 | * Parameters: 39 | * pattern - Pattern to be matched 40 | */ 41 | explicit PathPattern(std::string pattern) : _pattern(pattern) { } 42 | 43 | /* 44 | * Method: match 45 | * 46 | * Check if the path matches the given pattern 47 | * 48 | * Returns: 49 | * True if the path matches the pattern 50 | */ 51 | bool match(const Path& path) const; 52 | 53 | private: 54 | Path _pattern; 55 | }; 56 | 57 | 58 | #endif // __PathPattern_h 59 | -------------------------------------------------------------------------------- /coap/include/RequestHandler.h: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #pragma once 6 | 7 | #ifndef __RequestHandler_h 8 | #define __RequestHandler_h 9 | 10 | #include "Notifications.h" 11 | #include "Path.h" 12 | #include "RestResponse.h" 13 | 14 | #include 15 | #include 16 | 17 | namespace CoAP { 18 | 19 | class RequestHandlers; 20 | 21 | class RequestHandler { 22 | public: 23 | explicit RequestHandler(RequestHandlers& parent) : parent_(&parent) { } 24 | 25 | RequestHandler() = default; 26 | RequestHandler(const RequestHandler&) = default; 27 | RequestHandler& operator=(const RequestHandler&) = default; 28 | 29 | CoAP::RestResponse GET(const Path& uri) const { return get_(uri); } 30 | 31 | CoAP::RestResponse PUT(const Path& uri, const std::string& payload) const { return put_(uri, payload); } 32 | 33 | CoAP::RestResponse POST(const Path& uri, const std::string& payload) const { return post_(uri, payload); } 34 | 35 | CoAP::RestResponse DELETE(const Path& uri) const { return delete_(uri); } 36 | 37 | CoAP::RestResponse OBSERVE(const Path& uri, std::weak_ptr notifications) const { return observe_(uri, notifications); } 38 | 39 | bool isGetDelayed() const { return getIsDelayed_; } 40 | 41 | bool isPutDelayed() const { return putIsDelayed_; } 42 | 43 | bool isPostDelayed() const { return postIsDelayed_; } 44 | 45 | bool isDeleteDelayed() const { return deleteIsDelayed_; } 46 | 47 | bool isObserveDelayed() const { return observeIsDelayed_; } 48 | 49 | using GetFunction = std::function; 50 | using PutFunction = std::function; 51 | using PostFunction = std::function; 52 | using DeleteFunction = std::function; 53 | using ObserveFunction = std::function)>; 54 | 55 | RequestHandler& onGet(GetFunction func, bool delayed = false) { 56 | get_ = func; 57 | getIsDelayed_ = delayed; 58 | return *this; 59 | } 60 | 61 | RequestHandler& onPut(PutFunction func, bool delayed = false) { 62 | put_ = func; 63 | putIsDelayed_ = delayed; 64 | return *this; 65 | } 66 | 67 | RequestHandler& onPost(PostFunction func, bool delayed = false) { 68 | post_ = func; 69 | postIsDelayed_ = delayed; 70 | return *this; 71 | } 72 | 73 | RequestHandler& onDelete(DeleteFunction func, bool delayed = false) { 74 | delete_ = func; 75 | deleteIsDelayed_ = delayed; 76 | return *this; 77 | } 78 | 79 | RequestHandler& onObserve(ObserveFunction func, 80 | bool delayed = false) { 81 | observe_ = func; 82 | observeIsDelayed_ = delayed; 83 | return *this; 84 | } 85 | 86 | RequestHandler& onUri(std::string uri); 87 | 88 | private: 89 | GetFunction get_ = [](const Path&){ return CoAP::RestResponse().withCode(CoAP::Code::MethodNotAllowed); }; 90 | PutFunction put_ = [](const Path&, const std::string&){ return CoAP::RestResponse().withCode(CoAP::Code::MethodNotAllowed); }; 91 | PostFunction post_ = [](const Path&, const std::string&){ return CoAP::RestResponse().withCode(CoAP::Code::MethodNotAllowed); }; 92 | DeleteFunction delete_ = [](const Path&){ return CoAP::RestResponse().withCode(CoAP::Code::MethodNotAllowed); }; 93 | ObserveFunction observe_ = [](const Path&, std::weak_ptr){ return CoAP::RestResponse().withCode(CoAP::Code::MethodNotAllowed); }; 94 | 95 | bool getIsDelayed_{false}; 96 | bool putIsDelayed_{false}; 97 | bool postIsDelayed_{false}; 98 | bool deleteIsDelayed_{false}; 99 | bool observeIsDelayed_{false}; 100 | 101 | RequestHandlers* parent_{nullptr}; 102 | }; 103 | 104 | } // namespace CoAP 105 | 106 | #endif // __FluentRequestHandlerLeaf_h 107 | -------------------------------------------------------------------------------- /coap/include/RequestHandlers.h: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #pragma once 6 | 7 | #ifndef __RequestHandlers_h 8 | #define __RequestHandlers_h 9 | 10 | #include "RequestHandler.h" 11 | #include "PathPattern.h" 12 | #include "RestResponse.h" 13 | 14 | #include 15 | 16 | namespace CoAP { 17 | 18 | /** 19 | * Container for storing RequestHandlers and matching them to path patterns. 20 | */ 21 | class RequestHandlers { 22 | public: 23 | /** 24 | * Registers a new RequestHandler for the given path pattern and returns it for configuration like: 25 | * requestHandlers.onUri("/abc").onGet(...) 26 | * 27 | * @param pathPattern Pattern for matching the RequestHandler. 28 | * 29 | * @return The RequestHandler for the specified path pattern. 30 | */ 31 | RequestHandler& onUri(std::string pathPattern); 32 | 33 | /** 34 | * Returns the RequestHandler that matches the given path. 35 | * 36 | * @param path Path to find a RequestHandler for. 37 | * 38 | * @return Pointer to the matching RequestHandler or nullptr if no RequestHandler matches the path. 39 | */ 40 | RequestHandler* getHandler(const Path &path); 41 | 42 | private: 43 | std::vector> requestHandlers_; 44 | }; 45 | 46 | } // namespace CoAP 47 | 48 | #endif // __RequestHandlers_h 49 | -------------------------------------------------------------------------------- /coap/include/RestResponse.h: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #pragma once 6 | 7 | #ifndef __RestResponse_h 8 | #define __RestResponse_h 9 | 10 | #include "Code.h" 11 | 12 | #include 13 | #include 14 | 15 | namespace CoAP { 16 | 17 | /* 18 | * Class: RestResponse 19 | * 20 | * Response from a REST request. 21 | */ 22 | class RestResponse { 23 | public: 24 | /* 25 | * Method: fromIp 26 | * 27 | * Returns: 28 | * The IP address of the server from which the response was received. 29 | */ 30 | in_addr_t fromIp() const { return fromIP_; } 31 | 32 | /* 33 | * Method: withSenderIP 34 | * 35 | * Sets the sender IP address of this response. 36 | * 37 | * Parameters: 38 | * fromIP - Sender IP address 39 | * 40 | * Returns: 41 | * A copy of the response with the sender IP address set. 42 | */ 43 | RestResponse& withSenderIP(in_addr_t fromIP) { 44 | fromIP_ = fromIP; 45 | return *this; 46 | } 47 | 48 | /* 49 | * Method: fromPort 50 | * 51 | * Returns: 52 | * The UDP port of the server from which the response was received. 53 | */ 54 | uint16_t fromPort() const { return fromPort_; } 55 | 56 | /* 57 | * Method: withSenderPort 58 | * 59 | * Sets the sender port of this response. 60 | * 61 | * Parameters: 62 | * fromPort - Sender port 63 | * 64 | * Returns: 65 | * A copy of the response with the sender port set. 66 | */ 67 | RestResponse& withSenderPort(uint16_t fromPort) { 68 | fromPort_ = fromPort; 69 | return *this; 70 | } 71 | 72 | /* 73 | * Method: code 74 | * 75 | * Returns: 76 | * The response . 77 | */ 78 | Code code() const { return code_; } 79 | 80 | /* 81 | * Method: withCode 82 | * 83 | * Sets the response code of this response. 84 | * 85 | * Parameters: 86 | * code - Response code 87 | * 88 | * Returns: 89 | * A copy of the response with the response set. 90 | */ 91 | RestResponse& withCode(Code code) { 92 | code_ = code; 93 | return *this; 94 | } 95 | 96 | /* 97 | * Method: payload 98 | * 99 | * Returns: 100 | * The response payload. 101 | */ 102 | std::string payload() const { return payload_; } 103 | 104 | /* 105 | * Method: withPayload 106 | * 107 | * Sets the payload of this response. 108 | * 109 | * Parameters: 110 | * payload - Payload 111 | * 112 | * Returns: 113 | * A copy of the response with the payload set. 114 | */ 115 | // Learning: In this simple example call by value is faster than call by const ref 116 | RestResponse& withPayload(std::string payload) { 117 | payload_ = payload; 118 | return *this; 119 | } 120 | 121 | /* 122 | * Method: withContentFormat 123 | * 124 | * Sets the content format of this response. 125 | * 126 | * Parameters: 127 | * contentFormat - Content format 128 | * 129 | * Returns: 130 | * A copy of the response with the content format set. 131 | */ 132 | RestResponse withContentFormat(uint8_t contentFormat) { 133 | hasContentFormat_ = true; 134 | contentFormat_ = contentFormat; 135 | return *this; 136 | } 137 | 138 | /* 139 | * Method: hasContentFormat 140 | * 141 | * Returns: 142 | * true if the contentformat was set, otherwise false. 143 | */ 144 | bool hasContentFormat() const { 145 | return hasContentFormat_; 146 | } 147 | 148 | /* 149 | * Method: contentFormat 150 | * 151 | * Returns: 152 | * The the content format if it was set. 153 | * 154 | * See: 155 | * 156 | */ 157 | uint8_t contentFormat() const { 158 | return contentFormat_; 159 | } 160 | private: 161 | Code code_ = Code::NotFound; 162 | std::string payload_; 163 | bool hasContentFormat_{false}; 164 | uint8_t contentFormat_; 165 | in_addr_t fromIP_; 166 | uint16_t fromPort_; 167 | }; 168 | 169 | inline std::ostream& operator<<(std::ostream& os, const RestResponse& r) { 170 | os << r.code(); 171 | return os; 172 | } 173 | 174 | } // namespace CoAP 175 | 176 | #endif // __RestResponse_h 177 | -------------------------------------------------------------------------------- /coap/include/StringHelpers.h: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #pragma once 6 | 7 | #ifndef __StringHelpers_h 8 | #define __StringHelpers_h 9 | 10 | #include 11 | #include 12 | 13 | /** 14 | * Splits a string at the first occurence of the given token. 15 | * 16 | * @param string The string to be split. 17 | * @param token The token where the string will be split. 18 | * @return A pair with the left and the right part of the token. 19 | */ 20 | inline std::pair splitFirst(const std::string& string, char token) { 21 | auto pos = string.find_first_of(token); 22 | return std::make_pair(string.substr(0, pos), 23 | (pos != std::string::npos) ? string.substr(pos+1) 24 | : std::string()); 25 | }; 26 | 27 | /** 28 | * Splits a string at all occurences of the given token. 29 | * 30 | * @param string The string to be split. 31 | * @param token The token where the string will be split. 32 | * @return A vector with all split up parts of the string. 33 | */ 34 | inline std::vector splitAll(std::string string, char token) { 35 | std::vector result; 36 | 37 | auto parts = splitFirst(string, token); 38 | 39 | /* 40 | * "" -> "", "" -> [] 41 | * "abc" -> "abc", "" -> ["abc"] 42 | * ",abc" -> "", "abc" -> ["", "abc"] 43 | * "abc," -> "abc", "" -> ["abc", ""] 44 | * "a,bc" -> "a", "bc" -> ["a", "bc"] 45 | * "," -> "", "" -> ["", ""] 46 | */ 47 | while ( !parts.first.empty() 48 | || string.size()) { 49 | result.emplace_back(std::move(parts.first)); 50 | 51 | if (parts.second.empty() && string.back() == token) { 52 | result.emplace_back(""); 53 | break; 54 | } 55 | 56 | string = std::move(parts.second); 57 | parts = splitFirst(string, token); 58 | } 59 | 60 | return result; 61 | } 62 | 63 | #endif // __StringHelpers_h 64 | -------------------------------------------------------------------------------- /coap/include/URI.h: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #pragma once 6 | 7 | #ifndef __URI_h 8 | #define __URI_h 9 | 10 | #include 11 | #include "Optional.h" 12 | 13 | class URI { 14 | public: 15 | /** 16 | * Parses the given string and creates a URI Object if all required fields are found in the string. 17 | * 18 | * Supported formats are: 19 | * coap[s]://server[:port]/endpoint 20 | * 21 | * If the port is optional and if omitted the default port for coap/coaps will be used. 22 | * 23 | * @param uri Textual representation of the URI. 24 | * @return Either an URI object or nothing if the string is not well formed. 25 | */ 26 | static Optional fromString(const std::string& uri); 27 | 28 | const std::string& getProtocol() const { 29 | return protocol_; 30 | } 31 | 32 | const std::string& getServer() const { 33 | return server_; 34 | } 35 | 36 | uint16_t getPort() const { 37 | return port_; 38 | } 39 | 40 | const std::string& getPath() const { 41 | return path_; 42 | } 43 | 44 | private: 45 | std::string protocol_; 46 | std::string server_; 47 | uint16_t port_{0}; 48 | std::string path_; 49 | }; 50 | 51 | #endif //__URI_h 52 | -------------------------------------------------------------------------------- /coap/include/json.h: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * This is a simple library to convert from C++ to JSON and back again. 7 | * 8 | * It down not support lists of different element types. 9 | * 10 | * Todos: 11 | * - support for null 12 | * - test double value with exponent 13 | */ 14 | 15 | #pragma once 16 | 17 | #ifndef __JSON_h 18 | #define __JSON_h 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | namespace CoAP { 29 | 30 | namespace __internal__ { 31 | /** 32 | * Returns a string without left and right delimiters. 33 | * Throws runtime_error if the string doesn't have the required delimiters. 34 | */ 35 | inline std::string without(char left, char right, const std::string &s) { 36 | if (s.front() != left) throw std::runtime_error(s + " did not start with " + left); 37 | if (s.back() != right) throw std::runtime_error(s + " did not start with " + right); 38 | return s.substr(1, s.length() - 2); 39 | } 40 | 41 | /** 42 | * Same as without() but specialized for curly brackets. 43 | */ 44 | inline std::string withoutCurly(const std::string &s) { 45 | return without('{', '}', s); 46 | } 47 | 48 | /** 49 | * Same as without() but specialized for rectangular brackets. 50 | */ 51 | inline std::string withoutRect(const std::string &s) { 52 | return without('[', ']', s); 53 | } 54 | 55 | /** 56 | * Same as without() but specialized for quotation marks. 57 | */ 58 | inline std::string withoutQuot(const std::string &s) { 59 | return without('\"', '\"', s); 60 | } 61 | 62 | /** 63 | * Returns the position of the first list delimiter. 64 | * Returns std::string::npos if there is no list delimiter. 65 | */ 66 | inline size_t findListDelimiter(const std::string &j) { 67 | auto nesting = 0; 68 | auto index = 0; 69 | for (auto c : j) { 70 | switch (c) { 71 | case '{': 72 | case '[': 73 | ++nesting; 74 | break; 75 | case ']': 76 | case '}': 77 | --nesting; 78 | break; 79 | case ',': 80 | if (nesting == 0) return index; 81 | break; 82 | } 83 | ++index; 84 | } 85 | return std::string::npos; 86 | } 87 | 88 | /** 89 | * Returns the string without leading and trailing spaces. 90 | */ 91 | inline std::string trimmed(const std::string &s) { 92 | auto start = s.find_first_not_of(' '); 93 | if (start == std::string::npos) return ""; 94 | auto end = s.find_last_not_of(' '); 95 | auto count = end != std::string::npos ? (end - start + 1) : end; 96 | return s.substr(start, count); 97 | } 98 | 99 | } // namespace __internal__ 100 | 101 | // Object trampoline 102 | 103 | /** 104 | * Returns the JSON representation of the objects. 105 | */ 106 | template 107 | std::string to_json(const T& object) { 108 | return "{" + object.to_json() + "}"; 109 | } 110 | 111 | /** 112 | * Initializes the object from the JSON string. 113 | */ 114 | template 115 | void from_json(const std::string& json, T& object) { 116 | object.from_json(__internal__::trimmed(__internal__::withoutCurly(__internal__::trimmed(json)))); 117 | } 118 | 119 | // Boolean 120 | 121 | /** 122 | * Returns the JSON representation of the boolean value 123 | */ 124 | inline std::string to_json(bool value) { 125 | return value ? "true" : "false"; 126 | } 127 | 128 | inline void from_json(const std::string& json, bool& value) { 129 | value = json == "true"; 130 | } 131 | 132 | // Numbers 133 | 134 | /** 135 | * Returns the JSON representation of the value. 136 | */ 137 | inline std::string to_json(int value) { 138 | return std::to_string(value); 139 | } 140 | 141 | /** 142 | * Initializes the value from the JSON string. 143 | */ 144 | inline void from_json(const std::string& json, int& value) { 145 | value = std::stoi(__internal__::trimmed(json)); 146 | } 147 | 148 | /** 149 | * Returns the JSON representation of the value. 150 | */ 151 | inline std::string to_json(unsigned int value) { 152 | return std::to_string(value); 153 | } 154 | 155 | /** 156 | * Initializes the value from the JSON string. 157 | */ 158 | inline void from_json(const std::string& json, unsigned int& value) { 159 | value = std::stoul(__internal__::trimmed(json)); 160 | } 161 | 162 | /** 163 | * Returns the JSON representation of the value. 164 | */ 165 | inline std::string to_json(double value) { 166 | auto r = std::to_string(value); 167 | auto dotPos = r.find_first_of('.'); 168 | auto lastNonZeroPos = r.find_last_not_of('0'); 169 | if (dotPos != std::string::npos) r = r.substr(0, lastNonZeroPos+1); 170 | return r; 171 | } 172 | 173 | /** 174 | * Initializes the value from the JSON string. 175 | */ 176 | inline void from_json(const std::string& json, double& value) { 177 | value = std::stod(__internal__::trimmed(json)); 178 | } 179 | 180 | // Strings 181 | 182 | /** 183 | * Returns the JSON representation of the string. 184 | */ 185 | inline std::string to_json(const char* str) { 186 | return std::string("\"") + str + '\"'; 187 | } 188 | 189 | 190 | /** 191 | * Returns the JSON representation of the string. 192 | */ 193 | inline std::string to_json(std::string str) { 194 | return '\"' + str + '\"'; 195 | } 196 | 197 | /** 198 | * Initializes the string from the JSON string. 199 | */ 200 | inline void from_json(const std::string& json, std::string& str) { 201 | str = __internal__::withoutQuot(__internal__::trimmed(json)); 202 | } 203 | 204 | // Lists 205 | 206 | /** 207 | * Returns the JSON representation of the list. 208 | */ 209 | template 210 | std::string to_json(const std::list& valueList) { 211 | std::string s; 212 | if (!valueList.empty()) { 213 | std::stringstream ss; 214 | std::list ls; 215 | std::transform(valueList.begin(), 216 | valueList.end(), 217 | std::back_insert_iterator>(ls), 218 | [](const T& t) { return to_json(t); }); 219 | 220 | std::copy(ls.begin(), ls.end(), std::ostream_iterator(ss, ",")); 221 | s = ss.str(); 222 | s = s.substr(0, s.size() - 1); 223 | } 224 | return "[" + s + ']'; 225 | } 226 | 227 | /** 228 | * Initializes the list from the JSON string. 229 | */ 230 | template 231 | void from_json(const std::string& json, std::list& valueList) { 232 | valueList.clear(); 233 | 234 | struct Helper { 235 | void operator() (Helper& next, const std::string& json, std::list& valueList) { 236 | if (json.empty()) return; 237 | auto pos = __internal__::findListDelimiter(json); 238 | auto element = __internal__::trimmed(json.substr(0, pos)); 239 | T result; 240 | from_json(element, result); 241 | valueList.push_back(result); 242 | if (pos != std::string::npos) next(next, json.substr(pos + 1), valueList); 243 | } 244 | } helper; 245 | 246 | auto elements = __internal__::trimmed(__internal__::withoutRect(__internal__::trimmed(json))); 247 | if (elements.size()) helper(helper, elements, valueList); 248 | } 249 | 250 | // Maps/Dictionaries 251 | 252 | /** 253 | * Returns the JSON representation of the map. 254 | */ 255 | template 256 | std::string to_json(const std::map& valueMap) { 257 | std::string s; 258 | 259 | for (auto& e : valueMap) { 260 | s += to_json(e.first) + ":" + to_json(e.second) + ","; 261 | } 262 | 263 | s = s.empty() ? "" : s.substr(0, s.size() - 1); 264 | 265 | return "{" + s + "}"; 266 | } 267 | 268 | /** 269 | * Initializes a map with the key from the JSON string. 270 | */ 271 | template 272 | void from_json(const std::string& json, std::map& result) { 273 | 274 | struct Helper { 275 | void operator()(Helper& next, const std::string& json, std::map& result) { 276 | auto commaPos = __internal__::findListDelimiter(json); 277 | auto element = json.substr(0, commaPos); 278 | auto colonPos = element.find_first_of(':'); 279 | if (colonPos != std::string::npos) { 280 | std::string k; 281 | from_json(__internal__::trimmed(element.substr(0, colonPos)), k); 282 | T v; 283 | from_json(__internal__::trimmed(element.substr(colonPos + 1)), v); 284 | result.emplace(k, v); 285 | } 286 | if (commaPos != std::string::npos) next(next, json.substr(commaPos + 1), result); 287 | } 288 | } helper; 289 | 290 | if (json.size()) helper(helper, __internal__::withoutCurly(json), result); 291 | } 292 | 293 | // Objects 294 | 295 | /** 296 | * Returns the JSON representation of the object value with the key. 297 | */ 298 | template 299 | std::string to_json(const std::string& key, T value) { 300 | return to_json(key) + ":" + to_json(value); 301 | } 302 | 303 | /** 304 | * Initializes the object value with the key from the JSON string. 305 | */ 306 | template 307 | void from_json(const std::string& json, const std::string& key, T& value) { 308 | 309 | struct Helper { 310 | void operator() (Helper& next, const std::string& json, const std::string& key, T& value) { 311 | if (json.empty()) return; 312 | auto commaPos = __internal__::findListDelimiter(json); 313 | auto element = json.substr(0, commaPos); 314 | auto colonPos = element.find_first_of(':'); 315 | if (colonPos != std::string::npos) { 316 | auto k = __internal__::trimmed(element.substr(0, colonPos)); 317 | if (k == key) { 318 | auto v = __internal__::trimmed(element.substr(colonPos+1)); 319 | from_json(v, value); 320 | return; 321 | } 322 | } 323 | if (commaPos != std::string::npos) next(next, json.substr(commaPos + 1), key, value); 324 | } 325 | } helper; 326 | 327 | if (json.size()) helper(helper, json, to_json(key), value); 328 | } 329 | 330 | } // namespace CoAP 331 | 332 | #endif // __JSON_h 333 | -------------------------------------------------------------------------------- /coap/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.7) 2 | project(CoaP) 3 | 4 | file(GLOB_RECURSE HEADERS ../include/*.h) 5 | file(GLOB_RECURSE SRCS *.cpp *.h) 6 | 7 | include_directories(../include) 8 | 9 | add_library(coap SHARED ${SRCS} ${HEADERS}) 10 | 11 | target_link_libraries(coap pthread) 12 | 13 | install(TARGETS coap LIBRARY DESTINATION lib) 14 | 15 | install(FILES ${HEADERS} DESTINATION include/coap) 16 | -------------------------------------------------------------------------------- /coap/src/Client.cpp: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #include "Client.h" 6 | 7 | #include "Message.h" 8 | #include "ClientImpl.h" 9 | 10 | SETLOGLEVEL(LLWARNING); 11 | 12 | namespace CoAP { 13 | 14 | Client::Client(ClientImpl& impl, std::string server, uint16_t server_port) 15 | : impl_(impl) 16 | , server_ip_(NetUtils().ipFromHostname(server)) 17 | , server_port_(server_port) 18 | { 19 | } 20 | 21 | std::future Client::GET(std::string uri, bool confirmable) { 22 | return asFuture(impl_.GET(server_ip_, server_port_, uri, confirmable ? CoAP::Type::Confirmable : CoAP::Type::NonConfirmable)); 23 | } 24 | 25 | std::future Client::PUT(std::string uri, std::string payload, bool confirmable) { 26 | return asFuture(impl_.PUT(server_ip_, server_port_, uri, payload, confirmable ? CoAP::Type::Confirmable : CoAP::Type::NonConfirmable)); 27 | } 28 | 29 | std::future Client::POST(std::string uri, std::string payload, bool confirmable) { 30 | return asFuture(impl_.POST(server_ip_, server_port_, uri, payload, confirmable ? CoAP::Type::Confirmable : CoAP::Type::NonConfirmable)); 31 | } 32 | 33 | std::future Client::DELETE(std::string uri, bool confirmable) { 34 | return asFuture(impl_.DELETE(server_ip_, server_port_, uri, confirmable ? CoAP::Type::Confirmable : CoAP::Type::NonConfirmable)); 35 | } 36 | 37 | std::future Client::PING() { 38 | return asFuture(impl_.PING(server_ip_, server_port_)); 39 | } 40 | 41 | std::shared_ptr Client::OBSERVE(std::string uri, bool confirmable) { 42 | return impl_.OBSERVE(server_ip_, server_port_, uri, confirmable ? CoAP::Type::Confirmable : CoAP::Type::NonConfirmable); 43 | } 44 | 45 | std::future Client::asFuture(const std::shared_ptr& responses) { 46 | auto id = ++id_; 47 | auto p = promises_.emplace(std::make_pair(id, std::make_pair(std::promise(), responses))); 48 | 49 | // TODO: The callback must be registered along with the request that we don't miss a response due to race conditions 50 | p.first->second.second->subscribe([this, id](const RestResponse& r){ 51 | auto it = this->promises_.find(id); 52 | if (this->promises_.end() == it) { 53 | ELOG << "Received unexpected response (" << r << ")\n"; 54 | return; 55 | } 56 | it->second.first.set_value(r); 57 | this->promises_.erase(it); 58 | }); 59 | 60 | auto & promise = p.first->second.first; 61 | return promise.get_future(); 62 | } 63 | } // namespace CoAP 64 | -------------------------------------------------------------------------------- /coap/src/ClientImpl.cpp: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #include "ClientImpl.h" 6 | 7 | #include "Messaging.h" 8 | 9 | SETLOGLEVEL(LLWARNING); 10 | 11 | namespace CoAP { 12 | 13 | ClientImpl::~ClientImpl() { 14 | if (not notifications_.empty()) ELOG << "ClientImpl::notifications_ is not empty\n"; 15 | } 16 | 17 | void ClientImpl::onMessage(const Message& msg_received, in_addr_t fromIP, uint16_t fromPort) { 18 | ILOG << "onMessage(): Message(" << msg_received 19 | << " payload=" << msg_received.payload().length() << " bytes)\n"; 20 | 21 | std::lock_guard lock(mutex_); 22 | 23 | auto notificationIt = notifications_.find(msg_received.token()); 24 | 25 | if (notificationIt == notifications_.end()) { 26 | if (msg_received.optionalObserveValue()) { 27 | // TODO: Send reset message to stop server from sending further notifications 28 | } 29 | return; 30 | } 31 | 32 | auto sp = notificationIt->second.lock(); 33 | if (not sp) { 34 | if (msg_received.optionalObserveValue()) { 35 | // TODO: Send reset message to stop server from sending further notifications 36 | } 37 | return; 38 | } 39 | 40 | sp->onNext(RestResponse() 41 | .withSenderIP(fromIP) 42 | .withSenderPort(fromPort) 43 | .withCode(msg_received.code()) 44 | .withPayload(msg_received.payload())); 45 | } 46 | 47 | std::shared_ptr ClientImpl::GET(in_addr_t ip, uint16_t port, std::string uri, Type type) { 48 | ILOG << "Sending " << ((type == Type::Confirmable) ? "confirmable " : "") << "GET request with URI=" << uri << '\n'; 49 | return sendRequest(ip, port, Message(type, messageId_++, CoAP::Code::GET, newToken(), uri)); 50 | } 51 | 52 | std::shared_ptr ClientImpl::PUT(in_addr_t ip, uint16_t port, std::string uri, std::string payload, Type type) { 53 | ILOG << "Sending " << ((type == Type::Confirmable) ? "confirmable " : "") << "PUT request with URI=" << uri << '\n'; 54 | return sendRequest(ip, port, Message(type, messageId_++, CoAP::Code::PUT, newToken(), uri, payload)); 55 | } 56 | 57 | std::shared_ptr ClientImpl::POST(in_addr_t ip, uint16_t port, std::string uri, std::string payload, Type type) { 58 | ILOG << "Sending " << ((type == Type::Confirmable) ? "confirmable " : "") << "POST request with URI=" << uri << '\n'; 59 | return sendRequest(ip, port, Message(type, messageId_++, CoAP::Code::POST, newToken(), uri, payload)); 60 | } 61 | 62 | std::shared_ptr ClientImpl::DELETE(in_addr_t ip, uint16_t port, std::string uri, Type type) { 63 | ILOG << "Sending " << ((type == Type::Confirmable) ? "confirmable " : "") << "DELETE request with URI=" << uri << '\n'; 64 | return sendRequest(ip, port, Message(type, messageId_++, CoAP::Code::DELETE, newToken(), uri)); 65 | } 66 | 67 | std::shared_ptr ClientImpl::PING(in_addr_t ip, uint16_t port) { 68 | ILOG << "Sending ping request to the server\n"; 69 | return sendRequest(ip, port, Message(Type::Confirmable, messageId_++, Code::Empty, newToken(), "")); 70 | } 71 | 72 | std::shared_ptr ClientImpl::OBSERVE(in_addr_t ip, uint16_t port, std::string uri, Type type) { 73 | ILOG << "Sending " << ((type == Type::Confirmable) ? "confirmable " : "") << "OBSERVATION request with URI=" << uri << '\n'; 74 | return sendObservation(ip, port, Message(type, messageId_++, CoAP::Code::GET, newToken(), uri)); 75 | } 76 | 77 | std::shared_ptr ClientImpl::sendRequest(in_addr_t ip, uint16_t port, Message msg) { 78 | std::lock_guard lock(mutex_); 79 | 80 | auto token = msg.token(); 81 | auto notifications = std::make_shared([this, token](){ 82 | this->notifications_.erase(token); 83 | }); 84 | auto x = notifications_.emplace(token, notifications); 85 | // If there is already a request with the given token, we cannot send the request. 86 | if (not x.second) throw std::runtime_error("Sending request with already used token failed!"); 87 | 88 | DLOG << "Sending " << ((msg.type() == Type::Confirmable) ? "confirmable " : "") << "message with msgID=" 89 | << msg.messageId() << '\n'; 90 | messaging_.sendMessage(ip, port, std::move(msg)); 91 | return notifications; 92 | } 93 | 94 | std::shared_ptr ClientImpl::sendObservation(in_addr_t ip, uint16_t port, Message msg) { 95 | std::lock_guard lock(mutex_); 96 | 97 | auto token = msg.token(); 98 | auto notifications = std::make_shared([this, ip, port, msg, token](){ 99 | this->notifications_.erase(token); 100 | auto unobserve = msg; 101 | this->messaging_.sendMessage(ip, port, unobserve.withObserveValue(1)); 102 | }); 103 | auto x = notifications_.emplace(token, notifications); 104 | // If there is already a request with the given token, we cannot send the request. 105 | if (not x.second) throw std::runtime_error("Sending request with already used token failed!"); 106 | 107 | DLOG << "Sending " << ((msg.type() == Type::Confirmable) ? "confirmable " : "") << "message with msgID=" 108 | << msg.messageId() << '\n'; 109 | messaging_.sendMessage(ip, port, std::move(msg.withObserveValue(0))); 110 | return notifications; 111 | } 112 | 113 | } // namespace CoAP -------------------------------------------------------------------------------- /coap/src/ClientImpl.h: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #pragma once 6 | 7 | #ifndef __ClientImpl_h 8 | #define __ClientImpl_h 9 | 10 | #include "IConnection.h" 11 | #include "Logging.h" 12 | #include "Message.h" 13 | #include "NetUtils.h" 14 | #include "Notifications.h" 15 | #include "RestResponse.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | 24 | namespace CoAP { 25 | 26 | class Messaging; 27 | 28 | class ClientImpl { 29 | public: 30 | ClientImpl(Messaging& messaging) 31 | : messaging_(messaging) { 32 | } 33 | 34 | ~ClientImpl(); 35 | 36 | void onMessage(const Message& msg_received, in_addr_t fromIP, uint16_t fromPort); 37 | 38 | std::shared_ptr> GET(in_addr_t ip, uint16_t port, std::string uri, Type type); 39 | 40 | std::shared_ptr> PUT(in_addr_t ip, uint16_t port, std::string uri, std::string payload, Type type); 41 | 42 | std::shared_ptr> POST(in_addr_t ip, uint16_t port, std::string uri, std::string payload, Type type); 43 | 44 | std::shared_ptr> DELETE(in_addr_t ip, uint16_t port, std::string uri, Type type); 45 | 46 | std::shared_ptr> PING(in_addr_t ip, uint16_t port); 47 | 48 | std::shared_ptr> OBSERVE(in_addr_t ip, uint16_t port, std::string uri, Type type); 49 | 50 | private: 51 | 52 | uint64_t newToken() { 53 | // TODO: According to the RFC the token shall be randomized (at least 32-bit for internet traffic) 54 | // TODO: The length can be adapted based on the detection of attacks through reception of wrong tokens. 55 | return token_++; 56 | } 57 | 58 | /** 59 | * Sends a request and sets up the mechanism to relate responses back to the request. 60 | * 61 | * @return Shared pointer to the observable with the notifications. When the shared 62 | * pointer gets released the Interest in the notifications vanishes. 63 | */ 64 | std::shared_ptr sendRequest(in_addr_t ip, uint16_t port, Message msg); 65 | std::shared_ptr sendObservation(in_addr_t ip, uint16_t port, Message msg); 66 | 67 | // Continuously increasing message id for messages sent by this client. 68 | uint16_t messageId_{0}; 69 | 70 | uint64_t token_{0}; 71 | 72 | // Protection of the notifications container 73 | std::mutex mutex_; 74 | 75 | std::map>> notifications_; 76 | 77 | Messaging& messaging_; 78 | }; 79 | 80 | } // namespace CoAP; 81 | 82 | #endif // __ClientImpl_h 83 | -------------------------------------------------------------------------------- /coap/src/CoAP.cpp: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #include "CoAP.h" 6 | 7 | #include "Messaging.h" 8 | 9 | namespace CoAP { 10 | 11 | std::unique_ptr newMessaging(uint16_t port) { 12 | return std::unique_ptr(new Messaging(port)); 13 | } 14 | 15 | } // namespace CoAP -------------------------------------------------------------------------------- /coap/src/Code.cpp: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #include "Code.h" 6 | 7 | #include 8 | 9 | namespace CoAP { 10 | 11 | 12 | std::string toString(Code code) { 13 | switch (code) { 14 | #define COAP_CODE(V, N) case Code::N: return #N; 15 | COAP_CODES 16 | #undef COAP_CODE 17 | } 18 | return ""; 19 | } 20 | 21 | 22 | std::ostream& operator<<(std::ostream& os, Code rhs) { 23 | const auto code = 100 * (static_cast(rhs) >> 5) + 24 | (static_cast(rhs) & 0x1F); 25 | os << code << "-" << toString(rhs); 26 | return os; 27 | } 28 | 29 | } // namespace CoAP 30 | -------------------------------------------------------------------------------- /coap/src/Connection.cpp: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #include "Connection.h" 6 | #include "NetUtils.h" 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | namespace CoAP { 13 | 14 | // Multicast "All CoAP Nodes" Address 15 | // for IPv4: 224.0.1.187 16 | // for IPv6: link-local scoped address ff02::fd and the site-local scoped address ff05::fd 17 | void Connection::open(uint16_t port) { 18 | if (socket_ != 0) throw std::logic_error("Cannot open already open connection."); 19 | 20 | socket_ = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); 21 | if (socket_ == 0) throw std::runtime_error("Socket creation failed."); 22 | 23 | auto constexpr WITH_MULTICAST = false; 24 | if (WITH_MULTICAST) { 25 | // enable multicast messages to be sent back to the local host 26 | u_char loop = 1; 27 | if (-1 == setsockopt(socket_, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop))) { 28 | close(); 29 | throw std::runtime_error("Setting multicast loopback on socket failed."); 30 | } 31 | 32 | // joining a multicast group 33 | ip_mreq mreq; 34 | mreq.imr_interface.s_addr = INADDR_ANY; 35 | mreq.imr_multiaddr.s_addr = inet_addr("224.0.1.187"); 36 | if (-1 == setsockopt(socket_, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq))) { 37 | close(); 38 | throw std::runtime_error("Setting multicast membership on socket failed."); 39 | } 40 | } 41 | 42 | struct sockaddr_in sa; 43 | memset(&sa, 0, sizeof(sa)); 44 | sa.sin_family = AF_INET; 45 | sa.sin_addr.s_addr = htonl(INADDR_ANY); 46 | sa.sin_port = htons(port); 47 | 48 | if (-1 == bind(socket_, (struct sockaddr*) &sa, sizeof(sa))) { 49 | close(); 50 | throw std::runtime_error("bind failed.") ; 51 | } 52 | } 53 | 54 | void Connection::close() { 55 | ::close(socket_); 56 | socket_ = 0; 57 | } 58 | 59 | Optional Connection::get(std::chrono::milliseconds timeout) { 60 | if (socket_ == 0) throw std::logic_error("Cannot receive if connection was not opened before."); 61 | 62 | setReceiveTimeout(timeout); 63 | 64 | struct sockaddr_in sa; 65 | socklen_t fromlen = sizeof(sa); 66 | memset(&sa, 0, sizeof(sa)); 67 | ssize_t bytesReceived = recvfrom(socket_, buffer_, bufferSize_, 0, (struct sockaddr*) &sa, &fromlen); 68 | 69 | if (bytesReceived < 0) 70 | if (errno == EAGAIN) return Optional(); 71 | else throw std::runtime_error("Receiving telegram failed."); 72 | else 73 | return Optional(sa.sin_addr.s_addr, ntohs(sa.sin_port), std::vector(buffer_, buffer_+bytesReceived)); 74 | } 75 | 76 | void Connection::send(Telegram&& telegram) { 77 | if (socket_ == 0) throw std::logic_error("Cannot send if connection was not opened before."); 78 | 79 | struct sockaddr_in sa; 80 | memset(&sa, 0, sizeof(sa)); 81 | sa.sin_family = AF_INET; 82 | sa.sin_addr.s_addr = telegram.getIP(); 83 | sa.sin_port = htons(telegram.getPort()); 84 | 85 | // TODO: How can we replace this temporary copy, e.g. by a move or reference 86 | auto msg = telegram.getMessage(); 87 | auto bytes_sent = sendto(socket_, msg.data(), msg.size(), 0, (struct sockaddr*) &sa, sizeof(sa)); 88 | if (bytes_sent < 0) throw std::runtime_error("Sending telegram failed."); 89 | } 90 | 91 | void Connection::setReceiveTimeout(std::chrono::milliseconds timeout) { 92 | timeval tv; 93 | tv.tv_sec = timeout.count() / 1000; 94 | tv.tv_usec = (timeout.count() % 1000) * 1000; 95 | 96 | if (-1 == setsockopt(socket_, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) { 97 | close(); 98 | throw std::runtime_error("Setting receive timeout on socket failed."); 99 | } 100 | } 101 | 102 | int Connection::socket(int domain, int type, int protocol) const { 103 | return ::socket(domain, type, protocol); 104 | } 105 | 106 | int Connection::setsockopt(int socket, int level, int option_name, 107 | const void* option_value, socklen_t option_len) const { 108 | return ::setsockopt(socket, level, option_name, option_value, option_len); 109 | } 110 | 111 | int Connection::bind(int socket, const struct sockaddr* address, socklen_t address_len) const { 112 | return ::bind(socket, address, address_len); 113 | } 114 | } // namespace CoAP 115 | -------------------------------------------------------------------------------- /coap/src/Connection.h: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #pragma once 6 | 7 | #ifndef __Connection_h 8 | #define __Connection_h 9 | 10 | #include "Telegram.h" 11 | #include "IConnection.h" 12 | 13 | #include 14 | 15 | struct hostent; 16 | 17 | namespace CoAP { 18 | 19 | class Connection : public IConnection { 20 | public: 21 | Connection() = default; 22 | 23 | virtual ~Connection() = default; 24 | 25 | /** 26 | * Opens a connection on the given port. 27 | * 28 | * @param port Number of the port to open for incoming and outgoing telegrams 29 | * 30 | * @throws std::logic_error when open was already called before 31 | * @throws std::runtime_error when the connection could not be opened 32 | */ 33 | void open(uint16_t port); 34 | 35 | /** 36 | * Closes the socket and resets the internal state of this object. 37 | */ 38 | void close(); 39 | 40 | void send(Telegram&& telegram) override; 41 | 42 | Optional get(std::chrono::milliseconds timeout) override; 43 | 44 | protected: 45 | // sets the receive timeout on the socket 46 | virtual void setReceiveTimeout(std::chrono::milliseconds timeout); 47 | 48 | // trampoline for unit test to override system call socket 49 | virtual int socket(int domain, int type, int protocol) const; 50 | 51 | // trampoline for unit test to override system call bind 52 | virtual int bind(int socket, const struct sockaddr* address, socklen_t address_len) const; 53 | 54 | // trampoline for unit test to override system call setsockopt 55 | virtual int setsockopt(int socket, int level, int option_name, const void* option_value, socklen_t option_len) const; 56 | 57 | private: 58 | int socket_{0}; 59 | static constexpr int bufferSize_{2048}; 60 | uint8_t buffer_[bufferSize_]; 61 | }; 62 | 63 | } // namespace CoAP 64 | 65 | 66 | #endif // __Connection_h 67 | -------------------------------------------------------------------------------- /coap/src/IConnection.h: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #pragma once 6 | 7 | #ifndef __IConnection_h 8 | #define __IConnection_h 9 | 10 | #include "Optional.h" 11 | #include "Telegram.h" 12 | 13 | #include 14 | 15 | namespace CoAP { 16 | 17 | class IConnection { 18 | public: 19 | virtual ~IConnection() = default; 20 | 21 | /** 22 | * Sends the telegram. 23 | * 24 | * @param telegram Telegram to send 25 | * 26 | * @throws std::logic_error when the connection is not open 27 | * @throws std::runtime_error when sending the telegram failed 28 | */ 29 | virtual void send(Telegram&& telegram) = 0; 30 | 31 | /** 32 | * Waits for and reads a telegram from the network. 33 | * 34 | * @param timeout Time to wait for a telegram before returning nothing 35 | * 36 | * @return Either the telegram or nothing if no telegram was received before the timeout.. 37 | * 38 | * @throws std::logic_error when the connection is not open 39 | */ 40 | virtual Optional get(std::chrono::milliseconds timeout) = 0; 41 | }; 42 | 43 | } 44 | 45 | 46 | #endif //__IConnection_h 47 | -------------------------------------------------------------------------------- /coap/src/MClient.cpp: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #include "MClient.h" 6 | 7 | #include "ClientImpl.h" 8 | 9 | namespace CoAP { 10 | 11 | MClient::MClient(ClientImpl &impl, uint16_t server_port) 12 | : impl_(impl), multicast_ip_(htonl(0xE00001BB)) // "224.0.1.187" 13 | , server_port_(server_port) { 14 | } 15 | 16 | std::shared_ptr MClient::GET(std::string uri) { 17 | return impl_.GET(multicast_ip_, server_port_, uri, Type::NonConfirmable); 18 | } 19 | 20 | } // CoAP -------------------------------------------------------------------------------- /coap/src/Message.cpp: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #include "Message.h" 6 | 7 | #include "Logging.h" 8 | #include "Optional.h" 9 | #include "Path.h" 10 | #include "StringHelpers.h" 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | SETLOGLEVEL(LLWARNING) 17 | 18 | namespace CoAP { 19 | 20 | Message::Message(Type type, MessageId messageId, Code code, uint64_t token, std::string path, std::string payload) 21 | : type_(type) 22 | , messageId_(messageId) 23 | , token_(token) 24 | , code_(code) 25 | , payload_(std::move(payload)) 26 | { 27 | auto parts = splitFirst(path, '?'); 28 | path_ = std::move(parts.first); 29 | queries_ = splitAll(parts.second, '&'); 30 | } 31 | 32 | void appendUnsigned(Message::Buffer& buffer, uint64_t value, unsigned length) { 33 | while (length-- > 0) { 34 | uint8_t byte = (value >> (length * 8)) & 0xff; 35 | buffer.emplace_back(byte); 36 | } 37 | } 38 | 39 | Message::Buffer Message::asBuffer() const { 40 | Buffer buffer; 41 | buffer.reserve(256); 42 | 43 | // 0 1 2 3 44 | // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 45 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 46 | // |Ver| T | TKL | Code | Message ID | 47 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 48 | // | Token (if any, TKL bytes) ... 49 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 50 | // | Options (if any) ... 51 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 52 | // |1 1 1 1 1 1 1 1| Payload (if any) ... 53 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 54 | 55 | // Message header (4 Byte) 56 | auto token_length = tokenLength(token_); 57 | buffer.emplace_back(0x40 | (static_cast(type_) << 4) | token_length); 58 | buffer.emplace_back(static_cast(code_)); 59 | buffer.emplace_back(static_cast((messageId_ >> 8) & 0xff)); // message id high byte 60 | buffer.emplace_back(static_cast(messageId_ & 0xff)); // message id low byte 61 | 62 | // Token (optional) 63 | appendUnsigned(buffer, token_, token_length); 64 | 65 | // Options (optional) 66 | int option = 0; 67 | 68 | // Option: Observe 69 | if (observeValue_) { 70 | const int option_offset = Observe - option; 71 | option += option_offset; 72 | 73 | unsigned int length = tokenLength(observeValue_.value()); 74 | 75 | auto optionHeader = makeOptionHeader(option_offset, length); 76 | std::copy(begin(optionHeader), end(optionHeader), std::back_inserter(buffer)); 77 | 78 | appendUnsigned(buffer, observeValue_.value(), length); 79 | } 80 | 81 | // Option: Uri-Path 82 | auto path = Path(path_); 83 | for (size_t i = 0U; i < path.size(); ++i) { 84 | const int option_offset = UriPath - option; 85 | option += option_offset; 86 | 87 | auto part = path.getPart(i); 88 | auto optionHeader = makeOptionHeader(option_offset, part.length()); 89 | std::copy(begin(optionHeader), end(optionHeader), std::back_inserter(buffer)); 90 | std::copy(begin(part), end(part), std::back_inserter(buffer)); 91 | } 92 | 93 | // Option: Content-Format 94 | if (contentFormat_) { 95 | const int option_offset = ContentFormat - option; 96 | option += option_offset; 97 | 98 | unsigned int length = tokenLength(contentFormat_.value()); 99 | 100 | auto optionHeader = makeOptionHeader(option_offset, length); 101 | std::copy(begin(optionHeader), end(optionHeader), std::back_inserter(buffer)); 102 | 103 | appendUnsigned(buffer, contentFormat_.value(), length); 104 | } 105 | 106 | // Option: Uri-Query 107 | for (auto query : queries_) { 108 | const int option_offset = UriQuery - option; 109 | option += option_offset; 110 | 111 | auto optionHeader = makeOptionHeader(option_offset, query.length()); 112 | std::copy(begin(optionHeader), end(optionHeader), std::back_inserter(buffer)); 113 | std::copy(begin(query), end(query), std::back_inserter(buffer)); 114 | } 115 | 116 | // Payload (optional) 117 | if (payload_.size()) { 118 | // Payload marker (only if payload > 0 bytes) 119 | buffer.push_back(0xff); 120 | 121 | std::copy(begin(payload_), end(payload_), std::back_inserter(buffer)); 122 | } 123 | 124 | return buffer; 125 | } 126 | 127 | namespace { 128 | 129 | std::tuple parse1stByte(uint8_t byte) { 130 | int version = ((byte >> 6) & 0x3); 131 | // Accept only version 1 132 | if (version != 0x1) throw std::exception(); 133 | 134 | Type type = static_cast((byte >> 4) & 0x3); 135 | 136 | int token_length = byte & 0x0f; 137 | // Token is 0 to 8 bytes long 138 | if (token_length > 8) throw std::exception(); 139 | 140 | return std::make_tuple(type, token_length); 141 | }; 142 | 143 | template 144 | T parseUnsigned(std::vector::const_iterator &it, std::vector::const_iterator &end, unsigned length) { 145 | T value = 0; 146 | while (length-- > 0) { 147 | if (it == end) { 148 | WLOG << "Parsing exceeded buffer\n"; 149 | throw std::exception(); 150 | } 151 | value <<= 8; 152 | value += *it++; 153 | } 154 | return value; 155 | } 156 | 157 | } // namespace 158 | 159 | Message Message::fromBuffer(const std::vector& buffer) { 160 | auto it = begin(buffer); 161 | auto endOfBuffer = end(buffer); 162 | 163 | // Valid messages have at least 4 bytes 164 | if (buffer.size() < 4) throw std::exception(); 165 | 166 | Type type; 167 | int token_length; 168 | std::tie(type, token_length) = parse1stByte(*it++); 169 | 170 | // second byte - code 171 | auto code = static_cast(*it++); 172 | 173 | // third & forth bytes - message ID 174 | auto msgId = parseUnsigned(it, endOfBuffer, 2); 175 | 176 | // read the token 177 | if (token_length > 8) { 178 | WLOG << "Received token_length " << token_length << " > 8 bytes.\n"; 179 | throw std::exception(); 180 | } 181 | auto token = parseUnsigned(it, endOfBuffer, token_length); 182 | 183 | // read options 184 | auto option = EmptyOption; 185 | unsigned length{0}; 186 | unsigned consumed_bytes{0}; 187 | Optional contentFormat; 188 | Optional observeValue; 189 | Buffer path_buffer; 190 | std::string queries = "?"; 191 | while (it < endOfBuffer && *it != 0xff) { 192 | std::tie(option, length, consumed_bytes) = parseOptionHeader(option, &*it, &*endOfBuffer); 193 | it += consumed_bytes; 194 | if (length > std::distance(it, buffer.end())) { 195 | WLOG << "Received option " << option << " with invalid length=" << length << " bytes.\n"; 196 | throw std::exception(); 197 | } 198 | switch (option) { 199 | case Observe:observeValue = parseUnsigned(it, endOfBuffer, length); 200 | break; 201 | 202 | case UriPath: 203 | path_buffer.push_back(length); 204 | std::copy(it, it + length, std::back_inserter(path_buffer)); 205 | it += length; 206 | break; 207 | 208 | case ContentFormat:contentFormat = parseUnsigned(it, endOfBuffer, length); 209 | break; 210 | 211 | case UriQuery: 212 | queries += std::string(it, it + length) + '&'; 213 | it += length; 214 | break; 215 | 216 | default: 217 | // TODO: Handle unrecognized options according to the standard 218 | WLOG << "Unrecognized option " << option << " with length=" << length << " bytes.\n"; 219 | break; 220 | } 221 | } 222 | 223 | auto path = Path::fromBuffer(path_buffer).toString() + queries.substr(0, queries.length() - 1); 224 | 225 | // read payload 226 | std::string payload; 227 | if (it < endOfBuffer) { 228 | if (*it++ == 0xff) { 229 | std::copy(it, endOfBuffer, std::back_inserter(payload)); 230 | } 231 | } 232 | 233 | auto msg = Message(type, msgId, code, token, path, payload); 234 | if (contentFormat) msg.withContentFormat(contentFormat.value()); 235 | if (observeValue) msg.withObserveValue(observeValue.value()); 236 | return msg; 237 | } 238 | 239 | std::tuple Message::parseOptionHeader(Option option, 240 | const uint8_t *buffer, 241 | const uint8_t *end) { 242 | unsigned off{0}; 243 | if (buffer >= end) throw std::exception(); 244 | unsigned offset = (buffer[0] >> 4) & 0x0f; 245 | switch (offset) { 246 | case 13: 247 | if (buffer + 1 >= end) throw std::exception(); 248 | offset = 13 + buffer[1]; 249 | off = 1; 250 | break; 251 | case 14: 252 | if (buffer + 2 >= end) throw std::exception(); 253 | offset = 269 + (buffer[1] << 8) + buffer[2]; 254 | off = 2; 255 | break; 256 | case 15: 257 | // Todo: Payload marker or message format error 258 | throw std::exception(); 259 | break; 260 | } 261 | 262 | unsigned length = buffer[0] & 0x0f; 263 | switch (length) { 264 | case 13: 265 | if (buffer + off + 1 >= end) throw std::exception(); 266 | length = 13 + buffer[off + 1]; 267 | off += 1; 268 | break; 269 | case 14: 270 | if (buffer + off + 2 >= end) throw std::exception(); 271 | length = 269 + (buffer[off + 1] << 8) + buffer[off + 2]; 272 | off += 2; 273 | break; 274 | case 15: 275 | // Todo: Payload marker or message format error 276 | throw std::exception(); 277 | break; 278 | } 279 | 280 | return std::make_tuple(static_cast