OPENSSL报错: undefined symbol

master
陈曦 9 months ago
commit 967811164f

88
.gitignore vendored

@ -0,0 +1,88 @@
# Built application files
*.apk
*.aar
*.ap_
*.aab
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Uncomment the following line in case you need and you don't have the release build type files in your app
# release/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio Navigation editor temp files
.navigation/
# Android Studio captures folder
captures/
# IntelliJ
*.iml
.idea
.idea/workspace.xml
.idea/tasks.xml
.idea/gradle.xml
.idea/assetWizardSettings.xml
.idea/dictionaries
.idea/libraries
# Android Studio 3 in .gitignore file.
.idea/caches
.idea/modules.xml
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
.idea/navEditor.xml
# Keystore files
# Uncomment the following lines if you do not want to check your keystore files in.
#*.jks
#*.keystore
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
.cxx/
# Google Services (e.g. APIs or Firebase)
# google-services.json
# Freeline
freeline.py
freeline/
freeline_project_description.json
# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md
# Version control
vcs.xml
# lint
lint/intermediates/
lint/generated/
lint/outputs/
lint/tmp/
# lint/reports/
.DS_Store

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

@ -0,0 +1,128 @@
# Android Mqtt Client
基于[mosquitto](https://github.com/eclipse/mosquitto)的android mqtt客户端
* 使用[mosquitto/lib](https://github.com/eclipse/mosquitto/tree/master/lib)的代码用c++封装接口
* 对外提供java接口简单易用
* 支持OpenSSL加密
* 实现订阅、解除订阅、发布消息等功能
## 接口
MqttClient.java
```java
/**
* 获取单例
*/
public static MqttClient getInstance()
```
```java
/**
* 启动mqtt服务
*/
public void start(@NonNull String host, int port, @NonNull String uuid, boolean clearSession)
public void start(@NonNull String host, int port, @NonNull String uuid, boolean clearSession, @NonNull String caFilePath, @NonNull String username, @NonNull String password)
```
```java
/**
* 重连服务
*/
public void reconnect()
```
```java
/**
* 订阅主题
*/
public void subscribe(String topic)
public void subscribe(String topic, int qos)
public void subscribe(List<String> topicList)
public void subscribe(List<String> topicList, List<Integer> qosList)
```
```java
/**
* 解除订阅
*/
public void unsubscribe(String topic)
public void unsubscribe(List<String> topicList)
```
```java
/**
* 发布消息
*/
public void publish(String topic, String message)
public void publish(String topic, String message, int qos)
```
```java
/**
* mqtt事件回调接口
*/
public interface MqttCallback {
@WorkerThread
void onMessage(@NonNull String topic, @NonNull String message);
@WorkerThread
void onLog(String str);
}
```
```java
/**
* 设置mqtt事件回调
*/
public void setOnMqttCallback(@Nullable MqttCallback callback)
```
## 使用
```
- 导入Module: Android Studio -> File -> New -> Import Module -> 选中“library”文件夹
- 修改app/build.gradle文件添加ndk abiFilters设置
android {
...
defaultConfig {
...
ndk {
abiFilters 'armeabi-v7a', 'x86', 'arm64-v8a'
}
}
}
- 添加依赖
dependencies {
implementation project(':library')
}
```
## 编译问题
因为默认支持OpenSSL依赖静态库只添加了三个abi版本所以项目编译默认只支持```'armeabi-v7a', 'x86', 'arm64-v8a'```如需支持其他cpu abi请自行编译OpenSSL静态库
* 把```libssl.a libcrypto.a```文件添加到```/library/src/main/jni/lib```目录下并用文件夹区分abi类型
* 修改```library/build.gradle```添加对应的abi类型
* 修改```app/build.gradle```添加对应的abi类型
## 禁用OpenSSL
修改```/library/src/main/jni/Android.mk```文件,注释掉下面的代码
```
...
# ssl lib
#include $(CLEAR_VARS)
#LOCAL_MODULE := ssl
#LOCAL_SRC_FILES := lib/$(TARGET_ARCH_ABI)/libssl.a
#include $(PREBUILT_STATIC_LIBRARY)
# crypto lib
#include $(CLEAR_VARS)
#LOCAL_MODULE := ssl_crypto
#LOCAL_SRC_FILES := lib/$(TARGET_ARCH_ABI)/libcrypto.a
#include $(PREBUILT_STATIC_LIBRARY)
...
#LOCAL_CFLAGS += -DWITH_TLS_PSK
#LOCAL_CFLAGS += -DWITH_TLS
...
# link lib
#LOCAL_STATIC_LIBRARIES := ssl ssl_crypto
...
```
## 例子
app module只实现了简单的调用相关日志输出到了logcat

1
app/.gitignore vendored

@ -0,0 +1 @@
/build

@ -0,0 +1,50 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 29
ndkVersion '22.1.7171670'
buildToolsVersion "29.0.2"
defaultConfig {
applicationId "moe.key.yao.mqtt.client"
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
ndk {
abiFilters 'armeabi-v7a', 'x86', 'arm64-v8a'
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.google.guava:guava:28.0-android'
implementation project(':library')
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

@ -0,0 +1,24 @@
package moe.key.yao.mqtt.client
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("moe.key.yao.mqtt.client", appContext.packageName)
}
}

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="moe.key.yao.mqtt.client">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

@ -0,0 +1,115 @@
package moe.key.yao.mqtt.client
import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.google.common.io.ByteStreams
import kotlinx.android.synthetic.main.activity_main.*
import moe.key.yao.mqtt.library.MqttClient
import java.io.File
import java.io.FileOutputStream
class MainActivity : AppCompatActivity(), MqttClient.MqttCallback {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
MqttClient.getInstance().setOnMqttCallback(this)
initLayout()
initPermission()
}
override fun onDestroy() {
super.onDestroy()
MqttClient.getInstance().setOnMqttCallback(null)
}
private fun initLayout() {
btn_start.setOnClickListener {
startMqttService()
}
btn_subscribe.setOnClickListener {
subscribeTopic()
}
btn_unsubscribe.setOnClickListener {
unsubscribeTopic()
}
btn_publish.setOnClickListener {
publishMessage()
}
}
private fun initPermission() {
val hasPermission: Int = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
if (hasPermission != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE), 1)
return
}
}
private fun startMqttService() {
// copy ca file to sdcard
/*val caFilePath = "${getExternalFilesDir(null)}/ca.crt"
val file = File(caFilePath)
if (!file.exists()) {
file.createNewFile()
val inStream = assets.open("ca.crt")
val outStream = FileOutputStream(file)
ByteStreams.copy(inStream, outStream)
inStream.close()
outStream.close()
}*/
MqttClient.getInstance().start("192.168.1.101", 1883, "mqtt_android_client", false)
//MqttClient.getInstance().start("192.168.1.101", 8883, "mqtt_android_client", false, caFilePath, "username", "password")
}
private fun subscribeTopic() {
val topic = "/android/test/topicA"
MqttClient.getInstance().subscribe(topic)
}
private fun unsubscribeTopic() {
val topic = "/android/test/topicA"
MqttClient.getInstance().unsubscribe(topic)
}
private fun publishMessage() {
val topic = "/android/test/topicA"
val message = "test message"
MqttClient.getInstance().publish(topic, message)
}
override fun onMessage(topic: String, message: String) {
println("on message: $topic | $message")
}
override fun onLog(str: String?) {
println("on log: $str")
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == 1) {
var flag = false
for (i in permissions.indices) {
if (permissions[i] == Manifest.permission.READ_EXTERNAL_STORAGE && grantResults[i] == PackageManager.PERMISSION_GRANTED) {
flag = true
break
}
}
if (!flag) {
Toast.makeText(this, "获取权限失败", Toast.LENGTH_SHORT).show()
}
}
}
}

@ -0,0 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeWidth="1"
android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#008577"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/btn_start"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginTop="12dp"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:text="start service"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<Button
android:id="@+id/btn_subscribe"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginTop="12dp"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:text="subscribe"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_start"
app:layout_constraintEnd_toEndOf="parent"/>
<Button
android:id="@+id/btn_unsubscribe"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginTop="12dp"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:text="unsubscribe"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_subscribe"
app:layout_constraintEnd_toEndOf="parent"/>
<Button
android:id="@+id/btn_publish"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginTop="12dp"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:text="publish"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_unsubscribe"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#008577</color>
<color name="colorPrimaryDark">#00574B</color>
<color name="colorAccent">#D81B60</color>
</resources>

@ -0,0 +1,3 @@
<resources>
<string name="app_name">mqtt_client_android</string>
</resources>

@ -0,0 +1,11 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>

@ -0,0 +1,17 @@
package moe.key.yao.mqtt.client
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}

@ -0,0 +1,28 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.3.61'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

@ -0,0 +1,21 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official

Binary file not shown.

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

172
gradlew vendored

@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
gradlew.bat vendored

@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

@ -0,0 +1 @@
/build

@ -0,0 +1,53 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
ndkVersion '22.1.7171670'
defaultConfig {
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles 'consumer-rules.pro'
ndk {
abiFilters 'armeabi-v7a', 'x86', 'arm64-v8a'
}
externalNativeBuild {
ndkBuild {
arguments "NDK_APPLICATION_MK:=src/main/jni/Application.mk"
abiFilters 'armeabi-v7a', 'x86', 'arm64-v8a'
}
}
}
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
ndkBuild {
path file('src/main/jni/Android.mk')
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

@ -0,0 +1,27 @@
package moe.key.yao.mqtt.library;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("moe.key.yao.mqtt.library.test", appContext.getPackageName());
}
}

@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="moe.key.yao.mqtt.library" />

@ -0,0 +1,288 @@
package moe.key.yao.mqtt.library;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import java.util.List;
/**
* Created by key on 2020-02-20.
*
* @author key
*/
public class MqttClient {
static {
System.loadLibrary("mqtt");
}
public interface MqttCallback {
@WorkerThread
void onMessage(@NonNull String topic, @NonNull String message);
@WorkerThread
void onLog(String str);
}
private static MqttClient mInstance;
public static MqttClient getInstance() {
if (mInstance == null) {
synchronized (MqttClient.class) {
mInstance = new MqttClient();
}
}
return mInstance;
}
private MqttClient() {
}
private long mPtr = 0;
private MqttCallback mCallback = null;
/**
*
*/
public void setOnMqttCallback(@Nullable MqttCallback callback) {
mCallback = callback;
}
/**
* mqtt
* @param host
* @param port
* @param uuid uuid
* @param clearSession
*/
public void start(@NonNull String host, int port, @NonNull String uuid, boolean clearSession) {
startInternal(host, port, uuid, clearSession, false, null, null, null);
}
/**
* mqtt使ssl
* @param host
* @param port
* @param uuid uuid
* @param clearSession
* @param caFilePath
* @param username
* @param password
*/
public void start(@NonNull String host, int port, @NonNull String uuid, boolean clearSession, @NonNull String caFilePath, @NonNull String username, @NonNull String password) {
startInternal(host, port, uuid, clearSession, true, caFilePath, username, password);
}
private void startInternal(@NonNull String host, int port, @NonNull String uuid, boolean clearSession, boolean isTLS, @Nullable String caFilePath, @Nullable String username, @Nullable String password) {
if (mPtr == 0) {
mPtr = _init(host,
port,
uuid,
clearSession,
isTLS,
caFilePath == null ? "" : caFilePath,
username == null ? "" : username,
password == null ? "" : password);
}
_start(mPtr);
}
/**
*
*/
public void reconnect() {
if (mPtr == 0) {
return;
}
_reconnect(mPtr);
}
/**
* topic
*/
public void subscribe(String topic) {
if (mPtr == 0) {
return;
}
_subscribe(mPtr, new String[]{topic}, new int[]{2});
}
/**
* topic
*/
public void subscribe(String topic, int qos) {
if (mPtr == 0) {
return;
}
_subscribe(mPtr, new String[]{topic}, new int[]{qos});
}
/**
* topic
*/
public void subscribe(List<String> topicList) {
if (mPtr == 0) {
return;
}
String[] topicArray = new String[topicList.size()];
int[] qosArray = new int[topicList.size()];
for (int i = 0; i < topicList.size(); i++) {
topicArray[i] = topicList.get(i);
qosArray[i] = 2;
}
_subscribe(mPtr, topicArray, qosArray);
}
/**
* topic
*/
public void subscribe(List<String> topicList, List<Integer> qosList) {
if (mPtr == 0) {
return;
}
String[] topicArray = new String[topicList.size()];
int[] qosArray = new int[topicList.size()];
for (int i = 0; i < topicList.size(); i++) {
topicArray[i] = topicList.get(i);
qosArray[i] = qosList.get(i);
}
_subscribe(mPtr, topicArray, qosArray);
}
/**
* topic
*/
public void unsubscribe(String topic) {
if (mPtr == 0) {
return;
}
_unsubscribe(mPtr, new String[]{topic});
}
/**
* topic
*/
public void unsubscribe(List<String> topicList) {
if (mPtr == 0) {
return;
}
String[] topicArray = new String[topicList.size()];
for (int i = 0; i < topicList.size(); i++) {
topicArray[i] = topicList.get(i);
}
_unsubscribe(mPtr, topicArray);
}
/**
*
* @param topic topic
* @param message
*/
public void publish(String topic, String message) {
if (mPtr == 0) {
return;
}
_publish(mPtr, topic, message, 0);
}
/**
*
* @param topic topic
* @param message
* @param qos qos
*/
public void publish(String topic, String message, int qos) {
if (mPtr == 0) {
return;
}
_publish(mPtr, topic, message, qos);
}
// ==== callback
@WorkerThread
private void onMessage(String topic, String payload) {
if (mCallback != null) {
mCallback.onLog("Mqtt onMessage: " + topic + " | " + payload);
mCallback.onMessage(topic, payload);
}
}
@WorkerThread
private void onConnect() {
if (mCallback != null) {
mCallback.onLog("Mqtt onConnect");
}
}
@WorkerThread
private void onConnectWithFlag(int flags) {
if (mCallback != null) {
mCallback.onLog("Mqtt onConnect with flags: " + flags);
}
}
@WorkerThread
private void onDisconnect() {
if (mCallback != null) {
mCallback.onLog("Mqtt onDisconnect");
}
}
@WorkerThread
private void onPublish() {
if (mCallback != null) {
mCallback.onLog("Mqtt onPublish");
}
}
@WorkerThread
private void onSubscribe() {
if (mCallback != null) {
mCallback.onLog("Mqtt onSubscribe");
}
}
@WorkerThread
private void onUnsubscribe() {
if (mCallback != null) {
mCallback.onLog("Mqtt onUnsubscribe");
}
}
@WorkerThread
private void onLog(int level, String str) {
/*
#define MOSQ_LOG_NONE 0
#define MOSQ_LOG_INFO (1<<0)
#define MOSQ_LOG_NOTICE (1<<1)
#define MOSQ_LOG_WARNING (1<<2)
#define MOSQ_LOG_ERR (1<<3)
#define MOSQ_LOG_DEBUG (1<<4)
#define MOSQ_LOG_SUBSCRIBE (1<<5)
#define MOSQ_LOG_UNSUBSCRIBE (1<<6)
#define MOSQ_LOG_WEBSOCKETS (1<<7)
#define MOSQ_LOG_INTERNAL 0x80000000
#define MOSQ_LOG_ALL 0x7FFFFFFF
*/
if (mCallback != null) {
mCallback.onLog("Mqtt onLog: " + str + " | level: " + level);
}
}
// ==== native
private native long _init(String host, int port, String uuid, boolean clearSession, boolean isTLS, String caFilePath, String userName, String password);
private native void _start(long ptr);
private native void _reconnect(long ptr);
private native void _subscribe(long ptr, String[] topicArray, int[] qosArray);
private native void _unsubscribe(long ptr, String[] topArray);
private native void _publish(long ptr, String topic, String message, int qos);
}

@ -0,0 +1,95 @@
LOCAL_PATH := $(call my-dir)
# ssl lib
include $(CLEAR_VARS)
LOCAL_MODULE := ssl
LOCAL_SRC_FILES := lib/$(TARGET_ARCH_ABI)/libssl.a
include $(PREBUILT_STATIC_LIBRARY)
# crypto lib
include $(CLEAR_VARS)
LOCAL_MODULE := ssl_crypto
LOCAL_SRC_FILES := lib/$(TARGET_ARCH_ABI)/libcrypto.a
include $(PREBUILT_STATIC_LIBRARY)
# build flag
include $(CLEAR_VARS)
LOCAL_CFLAGS += -fexceptions
LOCAL_CPPFLAGS += -std=c++11
LOCAL_LDLIBS += -llog -landroid
# include
LOCAL_C_INCLUDES += $(LOCAL_PATH)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/openssl/include
LOCAL_C_INCLUDES += $(LOCAL_PATH)/uthash/src
LOCAL_C_INCLUDES += $(LOCAL_PATH)/mosquitto
LOCAL_C_INCLUDES += $(LOCAL_PATH)/mosquitto/lib
LOCAL_C_INCLUDES += $(LOCAL_PATH)/mosquitto/lib/cpp
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
# mosquitto lib
LOCAL_CFLAGS += -DWITH_SOCKS
LOCAL_CFLAGS += -DWITH_EC
LOCAL_CFLAGS += -DWITH_UUID
LOCAL_CFLAGS += -DWITH_SYS_TREE
LOCAL_CFLAGS += -DWITH_MENORY_TRACKING
LOCAL_CFLAGS += -DWITH_PERSISTENCE
LOCAL_CFLAGS += -DWITH_BRIDGE
LOCAL_CFLAGS += -DWITH_THREADING
LOCAL_CFLAGS += -DWITH_TLS_PSK
LOCAL_CFLAGS += -DWITH_TLS
LOCAL_SRC_FILES += mosquitto/lib/actions.c
LOCAL_SRC_FILES += mosquitto/lib/alias_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/callbacks.c
LOCAL_SRC_FILES += mosquitto/lib/connect.c
LOCAL_SRC_FILES += mosquitto/lib/handle_auth.c
LOCAL_SRC_FILES += mosquitto/lib/handle_connack.c
LOCAL_SRC_FILES += mosquitto/lib/handle_disconnect.c
LOCAL_SRC_FILES += mosquitto/lib/handle_ping.c
LOCAL_SRC_FILES += mosquitto/lib/handle_pubackcomp.c
LOCAL_SRC_FILES += mosquitto/lib/handle_publish.c
LOCAL_SRC_FILES += mosquitto/lib/handle_pubrec.c
LOCAL_SRC_FILES += mosquitto/lib/handle_pubrel.c
LOCAL_SRC_FILES += mosquitto/lib/handle_suback.c
LOCAL_SRC_FILES += mosquitto/lib/handle_unsuback.c
LOCAL_SRC_FILES += mosquitto/lib/helpers.c
LOCAL_SRC_FILES += mosquitto/lib/logging_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/loop.c
LOCAL_SRC_FILES += mosquitto/lib/memory_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/messages_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/mosquitto.c
LOCAL_SRC_FILES += mosquitto/lib/net_mosq_ocsp.c
LOCAL_SRC_FILES += mosquitto/lib/net_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/options.c
LOCAL_SRC_FILES += mosquitto/lib/packet_datatypes.c
LOCAL_SRC_FILES += mosquitto/lib/packet_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/property_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/read_handle.c
LOCAL_SRC_FILES += mosquitto/lib/send_connect.c
LOCAL_SRC_FILES += mosquitto/lib/send_disconnect.c
LOCAL_SRC_FILES += mosquitto/lib/send_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/send_publish.c
LOCAL_SRC_FILES += mosquitto/lib/send_subscribe.c
LOCAL_SRC_FILES += mosquitto/lib/send_unsubscribe.c
LOCAL_SRC_FILES += mosquitto/lib/socks_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/srv_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/thread_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/time_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/tls_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/utf8_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/util_mosq.c
LOCAL_SRC_FILES += mosquitto/lib/util_topic.c
LOCAL_SRC_FILES += mosquitto/lib/will_mosq.c
LOCAL_SRC_FILES += mosquitto_wrapper.cpp
LOCAL_SRC_FILES += JNIEnvHandler.cpp
LOCAL_SRC_FILES += MqttClient.cpp
# link lib
LOCAL_STATIC_LIBRARIES := ssl ssl_crypto
# module name
LOCAL_MODULE := mqtt
include $(BUILD_SHARED_LIBRARY)

@ -0,0 +1,5 @@
APP_OPTIM := release
APP_ABI := armeabi-v7a x86 arm64-v8a
APP_STL := c++_static
APP_PLATFORM := android-21
APP_PIE := false

@ -0,0 +1,117 @@
//
// Created by Key.Yao on 2020-02-20.
//
#include "JNIEnvHandler.h"
using namespace std;
struct ThreadMessage {
ThreadMessage(int i, void* m, bool flag) {
what = i;
msg = m;
exit = flag;
}
int what;
void* msg;
bool exit;
};
JNIEnvHandler::JNIEnvHandler(const char *threadName) {
_threadName = threadName;
_thread = nullptr;
_exitFlag = false;
_vm = nullptr;
_callback = nullptr;
}
JNIEnvHandler::~JNIEnvHandler() {
_thread = nullptr;
_callback = nullptr;
_instance = nullptr;
_vm = nullptr;
}
bool JNIEnvHandler::init(JavaVM *vm, jobject instance, handleMessage callback) {
if (!_thread) {
_thread = new thread(&JNIEnvHandler::process, this);
}
_vm = vm;
_instance = instance;
_callback = callback;
return true;
}
void JNIEnvHandler::exit() {
if (!_thread) {
return;
}
auto *msg = new ThreadMessage(-1, nullptr, true);
{
lock_guard<mutex> lock(_mutex);
_queue.push(msg);
_cv.notify_one();
}
_thread->join();
delete _thread;
_thread = nullptr;
_exitFlag = true;
}
std::thread::id JNIEnvHandler::getThreadId() {
return _thread->get_id();
}
void JNIEnvHandler::post(int what, void *obj) {
auto *msg = new ThreadMessage(what, obj, false);
unique_lock<mutex> lock(_mutex);
_queue.push(msg);
_cv.notify_one();
}
void JNIEnvHandler::process() {
_exitFlag = false;
JNIEnv *env = nullptr;
if (_vm) {
_vm->AttachCurrentThread(&env, nullptr);
}
while (true) {
ThreadMessage *msg = nullptr;
{
unique_lock<mutex> lock(_mutex);
while (_queue.empty()) {
_cv.wait(lock);
}
if (_queue.empty()) {
continue;
}
msg = _queue.front();
_queue.pop();
}
if (msg->exit) {
delete msg;
break;
}
if (env && _instance) {
_callback(msg->what, msg->msg, env, _instance);
}
delete msg;
}
env->DeleteGlobalRef(_instance);
if (_vm) {
_vm->DetachCurrentThread();
}
}

@ -0,0 +1,54 @@
//
// Created by Key.Yao on 2020-02-20.
//
#ifndef MQTT_CLIENT_ANDROID_JNIENVHANDLER_H
#define MQTT_CLIENT_ANDROID_JNIENVHANDLER_H
#include <jni.h>
#include <thread>
#include <queue>
#include <atomic>
#include <condition_variable>
typedef void(*handleMessage)(int what, void* obj, JNIEnv *env, jobject instance);
struct ThreadMessage;
class JNIEnvHandler {
public:
JNIEnvHandler(const char* threadName);
~JNIEnvHandler();
bool init(JavaVM *vm, jobject instance, handleMessage callback);
void exit();
std::thread::id getThreadId();
void post(int what, void *obj = nullptr);
private:
JNIEnvHandler(const JNIEnvHandler&);
JNIEnvHandler&operator=(const JNIEnvHandler&);
void process();
JavaVM *_vm;
jobject _instance;
std::thread *_thread;
std::queue<ThreadMessage*> _queue;
std::mutex _mutex;
std::condition_variable _cv;
std::atomic<bool> _exitFlag;
handleMessage _callback;
const char* _threadName;
};
#endif //MQTT_CLIENT_ANDROID_JNIENVHANDLER_H

@ -0,0 +1,635 @@
//
// Created by Key.Yao on 2020-02-20.
//
#include "MqttClient.h"
#include <jni.h>
#include <android/log.h>
#include <pthread.h>
#include <unistd.h>
#include "mosquitto_wrapper.h"
#include "JNIEnvHandler.h"
#define ALOG(...) ((void)__android_log_print(ANDROID_LOG_INFO, "MqttClientJNI", __VA_ARGS__))
#ifndef UNUSED
#define UNUSED(A) (void)(A)
#endif
#ifndef NELEM
#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
#endif
#ifndef MQTTCLIENT_JNICALL
#define MQTTCLIENT_JNICALL
#endif
#define TYPE_ON_MESSAGE 1
#define TYPE_ON_CONNECT 2
#define TYPE_ON_CONNECT_WITH_FLAG 3
#define TYPE_ON_DISCONNECT 4
#define TYPE_ON_PUBLISH 5
#define TYPE_ON_SUBSCRIBE 6
#define TYPE_ON_UNSUBSCRIBE 7
#define TYPE_ON_LOG 8
using namespace std;
using namespace mqttclient;
#ifdef __cplusplus
extern "C" {
#endif
// thread callback method define
void* mqtt_thread(void *p);
void* mqtt_reconnect_thread(void *p);
// mosquitto callback method define
void mqtt_on_message(void *instance, const struct mosquitto_message *message);
void mqtt_on_connect(void *instance, int rc);
void mqtt_on_connect_with_flag(void *instance, int rc, int flags);
void mqtt_on_disconnect(void *instance, int rc);
void mqtt_on_publish(void *instance, int rc);
void mqtt_on_subscribe(void *instance, int mid, int qos_count, const int *granted_qos);
void mqtt_on_unsubscribe(void *instance, int rc);
void mqtt_on_log(void *instance, int level, const char *str);
// callback to java define
void callback2Java(int what, void* obj, JNIEnv *env, jobject instance);
// jni method define
jlong MQTTCLIENT_JNICALL mqtt_native_init(JNIEnv *env, jobject instance, jstring hostString, jint portInt, jstring uuidString, jboolean clearSession, jboolean isTLS, jstring caFilePathString, jstring usernameString, jstring passwordString);
void MQTTCLIENT_JNICALL mqtt_native_start(JNIEnv *env, jobject instance, jlong ptr);
void MQTTCLIENT_JNICALL mqtt_native_reconnect(JNIEnv *env, jobject instance, jlong ptr);
void MQTTCLIENT_JNICALL mqtt_native_subscribe(JNIEnv *env, jobject instance, jlong ptr, jobjectArray topicJArray, jintArray qosJArray);
void MQTTCLIENT_JNICALL mqtt_native_unsubscribe(JNIEnv *env, jobject instance, jlong ptr, jobjectArray topicJArray);
void MQTTCLIENT_JNICALL mqtt_native_publish(JNIEnv *env, jobject instance, jlong ptr, jstring topicString, jstring messageString, jint qosInt);
JNINativeMethod mqtt_methods[] = {
{
"_init",
"(Ljava/lang/String;ILjava/lang/String;ZZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)J",
(void*) mqtt_native_init
},
{
"_start",
"(J)V",
(void*) mqtt_native_start
},
{
"_reconnect",
"(J)V",
(void*) mqtt_native_reconnect
},
{
"_subscribe",
"(J[Ljava/lang/String;[I)V",
(void*) mqtt_native_subscribe
},
{
"_unsubscribe",
"(J[Ljava/lang/String;)V",
(void*) mqtt_native_unsubscribe
},
{
"_publish",
"(JLjava/lang/String;Ljava/lang/String;I)V",
(void*) mqtt_native_publish
}
};
#ifdef __cplusplus
}
#endif
class ExtraData {
public:
string *host = nullptr;
int port = 0;
JNIEnvHandler *handler = nullptr;
JavaVM *javaVM = nullptr;
bool exitFlag = false;
bool running = false;
pthread_mutex_t mutexReconnect;
};
class MqttMsg {
public:
string *topic = nullptr;
string *payload = nullptr;
};
class MqttConnectFlag {
public:
int flags = 0;
};
class MqttLog {
public:
string *str = nullptr;
int level = 0;
};
static JavaVM *mVM;
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
UNUSED(reserved);
mVM = vm;
JNIEnv *env = nullptr;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
jclass clazz = env->FindClass("moe/key/yao/mqtt/library/MqttClient");
if (clazz == nullptr) {
return JNI_ERR;
}
if (env->RegisterNatives(clazz, mqtt_methods, NELEM(mqtt_methods)) < 0) {
env->DeleteLocalRef(clazz);
return JNI_ERR;
} else {
env->DeleteLocalRef(clazz);
}
// lib init
mqttclient::lib_init();
return JNI_VERSION_1_6;
}
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) {
UNUSED(vm);
UNUSED(reserved);
// lib clean up
mqttclient::lib_cleanup();
}
// jni method
jlong MQTTCLIENT_JNICALL mqtt_native_init(JNIEnv *env, jobject instance, jstring hostString, jint portInt, jstring uuidString, jboolean clearSession, jboolean isTLS, jstring caFilePathString, jstring usernameString, jstring passwordString) {
const char *host = env->GetStringUTFChars(hostString, nullptr);
const char *uuid = env->GetStringUTFChars(uuidString, nullptr);
const char *caFilePath = env->GetStringUTFChars(caFilePathString, nullptr);
const char *username = env->GetStringUTFChars(usernameString, nullptr);
const char *password = env->GetStringUTFChars(passwordString, nullptr);
bool sessionFlag = clearSession != 0;
auto *mosq = new mosquitto_wrapper(uuid, sessionFlag);
mosq->on_connect_callback = mqtt_on_connect;
mosq->on_connect_with_flag_callback = mqtt_on_connect_with_flag;
mosq->on_disconnect_callback = mqtt_on_disconnect;
mosq->on_publish_callback = mqtt_on_publish;
mosq->on_message_callback = mqtt_on_message;
mosq->on_subscribe_callback = mqtt_on_subscribe;
mosq->on_unsubscribe_callback = mqtt_on_unsubscribe;
mosq->on_log_callback = mqtt_on_log;
if (isTLS) {
mosq->tls_insecure_set(true);
mosq->tls_opts_set(1, "tlsv1", nullptr);
if (strlen(caFilePath) != 0) {
mosq->tls_set(caFilePath);
}
if (strlen(username) != 0 && strlen(password) != 0) {
mosq->username_pw_set(username, password);
}
}
auto *extra = new ExtraData();
extra->host = new string(host);
extra->port = portInt;
extra->javaVM = mVM;
extra->handler = new JNIEnvHandler("callback");
extra->handler->init(mVM, env->NewGlobalRef(instance), callback2Java);
pthread_mutex_init(&extra->mutexReconnect, nullptr);
mosq->extra = extra;
env->ReleaseStringUTFChars(hostString, host);
env->ReleaseStringUTFChars(caFilePathString, caFilePath);
env->ReleaseStringUTFChars(uuidString, uuid);
return reinterpret_cast<jlong>(mosq);
}
void MQTTCLIENT_JNICALL mqtt_native_start(JNIEnv *env, jobject instance, jlong ptr) {
UNUSED(env);
UNUSED(instance);
auto *mosq = reinterpret_cast<mosquitto_wrapper*>(ptr);
if (!mosq) {
return;
}
auto *extra = reinterpret_cast<ExtraData*>(mosq->extra);
if (!extra) {
return;
}
if (!extra->running) {
pthread_t tid;
pthread_create(&tid, nullptr, mqtt_thread, mosq);
pthread_detach(tid);
} else {
// running
ALOG("mqtt thread running");
}
}
void MQTTCLIENT_JNICALL mqtt_native_reconnect(JNIEnv *env, jobject instance, jlong ptr) {
UNUSED(env);
UNUSED(instance);
auto *mosq = reinterpret_cast<mosquitto_wrapper*>(ptr);
if (!mosq) {
return;
}
pthread_t tid;
pthread_create(&tid, nullptr, mqtt_reconnect_thread, mosq);
pthread_detach(tid);
}
void MQTTCLIENT_JNICALL mqtt_native_subscribe(JNIEnv *env, jobject instance, jlong ptr, jobjectArray topicJArray, jintArray qosJArray) {
UNUSED(instance);
jint arrayLen = env->GetArrayLength(topicJArray);
jint* qosList = env->GetIntArrayElements(qosJArray, nullptr);
string *topics[arrayLen];
for (int i = 0; i < arrayLen; i++) {
auto itemString = (jstring) env->GetObjectArrayElement(topicJArray, i);
const char *item = env->GetStringUTFChars(itemString, nullptr);
topics[i] = new string(item);
env->ReleaseStringUTFChars(itemString, item);
}
auto *mosq = reinterpret_cast<mosquitto_wrapper*>(ptr);
for (int i = 0 ; i < arrayLen; i ++) {
int mid;
mosq->subscribe(&mid, topics[i]->c_str(), qosList[i]);
delete topics[i];
topics[i] = nullptr;
}
}
void MQTTCLIENT_JNICALL mqtt_native_unsubscribe(JNIEnv *env, jobject instance, jlong ptr, jobjectArray topicJArray) {
UNUSED(instance);
jint arrayLen = env->GetArrayLength(topicJArray);
string *topics[arrayLen];
for (int i = 0; i < arrayLen; i++) {
auto itemString = (jstring) env->GetObjectArrayElement(topicJArray, i);
const char *item = env->GetStringUTFChars(itemString, nullptr);
topics[i] = new string(item);
env->ReleaseStringUTFChars(itemString, item);
}
auto *mosq = reinterpret_cast<mosquitto_wrapper*>(ptr);
for (int i = 0 ; i < arrayLen; i ++) {
int mid;
mosq->unsubscribe(&mid, topics[i]->c_str());
delete topics[i];
topics[i] = nullptr;
}
}
void MQTTCLIENT_JNICALL mqtt_native_publish(JNIEnv *env, jobject instance, jlong ptr, jstring topicString, jstring messageString, jint qosInt) {
UNUSED(instance);
const char *topic = env->GetStringUTFChars(topicString, nullptr);
const char *message = env->GetStringUTFChars(messageString, nullptr);
// publish
auto *mosq = reinterpret_cast<mosquitto_wrapper*>(ptr);
mosq->publish(nullptr, topic, (int) strlen(message), message, qosInt);
// release string
env->ReleaseStringUTFChars(topicString, topic);
env->ReleaseStringUTFChars(messageString, message);
}
// thread callback method
void* mqtt_thread(void *p) {
auto *mosq = reinterpret_cast<mosquitto_wrapper*>(p);
if (!mosq) {
return nullptr;
}
auto *extra = reinterpret_cast<ExtraData*>(mosq->extra);
if (!extra) {
return nullptr;
}
JNIEnv *env;
extra->javaVM->AttachCurrentThread(&env, nullptr);
extra->running = true;
mosq->connect(extra->host->c_str(), extra->port);
while (true) {
int rc = mosq->loop();
if (extra->exitFlag) {
break;
}
if (rc != MOSQ_ERR_SUCCESS) {
// error
const char *errorMsg = mqttclient::strerror(rc);
ALOG("mqtt connect error ========>> %s", errorMsg);
}
if (rc) {
sleep(10);
mosq->reconnect();
}
}
extra->javaVM->DetachCurrentThread();
extra->running = false;
pthread_mutex_destroy(&extra->mutexReconnect);
extra->javaVM = nullptr;
delete extra->host;
extra->handler->exit();
delete extra->handler;
extra->handler = nullptr;
delete extra;
mosq->extra = nullptr;
delete mosq;
return nullptr;
}
void* mqtt_reconnect_thread(void *p) {
auto *mosq = reinterpret_cast<mosquitto_wrapper*>(p);
if (!mosq) {
return nullptr;
}
auto *extra = reinterpret_cast<ExtraData*>(mosq->extra);
if (!extra) {
return nullptr;
}
pthread_mutex_lock(&extra->mutexReconnect);
mosq->disconnect();
pthread_mutex_unlock(&extra->mutexReconnect);
return nullptr;
}
// mosquitto callback method define
void mqtt_on_message(void *instance, const struct mosquitto_message *message) {
if (!instance) {
return;
}
auto mosq = reinterpret_cast<mosquitto_wrapper*>(instance);
if (!mosq->extra) {
return;
}
auto extra = reinterpret_cast<ExtraData*>(mosq->extra);
if (!extra) {
return;
}
if (!extra->handler) {
return;
}
auto msg = new MqttMsg();
msg->topic = new string(message->topic);
msg->payload = new string(reinterpret_cast<char*>(message->payload));
extra->handler->post(TYPE_ON_MESSAGE, msg);
}
void mqtt_on_connect(void *instance, int rc) {
UNUSED(rc);
if (!instance) {
return;
}
auto mosq = reinterpret_cast<mosquitto_wrapper*>(instance);
if (!mosq->extra) {
return;
}
auto extra = reinterpret_cast<ExtraData*>(mosq->extra);
if (!extra) {
return;
}
if (!extra->handler) {
return;
}
extra->handler->post(TYPE_ON_CONNECT);
}
void mqtt_on_connect_with_flag(void *instance, int rc, int flags) {
UNUSED(rc);
if (!instance) {
return;
}
auto mosq = reinterpret_cast<mosquitto_wrapper*>(instance);
if (!mosq->extra) {
return;
}
auto extra = reinterpret_cast<ExtraData*>(mosq->extra);
if (!extra) {
return;
}
if (!extra->handler) {
return;
}
auto connectFlag = new MqttConnectFlag();
connectFlag->flags = flags;
extra->handler->post(TYPE_ON_CONNECT_WITH_FLAG, connectFlag);
}
void mqtt_on_disconnect(void *instance, int rc) {
UNUSED(rc);
if (!instance) {
return;
}
auto mosq = reinterpret_cast<mosquitto_wrapper*>(instance);
if (!mosq->extra) {
return;
}
auto extra = reinterpret_cast<ExtraData*>(mosq->extra);
if (!extra) {
return;
}
if (!extra->handler) {
return;
}
extra->handler->post(TYPE_ON_DISCONNECT);
}
void mqtt_on_publish(void *instance, int rc) {
UNUSED(rc);
if (!instance) {
return;
}
auto mosq = reinterpret_cast<mosquitto_wrapper*>(instance);
if (!mosq->extra) {
return;
}
auto extra = reinterpret_cast<ExtraData*>(mosq->extra);
if (!extra) {
return;
}
if (!extra->handler) {
return;
}
extra->handler->post(TYPE_ON_PUBLISH);
}
void mqtt_on_subscribe(void *instance, int mid, int qos_count, const int *granted_qos) {
UNUSED(mid);
UNUSED(qos_count);
UNUSED(granted_qos);
if (!instance) {
return;
}
auto mosq = reinterpret_cast<mosquitto_wrapper*>(instance);
if (!mosq->extra) {
return;
}
auto extra = reinterpret_cast<ExtraData*>(mosq->extra);
if (!extra) {
return;
}
if (!extra->handler) {
return;
}
extra->handler->post(TYPE_ON_SUBSCRIBE);
}
void mqtt_on_unsubscribe(void *instance, int rc) {
UNUSED(rc);
if (!instance) {
return;
}
auto mosq = reinterpret_cast<mosquitto_wrapper*>(instance);
if (!mosq->extra) {
return;
}
auto extra = reinterpret_cast<ExtraData*>(mosq->extra);
if (!extra) {
return;
}
if (!extra->handler) {
return;
}
extra->handler->post(TYPE_ON_UNSUBSCRIBE);
}
void mqtt_on_log(void *instance, int level, const char *str) {
if (!instance) {
return;
}
auto mosq = reinterpret_cast<mosquitto_wrapper*>(instance);
if (!mosq->extra) {
return;
}
auto extra = reinterpret_cast<ExtraData*>(mosq->extra);
if (!extra) {
return;
}
if (!extra->handler) {
return;
}
auto log = new MqttLog();
log->level = level;
log->str = new string(str);
extra->handler->post(TYPE_ON_LOG, log);
}
// callback to java
void callback2Java(int what, void* obj, JNIEnv *env, jobject instance) {
auto clazz = env->GetObjectClass(instance);
jmethodID methodId = nullptr;
switch (what) {
case TYPE_ON_MESSAGE: {
if (!obj) {
break;
}
methodId = env->GetMethodID(clazz, "onMessage", "(Ljava/lang/String;Ljava/lang/String;)V");
auto msg = reinterpret_cast<MqttMsg*>(obj);
jstring topicString = env->NewStringUTF(msg->topic->c_str());
jstring payloadString = env->NewStringUTF(msg->payload->c_str());
env->CallVoidMethod(instance, methodId, topicString, payloadString);
env->DeleteLocalRef(topicString);
env->DeleteLocalRef(payloadString);
delete msg->topic;
delete msg->payload;
delete msg;
break;
};
case TYPE_ON_CONNECT: {
methodId = env->GetMethodID(clazz, "onConnect", "()V");
env->CallVoidMethod(instance, methodId);
break;
}
case TYPE_ON_CONNECT_WITH_FLAG: {
methodId = env->GetMethodID(clazz, "onConnectWithFlag", "(I)V");
auto log = reinterpret_cast<MqttConnectFlag*>(obj);
int flags = log->flags;
env->CallVoidMethod(instance, methodId, flags);
delete log;
break;
}
case TYPE_ON_DISCONNECT: {
methodId = env->GetMethodID(clazz, "onDisconnect", "()V");
env->CallVoidMethod(instance, methodId);
break;
}
case TYPE_ON_PUBLISH: {
methodId = env->GetMethodID(clazz, "onPublish", "()V");
env->CallVoidMethod(instance, methodId);
break;
}
case TYPE_ON_SUBSCRIBE: {
methodId = env->GetMethodID(clazz, "onSubscribe", "()V");
env->CallVoidMethod(instance, methodId);
break;
}
case TYPE_ON_UNSUBSCRIBE: {
methodId = env->GetMethodID(clazz, "onUnsubscribe", "()V");
env->CallVoidMethod(instance, methodId);
break;
}
case TYPE_ON_LOG: {
if (!obj) {
return;
}
methodId = env->GetMethodID(clazz, "onLog", "(ILjava/lang/String;)V");
auto log = reinterpret_cast<MqttLog*>(obj);
jstring msgString = env->NewStringUTF(log->str->c_str());
env->CallVoidMethod(instance, methodId, log->level, msgString);
env->DeleteLocalRef(msgString);
delete log->str;
delete log;
break;
}
default:
break;
}
env->DeleteLocalRef(clazz);
}

@ -0,0 +1,14 @@
//
// Created by Key.Yao on 2020-02-20.
//
#ifndef MQTT_CLIENT_ANDROID_MQTTCLIENT_H
#define MQTT_CLIENT_ANDROID_MQTTCLIENT_H
class MqttClient {
};
#endif //MQTT_CLIENT_ANDROID_MQTTCLIENT_H

@ -0,0 +1,72 @@
#ifndef CONFIG_H
#define CONFIG_H
/* ============================================================
* Platform options
* ============================================================ */
#ifdef __APPLE__
# define __DARWIN_C_SOURCE
#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__SYMBIAN32__) || defined(__QNX__)
# define _XOPEN_SOURCE 700
# define __BSD_VISIBLE 1
# define HAVE_NETINET_IN_H
#else
# define _XOPEN_SOURCE 700
# define _DEFAULT_SOURCE 1
# define _POSIX_C_SOURCE 200809L
#endif
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#define OPENSSL_LOAD_CONF
/* ============================================================
* Compatibility defines
* ============================================================ */
#if defined(_MSC_VER) && _MSC_VER < 1900
# define snprintf sprintf_s
# define EPROTO ECONNABORTED
#endif
#ifdef WIN32
# ifndef strcasecmp
# define strcasecmp strcmpi
# endif
# define strtok_r strtok_s
# define strerror_r(e, b, l) strerror_s(b, l, e)
#endif
#define uthash_malloc(sz) mosquitto__malloc(sz)
#define uthash_free(ptr,sz) mosquitto__free(ptr)
#ifdef WITH_TLS
# include <openssl/opensslconf.h>
# if defined(WITH_TLS_PSK) && !defined(OPENSSL_NO_PSK)
# define FINAL_WITH_TLS_PSK
# endif
#endif
#ifdef __COVERITY__
# include <stdint.h>
/* These are "wrong", but we don't use them so it doesn't matter */
# define _Float32 uint32_t
# define _Float32x uint32_t
# define _Float64 uint64_t
# define _Float64x uint64_t
# define _Float128 uint64_t
#endif
#define UNUSED(A) (void)(A)
/* Android Bionic libpthread implementation doesn't have pthread_cancel */
#ifndef ANDROID
# define HAVE_PTHREAD_CANCEL
#endif
#endif

@ -0,0 +1,274 @@
/*
Copyright (c) 2010-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <string.h>
#include "mosquitto.h"
#include "mosquitto_internal.h"
#include "memory_mosq.h"
#include "messages_mosq.h"
#include "mqtt_protocol.h"
#include "net_mosq.h"
#include "packet_mosq.h"
#include "send_mosq.h"
#include "util_mosq.h"
int mosquitto_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, const void *payload, int qos, bool retain)
{
return mosquitto_publish_v5(mosq, mid, topic, payloadlen, payload, qos, retain, NULL);
}
int mosquitto_publish_v5(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, const void *payload, int qos, bool retain, const mosquitto_property *properties)
{
struct mosquitto_message_all *message;
uint16_t local_mid;
const mosquitto_property *p;
const mosquitto_property *outgoing_properties = NULL;
mosquitto_property *properties_copy = NULL;
mosquitto_property local_property;
bool have_topic_alias;
int rc;
int tlen = 0;
uint32_t remaining_length;
if(!mosq || qos<0 || qos>2) return MOSQ_ERR_INVAL;
if(mosq->protocol != mosq_p_mqtt5 && properties) return MOSQ_ERR_NOT_SUPPORTED;
if(qos > mosq->maximum_qos) return MOSQ_ERR_QOS_NOT_SUPPORTED;
if(properties){
if(properties->client_generated){
outgoing_properties = properties;
}else{
memcpy(&local_property, properties, sizeof(mosquitto_property));
local_property.client_generated = true;
local_property.next = NULL;
outgoing_properties = &local_property;
}
rc = mosquitto_property_check_all(CMD_PUBLISH, outgoing_properties);
if(rc) return rc;
}
if(!topic || STREMPTY(topic)){
if(topic) topic = NULL;
if(mosq->protocol == mosq_p_mqtt5){
p = outgoing_properties;
have_topic_alias = false;
while(p){
if(p->identifier == MQTT_PROP_TOPIC_ALIAS){
have_topic_alias = true;
break;
}
p = p->next;
}
if(have_topic_alias == false){
return MOSQ_ERR_INVAL;
}
}else{
return MOSQ_ERR_INVAL;
}
}else{
tlen = strlen(topic);
if(mosquitto_validate_utf8(topic, tlen)) return MOSQ_ERR_MALFORMED_UTF8;
if(payloadlen < 0 || payloadlen > MQTT_MAX_PAYLOAD) return MOSQ_ERR_PAYLOAD_SIZE;
if(mosquitto_pub_topic_check(topic) != MOSQ_ERR_SUCCESS){
return MOSQ_ERR_INVAL;
}
}
if(mosq->maximum_packet_size > 0){
remaining_length = 1 + 2+tlen + payloadlen + property__get_length_all(outgoing_properties);
if(qos > 0){
remaining_length++;
}
if(packet__check_oversize(mosq, remaining_length)){
return MOSQ_ERR_OVERSIZE_PACKET;
}
}
local_mid = mosquitto__mid_generate(mosq);
if(mid){
*mid = local_mid;
}
if(qos == 0){
return send__publish(mosq, local_mid, topic, payloadlen, payload, qos, retain, false, outgoing_properties, NULL, 0);
}else{
if(outgoing_properties){
rc = mosquitto_property_copy_all(&properties_copy, outgoing_properties);
if(rc) return rc;
}
message = mosquitto__calloc(1, sizeof(struct mosquitto_message_all));
if(!message){
mosquitto_property_free_all(&properties_copy);
return MOSQ_ERR_NOMEM;
}
message->next = NULL;
message->timestamp = mosquitto_time();
message->msg.mid = local_mid;
if(topic){
message->msg.topic = mosquitto__strdup(topic);
if(!message->msg.topic){
message__cleanup(&message);
mosquitto_property_free_all(&properties_copy);
return MOSQ_ERR_NOMEM;
}
}
if(payloadlen){
message->msg.payloadlen = payloadlen;
message->msg.payload = mosquitto__malloc(payloadlen*sizeof(uint8_t));
if(!message->msg.payload){
message__cleanup(&message);
mosquitto_property_free_all(&properties_copy);
return MOSQ_ERR_NOMEM;
}
memcpy(message->msg.payload, payload, payloadlen*sizeof(uint8_t));
}else{
message->msg.payloadlen = 0;
message->msg.payload = NULL;
}
message->msg.qos = qos;
message->msg.retain = retain;
message->dup = false;
message->properties = properties_copy;
pthread_mutex_lock(&mosq->msgs_out.mutex);
message->state = mosq_ms_invalid;
message__queue(mosq, message, mosq_md_out);
pthread_mutex_unlock(&mosq->msgs_out.mutex);
return MOSQ_ERR_SUCCESS;
}
}
int mosquitto_subscribe(struct mosquitto *mosq, int *mid, const char *sub, int qos)
{
return mosquitto_subscribe_multiple(mosq, mid, 1, (char *const *const)&sub, qos, 0, NULL);
}
int mosquitto_subscribe_v5(struct mosquitto *mosq, int *mid, const char *sub, int qos, int options, const mosquitto_property *properties)
{
return mosquitto_subscribe_multiple(mosq, mid, 1, (char *const *const)&sub, qos, options, properties);
}
int mosquitto_subscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, int qos, int options, const mosquitto_property *properties)
{
const mosquitto_property *outgoing_properties = NULL;
mosquitto_property local_property;
int i;
int rc;
uint32_t remaining_length = 0;
int slen;
if(!mosq || !sub_count || !sub) return MOSQ_ERR_INVAL;
if(mosq->protocol != mosq_p_mqtt5 && properties) return MOSQ_ERR_NOT_SUPPORTED;
if(qos < 0 || qos > 2) return MOSQ_ERR_INVAL;
if((options & 0x30) == 0x30 || (options & 0xC0) != 0) return MOSQ_ERR_INVAL;
if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN;
if(properties){
if(properties->client_generated){
outgoing_properties = properties;
}else{
memcpy(&local_property, properties, sizeof(mosquitto_property));
local_property.client_generated = true;
local_property.next = NULL;
outgoing_properties = &local_property;
}
rc = mosquitto_property_check_all(CMD_SUBSCRIBE, outgoing_properties);
if(rc) return rc;
}
for(i=0; i<sub_count; i++){
if(mosquitto_sub_topic_check(sub[i])) return MOSQ_ERR_INVAL;
slen = strlen(sub[i]);
if(mosquitto_validate_utf8(sub[i], slen)) return MOSQ_ERR_MALFORMED_UTF8;
remaining_length += 2+slen + 1;
}
if(mosq->maximum_packet_size > 0){
remaining_length += 2 + property__get_length_all(outgoing_properties);
if(packet__check_oversize(mosq, remaining_length)){
return MOSQ_ERR_OVERSIZE_PACKET;
}
}
if(mosq->protocol == mosq_p_mqtt311 || mosq->protocol == mosq_p_mqtt31){
options = 0;
}
return send__subscribe(mosq, mid, sub_count, sub, qos|options, outgoing_properties);
}
int mosquitto_unsubscribe(struct mosquitto *mosq, int *mid, const char *sub)
{
return mosquitto_unsubscribe_multiple(mosq, mid, 1, (char *const *const)&sub, NULL);
}
int mosquitto_unsubscribe_v5(struct mosquitto *mosq, int *mid, const char *sub, const mosquitto_property *properties)
{
return mosquitto_unsubscribe_multiple(mosq, mid, 1, (char *const *const)&sub, properties);
}
int mosquitto_unsubscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, const mosquitto_property *properties)
{
const mosquitto_property *outgoing_properties = NULL;
mosquitto_property local_property;
int rc;
int i;
uint32_t remaining_length = 0;
int slen;
if(!mosq) return MOSQ_ERR_INVAL;
if(mosq->protocol != mosq_p_mqtt5 && properties) return MOSQ_ERR_NOT_SUPPORTED;
if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN;
if(properties){
if(properties->client_generated){
outgoing_properties = properties;
}else{
memcpy(&local_property, properties, sizeof(mosquitto_property));
local_property.client_generated = true;
local_property.next = NULL;
outgoing_properties = &local_property;
}
rc = mosquitto_property_check_all(CMD_UNSUBSCRIBE, outgoing_properties);
if(rc) return rc;
}
for(i=0; i<sub_count; i++){
if(mosquitto_sub_topic_check(sub[i])) return MOSQ_ERR_INVAL;
slen = strlen(sub[i]);
if(mosquitto_validate_utf8(sub[i], slen)) return MOSQ_ERR_MALFORMED_UTF8;
remaining_length += 2+slen;
}
if(mosq->maximum_packet_size > 0){
remaining_length += 2 + property__get_length_all(outgoing_properties);
if(packet__check_oversize(mosq, remaining_length)){
return MOSQ_ERR_OVERSIZE_PACKET;
}
}
return send__unsubscribe(mosq, mid, sub_count, sub, outgoing_properties);
}

@ -0,0 +1,85 @@
/*
Copyright (c) 2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include "mosquitto.h"
#include "alias_mosq.h"
#include "memory_mosq.h"
int alias__add(struct mosquitto *mosq, const char *topic, int alias)
{
int i;
struct mosquitto__alias *aliases;
for(i=0; i<mosq->alias_count; i++){
if(mosq->aliases[i].alias == alias){
mosquitto__free(mosq->aliases[i].topic);
mosq->aliases[i].topic = mosquitto__strdup(topic);
if(mosq->aliases[i].topic){
return MOSQ_ERR_SUCCESS;
}else{
return MOSQ_ERR_NOMEM;
}
}
}
/* New alias */
aliases = mosquitto__realloc(mosq->aliases, sizeof(struct mosquitto__alias)*(mosq->alias_count+1));
if(!aliases) return MOSQ_ERR_NOMEM;
mosq->aliases = aliases;
mosq->aliases[mosq->alias_count].alias = alias;
mosq->aliases[mosq->alias_count].topic = mosquitto__strdup(topic);
if(!mosq->aliases[mosq->alias_count].topic){
return MOSQ_ERR_NOMEM;
}
mosq->alias_count++;
return MOSQ_ERR_SUCCESS;
}
int alias__find(struct mosquitto *mosq, char **topic, int alias)
{
int i;
for(i=0; i<mosq->alias_count; i++){
if(mosq->aliases[i].alias == alias){
*topic = mosquitto__strdup(mosq->aliases[i].topic);
if(*topic){
return MOSQ_ERR_SUCCESS;
}else{
return MOSQ_ERR_NOMEM;
}
}
}
return MOSQ_ERR_INVAL;
}
void alias__free_all(struct mosquitto *mosq)
{
int i;
for(i=0; i<mosq->alias_count; i++){
mosquitto__free(mosq->aliases[i].topic);
}
mosquitto__free(mosq->aliases);
mosq->aliases = NULL;
mosq->alias_count = 0;
}

@ -0,0 +1,26 @@
/*
Copyright (c) 2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#ifndef ALIAS_MOSQ_H
#define ALIAS_MOSQ_H
#include "mosquitto_internal.h"
int alias__add(struct mosquitto *mosq, const char *topic, int alias);
int alias__find(struct mosquitto *mosq, char **topic, int alias);
void alias__free_all(struct mosquitto *mosq);
#endif

@ -0,0 +1,120 @@
/*
Copyright (c) 2010-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include "mosquitto.h"
#include "mosquitto_internal.h"
void mosquitto_connect_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int))
{
pthread_mutex_lock(&mosq->callback_mutex);
mosq->on_connect = on_connect;
pthread_mutex_unlock(&mosq->callback_mutex);
}
void mosquitto_connect_with_flags_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int, int))
{
pthread_mutex_lock(&mosq->callback_mutex);
mosq->on_connect_with_flags = on_connect;
pthread_mutex_unlock(&mosq->callback_mutex);
}
void mosquitto_connect_v5_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int, int, const mosquitto_property *))
{
pthread_mutex_lock(&mosq->callback_mutex);
mosq->on_connect_v5 = on_connect;
pthread_mutex_unlock(&mosq->callback_mutex);
}
void mosquitto_disconnect_callback_set(struct mosquitto *mosq, void (*on_disconnect)(struct mosquitto *, void *, int))
{
pthread_mutex_lock(&mosq->callback_mutex);
mosq->on_disconnect = on_disconnect;
pthread_mutex_unlock(&mosq->callback_mutex);
}
void mosquitto_disconnect_v5_callback_set(struct mosquitto *mosq, void (*on_disconnect)(struct mosquitto *, void *, int, const mosquitto_property *))
{
pthread_mutex_lock(&mosq->callback_mutex);
mosq->on_disconnect_v5 = on_disconnect;
pthread_mutex_unlock(&mosq->callback_mutex);
}
void mosquitto_publish_callback_set(struct mosquitto *mosq, void (*on_publish)(struct mosquitto *, void *, int))
{
pthread_mutex_lock(&mosq->callback_mutex);
mosq->on_publish = on_publish;
pthread_mutex_unlock(&mosq->callback_mutex);
}
void mosquitto_publish_v5_callback_set(struct mosquitto *mosq, void (*on_publish)(struct mosquitto *, void *, int, int, const mosquitto_property *props))
{
pthread_mutex_lock(&mosq->callback_mutex);
mosq->on_publish_v5 = on_publish;
pthread_mutex_unlock(&mosq->callback_mutex);
}
void mosquitto_message_callback_set(struct mosquitto *mosq, void (*on_message)(struct mosquitto *, void *, const struct mosquitto_message *))
{
pthread_mutex_lock(&mosq->callback_mutex);
mosq->on_message = on_message;
pthread_mutex_unlock(&mosq->callback_mutex);
}
void mosquitto_message_v5_callback_set(struct mosquitto *mosq, void (*on_message)(struct mosquitto *, void *, const struct mosquitto_message *, const mosquitto_property *props))
{
pthread_mutex_lock(&mosq->callback_mutex);
mosq->on_message_v5 = on_message;
pthread_mutex_unlock(&mosq->callback_mutex);
}
void mosquitto_subscribe_callback_set(struct mosquitto *mosq, void (*on_subscribe)(struct mosquitto *, void *, int, int, const int *))
{
pthread_mutex_lock(&mosq->callback_mutex);
mosq->on_subscribe = on_subscribe;
pthread_mutex_unlock(&mosq->callback_mutex);
}
void mosquitto_subscribe_v5_callback_set(struct mosquitto *mosq, void (*on_subscribe)(struct mosquitto *, void *, int, int, const int *, const mosquitto_property *props))
{
pthread_mutex_lock(&mosq->callback_mutex);
mosq->on_subscribe_v5 = on_subscribe;
pthread_mutex_unlock(&mosq->callback_mutex);
}
void mosquitto_unsubscribe_callback_set(struct mosquitto *mosq, void (*on_unsubscribe)(struct mosquitto *, void *, int))
{
pthread_mutex_lock(&mosq->callback_mutex);
mosq->on_unsubscribe = on_unsubscribe;
pthread_mutex_unlock(&mosq->callback_mutex);
}
void mosquitto_unsubscribe_v5_callback_set(struct mosquitto *mosq, void (*on_unsubscribe)(struct mosquitto *, void *, int, const mosquitto_property *props))
{
pthread_mutex_lock(&mosq->callback_mutex);
mosq->on_unsubscribe_v5 = on_unsubscribe;
pthread_mutex_unlock(&mosq->callback_mutex);
}
void mosquitto_log_callback_set(struct mosquitto *mosq, void (*on_log)(struct mosquitto *, void *, int, const char *))
{
pthread_mutex_lock(&mosq->log_callback_mutex);
mosq->on_log = on_log;
pthread_mutex_unlock(&mosq->log_callback_mutex);
}

@ -0,0 +1,296 @@
/*
Copyright (c) 2010-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <string.h>
#include "mosquitto.h"
#include "mosquitto_internal.h"
#include "logging_mosq.h"
#include "messages_mosq.h"
#include "memory_mosq.h"
#include "packet_mosq.h"
#include "mqtt_protocol.h"
#include "net_mosq.h"
#include "send_mosq.h"
#include "socks_mosq.h"
#include "util_mosq.h"
static char alphanum[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
static int mosquitto__reconnect(struct mosquitto *mosq, bool blocking, const mosquitto_property *properties);
static int mosquitto__connect_init(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address);
static int mosquitto__connect_init(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address)
{
int i;
int rc;
if(!mosq) return MOSQ_ERR_INVAL;
if(!host || port <= 0) return MOSQ_ERR_INVAL;
if(mosq->id == NULL && (mosq->protocol == mosq_p_mqtt31 || mosq->protocol == mosq_p_mqtt311)){
mosq->id = (char *)mosquitto__calloc(24, sizeof(char));
if(!mosq->id){
return MOSQ_ERR_NOMEM;
}
mosq->id[0] = 'm';
mosq->id[1] = 'o';
mosq->id[2] = 's';
mosq->id[3] = 'q';
mosq->id[4] = '-';
rc = util__random_bytes(&mosq->id[5], 18);
if(rc) return rc;
for(i=5; i<23; i++){
mosq->id[i] = alphanum[(mosq->id[i]&0x7F)%(sizeof(alphanum)-1)];
}
}
mosquitto__free(mosq->host);
mosq->host = mosquitto__strdup(host);
if(!mosq->host) return MOSQ_ERR_NOMEM;
mosq->port = port;
mosquitto__free(mosq->bind_address);
if(bind_address){
mosq->bind_address = mosquitto__strdup(bind_address);
if(!mosq->bind_address) return MOSQ_ERR_NOMEM;
}
mosq->keepalive = keepalive;
mosq->msgs_in.inflight_quota = mosq->msgs_in.inflight_maximum;
mosq->msgs_out.inflight_quota = mosq->msgs_out.inflight_maximum;
if(mosq->sockpairR != INVALID_SOCKET){
COMPAT_CLOSE(mosq->sockpairR);
mosq->sockpairR = INVALID_SOCKET;
}
if(mosq->sockpairW != INVALID_SOCKET){
COMPAT_CLOSE(mosq->sockpairW);
mosq->sockpairW = INVALID_SOCKET;
}
if(net__socketpair(&mosq->sockpairR, &mosq->sockpairW)){
log__printf(mosq, MOSQ_LOG_WARNING,
"Warning: Unable to open socket pair, outgoing publish commands may be delayed.");
}
return MOSQ_ERR_SUCCESS;
}
int mosquitto_connect(struct mosquitto *mosq, const char *host, int port, int keepalive)
{
return mosquitto_connect_bind(mosq, host, port, keepalive, NULL);
}
int mosquitto_connect_bind(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address)
{
return mosquitto_connect_bind_v5(mosq, host, port, keepalive, bind_address, NULL);
}
int mosquitto_connect_bind_v5(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address, const mosquitto_property *properties)
{
int rc;
if(properties){
rc = mosquitto_property_check_all(CMD_CONNECT, properties);
if(rc) return rc;
}
rc = mosquitto__connect_init(mosq, host, port, keepalive, bind_address);
if(rc) return rc;
mosquitto__set_state(mosq, mosq_cs_new);
return mosquitto__reconnect(mosq, true, properties);
}
int mosquitto_connect_async(struct mosquitto *mosq, const char *host, int port, int keepalive)
{
return mosquitto_connect_bind_async(mosq, host, port, keepalive, NULL);
}
int mosquitto_connect_bind_async(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address)
{
int rc = mosquitto__connect_init(mosq, host, port, keepalive, bind_address);
if(rc) return rc;
return mosquitto__reconnect(mosq, false, NULL);
}
int mosquitto_reconnect_async(struct mosquitto *mosq)
{
return mosquitto__reconnect(mosq, false, NULL);
}
int mosquitto_reconnect(struct mosquitto *mosq)
{
return mosquitto__reconnect(mosq, true, NULL);
}
static int mosquitto__reconnect(struct mosquitto *mosq, bool blocking, const mosquitto_property *properties)
{
const mosquitto_property *outgoing_properties = NULL;
mosquitto_property local_property;
int rc;
if(!mosq) return MOSQ_ERR_INVAL;
if(!mosq->host || mosq->port <= 0) return MOSQ_ERR_INVAL;
if(mosq->protocol != mosq_p_mqtt5 && properties) return MOSQ_ERR_NOT_SUPPORTED;
if(properties){
if(properties->client_generated){
outgoing_properties = properties;
}else{
memcpy(&local_property, properties, sizeof(mosquitto_property));
local_property.client_generated = true;
local_property.next = NULL;
outgoing_properties = &local_property;
}
rc = mosquitto_property_check_all(CMD_CONNECT, outgoing_properties);
if(rc) return rc;
}
pthread_mutex_lock(&mosq->msgtime_mutex);
mosq->last_msg_in = mosquitto_time();
mosq->next_msg_out = mosq->last_msg_in + mosq->keepalive;
pthread_mutex_unlock(&mosq->msgtime_mutex);
mosq->ping_t = 0;
packet__cleanup(&mosq->in_packet);
packet__cleanup_all(mosq);
message__reconnect_reset(mosq);
if(mosq->sock != INVALID_SOCKET){
net__socket_close(mosq); //close socket
}
#ifdef WITH_SOCKS
if(mosq->socks5_host){
rc = net__socket_connect(mosq, mosq->socks5_host, mosq->socks5_port, mosq->bind_address, blocking);
}else
#endif
{
rc = net__socket_connect(mosq, mosq->host, mosq->port, mosq->bind_address, blocking);
}
if(rc>0){
mosquitto__set_state(mosq, mosq_cs_connect_pending);
return rc;
}
#ifdef WITH_SOCKS
if(mosq->socks5_host){
mosquitto__set_state(mosq, mosq_cs_socks5_new);
return socks5__send(mosq);
}else
#endif
{
mosquitto__set_state(mosq, mosq_cs_connected);
rc = send__connect(mosq, mosq->keepalive, mosq->clean_start, outgoing_properties);
if(rc){
packet__cleanup_all(mosq);
net__socket_close(mosq);
mosquitto__set_state(mosq, mosq_cs_new);
}
return rc;
}
}
int mosquitto_disconnect(struct mosquitto *mosq)
{
return mosquitto_disconnect_v5(mosq, 0, NULL);
}
int mosquitto_disconnect_v5(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties)
{
const mosquitto_property *outgoing_properties = NULL;
mosquitto_property local_property;
int rc;
if(!mosq) return MOSQ_ERR_INVAL;
if(mosq->protocol != mosq_p_mqtt5 && properties) return MOSQ_ERR_NOT_SUPPORTED;
if(properties){
if(properties->client_generated){
outgoing_properties = properties;
}else{
memcpy(&local_property, properties, sizeof(mosquitto_property));
local_property.client_generated = true;
local_property.next = NULL;
outgoing_properties = &local_property;
}
rc = mosquitto_property_check_all(CMD_DISCONNECT, outgoing_properties);
if(rc) return rc;
}
mosquitto__set_state(mosq, mosq_cs_disconnected);
if(mosq->sock == INVALID_SOCKET){
return MOSQ_ERR_NO_CONN;
}else{
return send__disconnect(mosq, reason_code, outgoing_properties);
}
}
void do_client_disconnect(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties)
{
mosquitto__set_state(mosq, mosq_cs_disconnected);
net__socket_close(mosq);
/* Free data and reset values */
pthread_mutex_lock(&mosq->out_packet_mutex);
mosq->current_out_packet = mosq->out_packet;
if(mosq->out_packet){
mosq->out_packet = mosq->out_packet->next;
if(!mosq->out_packet){
mosq->out_packet_last = NULL;
}
}
pthread_mutex_unlock(&mosq->out_packet_mutex);
pthread_mutex_lock(&mosq->msgtime_mutex);
mosq->next_msg_out = mosquitto_time() + mosq->keepalive;
pthread_mutex_unlock(&mosq->msgtime_mutex);
pthread_mutex_lock(&mosq->callback_mutex);
if(mosq->on_disconnect){
mosq->in_callback = true;
mosq->on_disconnect(mosq, mosq->userdata, reason_code);
mosq->in_callback = false;
}
if(mosq->on_disconnect_v5){
mosq->in_callback = true;
mosq->on_disconnect_v5(mosq, mosq->userdata, reason_code, properties);
mosq->in_callback = false;
}
pthread_mutex_unlock(&mosq->callback_mutex);
pthread_mutex_unlock(&mosq->current_out_packet_mutex);
}

@ -0,0 +1,13 @@
#ifndef DUMMYPTHREAD_H
#define DUMMYPTHREAD_H
#define pthread_create(A, B, C, D)
#define pthread_join(A, B)
#define pthread_cancel(A)
#define pthread_mutex_init(A, B)
#define pthread_mutex_destroy(A)
#define pthread_mutex_lock(A)
#define pthread_mutex_unlock(A)
#endif

@ -0,0 +1,49 @@
/*
Copyright (c) 2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <stdio.h>
#include <string.h>
#include "logging_mosq.h"
#include "mosquitto_internal.h"
#include "mqtt_protocol.h"
#include "packet_mosq.h"
#include "property_mosq.h"
int handle__auth(struct mosquitto *mosq)
{
int rc = 0;
uint8_t reason_code;
mosquitto_property *properties = NULL;
if(!mosq) return MOSQ_ERR_INVAL;
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received AUTH", mosq->id);
if(mosq->protocol != mosq_p_mqtt5){
return MOSQ_ERR_PROTOCOL;
}
if(packet__read_byte(&mosq->in_packet, &reason_code)) return 1;
rc = property__read_all(CMD_AUTH, &mosq->in_packet, &properties);
if(rc) return rc;
mosquitto_property_free_all(&properties); /* FIXME - TEMPORARY UNTIL PROPERTIES PROCESSED */
return MOSQ_ERR_SUCCESS;
}

@ -0,0 +1,129 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#include "mosquitto.h"
#include "logging_mosq.h"
#include "memory_mosq.h"
#include "messages_mosq.h"
#include "mqtt_protocol.h"
#include "net_mosq.h"
#include "packet_mosq.h"
#include "property_mosq.h"
#include "read_handle.h"
static void connack_callback(struct mosquitto *mosq, uint8_t reason_code, uint8_t connect_flags, const mosquitto_property *properties)
{
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received CONNACK (%d)", mosq->id, reason_code);
if(reason_code == MQTT_RC_SUCCESS){
mosq->reconnects = 0;
}
pthread_mutex_lock(&mosq->callback_mutex);
if(mosq->on_connect){
mosq->in_callback = true;
mosq->on_connect(mosq, mosq->userdata, reason_code);
mosq->in_callback = false;
}
if(mosq->on_connect_with_flags){
mosq->in_callback = true;
mosq->on_connect_with_flags(mosq, mosq->userdata, reason_code, connect_flags);
mosq->in_callback = false;
}
if(mosq->on_connect_v5){
mosq->in_callback = true;
mosq->on_connect_v5(mosq, mosq->userdata, reason_code, connect_flags, properties);
mosq->in_callback = false;
}
pthread_mutex_unlock(&mosq->callback_mutex);
}
int handle__connack(struct mosquitto *mosq)
{
uint8_t connect_flags;
uint8_t reason_code;
int rc;
mosquitto_property *properties = NULL;
char *clientid = NULL;
assert(mosq);
rc = packet__read_byte(&mosq->in_packet, &connect_flags);
if(rc) return rc;
rc = packet__read_byte(&mosq->in_packet, &reason_code);
if(rc) return rc;
if(mosq->protocol == mosq_p_mqtt5){
rc = property__read_all(CMD_CONNACK, &mosq->in_packet, &properties);
if(rc == MOSQ_ERR_PROTOCOL && reason_code == CONNACK_REFUSED_PROTOCOL_VERSION){
/* This could occur because we are connecting to a v3.x broker and
* it has replied with "unacceptable protocol version", but with a
* v3 CONNACK. */
connack_callback(mosq, MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION, connect_flags, NULL);
return rc;
}else if(rc){
return rc;
}
}
mosquitto_property_read_string(properties, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, &clientid, false);
if(clientid){
if(mosq->id){
/* We've been sent a client identifier but already have one. This
* shouldn't happen. */
free(clientid);
mosquitto_property_free_all(&properties);
return MOSQ_ERR_PROTOCOL;
}else{
mosq->id = clientid;
clientid = NULL;
}
}
mosquitto_property_read_byte(properties, MQTT_PROP_MAXIMUM_QOS, &mosq->maximum_qos, false);
mosquitto_property_read_int16(properties, MQTT_PROP_RECEIVE_MAXIMUM, &mosq->msgs_out.inflight_maximum, false);
mosquitto_property_read_int16(properties, MQTT_PROP_SERVER_KEEP_ALIVE, &mosq->keepalive, false);
mosquitto_property_read_int32(properties, MQTT_PROP_MAXIMUM_PACKET_SIZE, &mosq->maximum_packet_size, false);
mosq->msgs_out.inflight_quota = mosq->msgs_out.inflight_maximum;
connack_callback(mosq, reason_code, connect_flags, properties);
mosquitto_property_free_all(&properties);
switch(reason_code){
case 0:
pthread_mutex_lock(&mosq->state_mutex);
if(mosq->state != mosq_cs_disconnecting){
mosq->state = mosq_cs_active;
}
pthread_mutex_unlock(&mosq->state_mutex);
message__retry_check(mosq);
return MOSQ_ERR_SUCCESS;
case 1:
case 2:
case 3:
case 4:
case 5:
return MOSQ_ERR_CONN_REFUSED;
default:
return MOSQ_ERR_PROTOCOL;
}
}

@ -0,0 +1,62 @@
/*
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <stdio.h>
#include <string.h>
#include "logging_mosq.h"
#include "mqtt_protocol.h"
#include "memory_mosq.h"
#include "net_mosq.h"
#include "packet_mosq.h"
#include "property_mosq.h"
#include "send_mosq.h"
#include "util_mosq.h"
int handle__disconnect(struct mosquitto *mosq)
{
int rc;
uint8_t reason_code;
mosquitto_property *properties = NULL;
if(!mosq){
return MOSQ_ERR_INVAL;
}
if(mosq->protocol != mosq_p_mqtt5){
return MOSQ_ERR_PROTOCOL;
}
rc = packet__read_byte(&mosq->in_packet, &reason_code);
if(rc) return rc;
if(mosq->in_packet.remaining_length > 2){
rc = property__read_all(CMD_DISCONNECT, &mosq->in_packet, &properties);
if(rc) return rc;
mosquitto_property_free_all(&properties);
}
log__printf(mosq, MOSQ_LOG_DEBUG, "Received DISCONNECT (%d)", reason_code);
do_client_disconnect(mosq, reason_code, properties);
mosquitto_property_free_all(&properties);
return MOSQ_ERR_SUCCESS;
}

@ -0,0 +1,76 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#ifdef WITH_BROKER
# include "mosquitto_broker_internal.h"
#endif
#include "mosquitto.h"
#include "logging_mosq.h"
#include "memory_mosq.h"
#include "messages_mosq.h"
#include "mqtt_protocol.h"
#include "net_mosq.h"
#include "packet_mosq.h"
#include "read_handle.h"
#include "send_mosq.h"
#include "util_mosq.h"
int handle__pingreq(struct mosquitto *mosq)
{
int state;
assert(mosq);
state = mosquitto__get_state(mosq);
if(state != mosq_cs_active){
return MOSQ_ERR_PROTOCOL;
}
#ifdef WITH_BROKER
log__printf(NULL, MOSQ_LOG_DEBUG, "Received PINGREQ from %s", mosq->id);
#else
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PINGREQ", mosq->id);
#endif
return send__pingresp(mosq);
}
int handle__pingresp(struct mosquitto *mosq)
{
int state;
assert(mosq);
state = mosquitto__get_state(mosq);
if(state != mosq_cs_active){
return MOSQ_ERR_PROTOCOL;
}
mosq->ping_t = 0; /* No longer waiting for a PINGRESP. */
#ifdef WITH_BROKER
log__printf(NULL, MOSQ_LOG_DEBUG, "Received PINGRESP from %s", mosq->id);
#else
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PINGRESP", mosq->id);
#endif
return MOSQ_ERR_SUCCESS;
}

@ -0,0 +1,120 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#ifdef WITH_BROKER
# include "mosquitto_broker_internal.h"
#endif
#include "mosquitto.h"
#include "logging_mosq.h"
#include "memory_mosq.h"
#include "messages_mosq.h"
#include "mqtt_protocol.h"
#include "net_mosq.h"
#include "packet_mosq.h"
#include "read_handle.h"
#include "send_mosq.h"
#include "util_mosq.h"
#ifdef WITH_BROKER
int handle__pubackcomp(struct mosquitto_db *db, struct mosquitto *mosq, const char *type)
#else
int handle__pubackcomp(struct mosquitto *mosq, const char *type)
#endif
{
uint8_t reason_code = 0;
uint16_t mid;
int rc;
mosquitto_property *properties = NULL;
int qos;
int state;
assert(mosq);
state = mosquitto__get_state(mosq);
if(state != mosq_cs_active){
return MOSQ_ERR_PROTOCOL;
}
pthread_mutex_lock(&mosq->msgs_out.mutex);
util__increment_send_quota(mosq);
pthread_mutex_unlock(&mosq->msgs_out.mutex);
rc = packet__read_uint16(&mosq->in_packet, &mid);
if(rc) return rc;
qos = type[3] == 'A'?1:2; /* pubAck or pubComp */
if(mid == 0) return MOSQ_ERR_PROTOCOL;
if(mosq->protocol == mosq_p_mqtt5 && mosq->in_packet.remaining_length > 2){
rc = packet__read_byte(&mosq->in_packet, &reason_code);
if(rc) return rc;
if(mosq->in_packet.remaining_length > 3){
rc = property__read_all(CMD_PUBACK, &mosq->in_packet, &properties);
if(rc) return rc;
}
}
#ifdef WITH_BROKER
log__printf(NULL, MOSQ_LOG_DEBUG, "Received %s from %s (Mid: %d, RC:%d)", type, mosq->id, mid, reason_code);
/* Immediately free, we don't do anything with Reason String or User Property at the moment */
mosquitto_property_free_all(&properties);
rc = db__message_delete_outgoing(db, mosq, mid, mosq_ms_wait_for_pubcomp, qos);
if(rc == MOSQ_ERR_NOT_FOUND){
log__printf(mosq, MOSQ_LOG_WARNING, "Warning: Received %s from %s for an unknown packet identifier %d.", type, mosq->id, mid);
return MOSQ_ERR_SUCCESS;
}else{
return rc;
}
#else
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received %s (Mid: %d, RC:%d)", mosq->id, type, mid, reason_code);
rc = message__delete(mosq, mid, mosq_md_out, qos);
if(rc){
return rc;
}else{
/* Only inform the client the message has been sent once. */
pthread_mutex_lock(&mosq->callback_mutex);
if(mosq->on_publish){
mosq->in_callback = true;
mosq->on_publish(mosq, mosq->userdata, mid);
mosq->in_callback = false;
}
if(mosq->on_publish_v5){
mosq->in_callback = true;
mosq->on_publish_v5(mosq, mosq->userdata, mid, reason_code, properties);
mosq->in_callback = false;
}
pthread_mutex_unlock(&mosq->callback_mutex);
mosquitto_property_free_all(&properties);
}
pthread_mutex_lock(&mosq->msgs_out.mutex);
message__release_to_inflight(mosq, mosq_md_out);
pthread_mutex_unlock(&mosq->msgs_out.mutex);
return MOSQ_ERR_SUCCESS;
#endif
}

@ -0,0 +1,169 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#include <string.h>
#include "mosquitto.h"
#include "mosquitto_internal.h"
#include "logging_mosq.h"
#include "memory_mosq.h"
#include "mqtt_protocol.h"
#include "messages_mosq.h"
#include "packet_mosq.h"
#include "property_mosq.h"
#include "send_mosq.h"
#include "time_mosq.h"
#include "util_mosq.h"
int handle__publish(struct mosquitto *mosq)
{
uint8_t header;
struct mosquitto_message_all *message;
int rc = 0;
uint16_t mid;
int slen;
mosquitto_property *properties = NULL;
int state;
assert(mosq);
state = mosquitto__get_state(mosq);
if(state != mosq_cs_active){
return MOSQ_ERR_PROTOCOL;
}
message = mosquitto__calloc(1, sizeof(struct mosquitto_message_all));
if(!message) return MOSQ_ERR_NOMEM;
header = mosq->in_packet.command;
message->dup = (header & 0x08)>>3;
message->msg.qos = (header & 0x06)>>1;
message->msg.retain = (header & 0x01);
rc = packet__read_string(&mosq->in_packet, &message->msg.topic, &slen);
if(rc){
message__cleanup(&message);
return rc;
}
if(!slen){
message__cleanup(&message);
return MOSQ_ERR_PROTOCOL;
}
if(message->msg.qos > 0){
if(mosq->protocol == mosq_p_mqtt5){
if(mosq->msgs_in.inflight_quota == 0){
message__cleanup(&message);
/* FIXME - should send a DISCONNECT here */
return MOSQ_ERR_PROTOCOL;
}
}
rc = packet__read_uint16(&mosq->in_packet, &mid);
if(rc){
message__cleanup(&message);
return rc;
}
if(mid == 0){
message__cleanup(&message);
return MOSQ_ERR_PROTOCOL;
}
message->msg.mid = (int)mid;
}
if(mosq->protocol == mosq_p_mqtt5){
rc = property__read_all(CMD_PUBLISH, &mosq->in_packet, &properties);
if(rc) return rc;
}
message->msg.payloadlen = mosq->in_packet.remaining_length - mosq->in_packet.pos;
if(message->msg.payloadlen){
message->msg.payload = mosquitto__calloc(message->msg.payloadlen+1, sizeof(uint8_t));
if(!message->msg.payload){
message__cleanup(&message);
mosquitto_property_free_all(&properties);
return MOSQ_ERR_NOMEM;
}
rc = packet__read_bytes(&mosq->in_packet, message->msg.payload, message->msg.payloadlen);
if(rc){
message__cleanup(&message);
mosquitto_property_free_all(&properties);
return rc;
}
}
log__printf(mosq, MOSQ_LOG_DEBUG,
"Client %s received PUBLISH (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))",
mosq->id, message->dup, message->msg.qos, message->msg.retain,
message->msg.mid, message->msg.topic,
(long)message->msg.payloadlen);
message->timestamp = mosquitto_time();
switch(message->msg.qos){
case 0:
pthread_mutex_lock(&mosq->callback_mutex);
if(mosq->on_message){
mosq->in_callback = true;
mosq->on_message(mosq, mosq->userdata, &message->msg);
mosq->in_callback = false;
}
if(mosq->on_message_v5){
mosq->in_callback = true;
mosq->on_message_v5(mosq, mosq->userdata, &message->msg, properties);
mosq->in_callback = false;
}
pthread_mutex_unlock(&mosq->callback_mutex);
message__cleanup(&message);
mosquitto_property_free_all(&properties);
return MOSQ_ERR_SUCCESS;
case 1:
util__decrement_receive_quota(mosq);
rc = send__puback(mosq, message->msg.mid, 0);
pthread_mutex_lock(&mosq->callback_mutex);
if(mosq->on_message){
mosq->in_callback = true;
mosq->on_message(mosq, mosq->userdata, &message->msg);
mosq->in_callback = false;
}
if(mosq->on_message_v5){
mosq->in_callback = true;
mosq->on_message_v5(mosq, mosq->userdata, &message->msg, properties);
mosq->in_callback = false;
}
pthread_mutex_unlock(&mosq->callback_mutex);
message__cleanup(&message);
mosquitto_property_free_all(&properties);
return rc;
case 2:
message->properties = properties;
util__decrement_receive_quota(mosq);
rc = send__pubrec(mosq, message->msg.mid, 0);
pthread_mutex_lock(&mosq->msgs_in.mutex);
message->state = mosq_ms_wait_for_pubrel;
message__queue(mosq, message, mosq_md_in);
pthread_mutex_unlock(&mosq->msgs_in.mutex);
return rc;
default:
message__cleanup(&message);
mosquitto_property_free_all(&properties);
return MOSQ_ERR_PROTOCOL;
}
}

@ -0,0 +1,112 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#ifdef WITH_BROKER
# include "mosquitto_broker_internal.h"
#endif
#include "mosquitto.h"
#include "logging_mosq.h"
#include "memory_mosq.h"
#include "messages_mosq.h"
#include "mqtt_protocol.h"
#include "net_mosq.h"
#include "packet_mosq.h"
#include "read_handle.h"
#include "send_mosq.h"
#include "util_mosq.h"
int handle__pubrec(struct mosquitto_db *db, struct mosquitto *mosq)
{
uint8_t reason_code = 0;
uint16_t mid;
int rc;
mosquitto_property *properties = NULL;
int state;
assert(mosq);
state = mosquitto__get_state(mosq);
if(state != mosq_cs_active){
return MOSQ_ERR_PROTOCOL;
}
rc = packet__read_uint16(&mosq->in_packet, &mid);
if(rc) return rc;
if(mid == 0) return MOSQ_ERR_PROTOCOL;
if(mosq->protocol == mosq_p_mqtt5 && mosq->in_packet.remaining_length > 2){
rc = packet__read_byte(&mosq->in_packet, &reason_code);
if(rc) return rc;
if(mosq->in_packet.remaining_length > 3){
rc = property__read_all(CMD_PUBREC, &mosq->in_packet, &properties);
if(rc) return rc;
/* Immediately free, we don't do anything with Reason String or User Property at the moment */
mosquitto_property_free_all(&properties);
}
}
#ifdef WITH_BROKER
log__printf(NULL, MOSQ_LOG_DEBUG, "Received PUBREC from %s (Mid: %d)", mosq->id, mid);
if(reason_code < 0x80){
rc = db__message_update_outgoing(mosq, mid, mosq_ms_wait_for_pubcomp, 2);
}else{
return db__message_delete_outgoing(db, mosq, mid, mosq_ms_wait_for_pubrec, 2);
}
#else
UNUSED(db);
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PUBREC (Mid: %d)", mosq->id, mid);
if(reason_code < 0x80 || mosq->protocol != mosq_p_mqtt5){
rc = message__out_update(mosq, mid, mosq_ms_wait_for_pubcomp, 2);
}else{
if(!message__delete(mosq, mid, mosq_md_out, 2)){
/* Only inform the client the message has been sent once. */
pthread_mutex_lock(&mosq->callback_mutex);
if(mosq->on_publish_v5){
mosq->in_callback = true;
mosq->on_publish_v5(mosq, mosq->userdata, mid, reason_code, properties);
mosq->in_callback = false;
}
pthread_mutex_unlock(&mosq->callback_mutex);
}
util__increment_send_quota(mosq);
pthread_mutex_lock(&mosq->msgs_out.mutex);
message__release_to_inflight(mosq, mosq_md_out);
pthread_mutex_unlock(&mosq->msgs_out.mutex);
return MOSQ_ERR_SUCCESS;
}
#endif
if(rc == MOSQ_ERR_NOT_FOUND){
log__printf(mosq, MOSQ_LOG_WARNING, "Warning: Received PUBREC from %s for an unknown packet identifier %d.", mosq->id, mid);
}else if(rc != MOSQ_ERR_SUCCESS){
return rc;
}
rc = send__pubrel(mosq, mid);
if(rc) return rc;
return MOSQ_ERR_SUCCESS;
}

@ -0,0 +1,128 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#ifdef WITH_BROKER
# include "mosquitto_broker_internal.h"
#endif
#include "mosquitto.h"
#include "logging_mosq.h"
#include "memory_mosq.h"
#include "messages_mosq.h"
#include "mqtt_protocol.h"
#include "net_mosq.h"
#include "packet_mosq.h"
#include "read_handle.h"
#include "send_mosq.h"
#include "util_mosq.h"
int handle__pubrel(struct mosquitto_db *db, struct mosquitto *mosq)
{
uint8_t reason_code;
uint16_t mid;
#ifndef WITH_BROKER
struct mosquitto_message_all *message = NULL;
#endif
int rc;
mosquitto_property *properties = NULL;
int state;
assert(mosq);
state = mosquitto__get_state(mosq);
if(state != mosq_cs_active){
return MOSQ_ERR_PROTOCOL;
}
if(mosq->protocol != mosq_p_mqtt31){
if((mosq->in_packet.command&0x0F) != 0x02){
return MOSQ_ERR_PROTOCOL;
}
}
rc = packet__read_uint16(&mosq->in_packet, &mid);
if(rc) return rc;
if(mid == 0) return MOSQ_ERR_PROTOCOL;
if(mosq->protocol == mosq_p_mqtt5 && mosq->in_packet.remaining_length > 2){
rc = packet__read_byte(&mosq->in_packet, &reason_code);
if(rc) return rc;
if(mosq->in_packet.remaining_length > 3){
rc = property__read_all(CMD_PUBREL, &mosq->in_packet, &properties);
if(rc) return rc;
}
}
#ifdef WITH_BROKER
log__printf(NULL, MOSQ_LOG_DEBUG, "Received PUBREL from %s (Mid: %d)", mosq->id, mid);
/* Immediately free, we don't do anything with Reason String or User Property at the moment */
mosquitto_property_free_all(&properties);
rc = db__message_release_incoming(db, mosq, mid);
if(rc == MOSQ_ERR_NOT_FOUND){
/* Message not found. Still send a PUBCOMP anyway because this could be
* due to a repeated PUBREL after a client has reconnected. */
}else if(rc != MOSQ_ERR_SUCCESS){
return rc;
}
rc = send__pubcomp(mosq, mid);
if(rc) return rc;
#else
UNUSED(db);
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PUBREL (Mid: %d)", mosq->id, mid);
rc = send__pubcomp(mosq, mid);
if(rc){
message__remove(mosq, mid, mosq_md_in, &message, 2);
return rc;
}
rc = message__remove(mosq, mid, mosq_md_in, &message, 2);
if(rc){
return rc;
}else{
/* Only pass the message on if we have removed it from the queue - this
* prevents multiple callbacks for the same message. */
pthread_mutex_lock(&mosq->callback_mutex);
if(mosq->on_message){
mosq->in_callback = true;
mosq->on_message(mosq, mosq->userdata, &message->msg);
mosq->in_callback = false;
}
if(mosq->on_message_v5){
mosq->in_callback = true;
mosq->on_message_v5(mosq, mosq->userdata, &message->msg, message->properties);
mosq->in_callback = false;
}
pthread_mutex_unlock(&mosq->callback_mutex);
mosquitto_property_free_all(&properties);
message__cleanup(&message);
}
#endif
return MOSQ_ERR_SUCCESS;
}

@ -0,0 +1,101 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#ifdef WITH_BROKER
# include "mosquitto_broker_internal.h"
#endif
#include "mosquitto.h"
#include "mosquitto_internal.h"
#include "logging_mosq.h"
#include "memory_mosq.h"
#include "mqtt_protocol.h"
#include "packet_mosq.h"
#include "property_mosq.h"
#include "util_mosq.h"
int handle__suback(struct mosquitto *mosq)
{
uint16_t mid;
uint8_t qos;
int *granted_qos;
int qos_count;
int i = 0;
int rc;
mosquitto_property *properties = NULL;
int state;
assert(mosq);
state = mosquitto__get_state(mosq);
if(state != mosq_cs_active){
return MOSQ_ERR_PROTOCOL;
}
#ifdef WITH_BROKER
log__printf(NULL, MOSQ_LOG_DEBUG, "Received SUBACK from %s", mosq->id);
#else
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received SUBACK", mosq->id);
#endif
rc = packet__read_uint16(&mosq->in_packet, &mid);
if(rc) return rc;
if(mid == 0) return MOSQ_ERR_PROTOCOL;
if(mosq->protocol == mosq_p_mqtt5){
rc = property__read_all(CMD_SUBACK, &mosq->in_packet, &properties);
if(rc) return rc;
}
qos_count = mosq->in_packet.remaining_length - mosq->in_packet.pos;
granted_qos = mosquitto__malloc(qos_count*sizeof(int));
if(!granted_qos) return MOSQ_ERR_NOMEM;
while(mosq->in_packet.pos < mosq->in_packet.remaining_length){
rc = packet__read_byte(&mosq->in_packet, &qos);
if(rc){
mosquitto__free(granted_qos);
return rc;
}
granted_qos[i] = (int)qos;
i++;
}
#ifdef WITH_BROKER
/* Immediately free, we don't do anything with Reason String or User Property at the moment */
mosquitto_property_free_all(&properties);
#else
pthread_mutex_lock(&mosq->callback_mutex);
if(mosq->on_subscribe){
mosq->in_callback = true;
mosq->on_subscribe(mosq, mosq->userdata, mid, qos_count, granted_qos);
mosq->in_callback = false;
}
if(mosq->on_subscribe_v5){
mosq->in_callback = true;
mosq->on_subscribe_v5(mosq, mosq->userdata, mid, qos_count, granted_qos, properties);
mosq->in_callback = false;
}
pthread_mutex_unlock(&mosq->callback_mutex);
mosquitto_property_free_all(&properties);
#endif
mosquitto__free(granted_qos);
return MOSQ_ERR_SUCCESS;
}

@ -0,0 +1,89 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#ifdef WITH_BROKER
# include "mosquitto_broker_internal.h"
#endif
#include "mosquitto.h"
#include "logging_mosq.h"
#include "memory_mosq.h"
#include "messages_mosq.h"
#include "mqtt_protocol.h"
#include "net_mosq.h"
#include "packet_mosq.h"
#include "property_mosq.h"
#include "read_handle.h"
#include "send_mosq.h"
#include "util_mosq.h"
int handle__unsuback(struct mosquitto *mosq)
{
uint16_t mid;
int rc;
mosquitto_property *properties = NULL;
int state;
assert(mosq);
state = mosquitto__get_state(mosq);
if(state != mosq_cs_active){
return MOSQ_ERR_PROTOCOL;
}
#ifdef WITH_BROKER
log__printf(NULL, MOSQ_LOG_DEBUG, "Received UNSUBACK from %s", mosq->id);
#else
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received UNSUBACK", mosq->id);
#endif
rc = packet__read_uint16(&mosq->in_packet, &mid);
if(rc) return rc;
if(mid == 0) return MOSQ_ERR_PROTOCOL;
if(mosq->protocol == mosq_p_mqtt5){
rc = property__read_all(CMD_UNSUBACK, &mosq->in_packet, &properties);
if(rc) return rc;
}
#ifdef WITH_BROKER
/* Immediately free, we don't do anything with Reason String or User Property at the moment */
mosquitto_property_free_all(&properties);
#else
pthread_mutex_lock(&mosq->callback_mutex);
if(mosq->on_unsubscribe){
mosq->in_callback = true;
mosq->on_unsubscribe(mosq, mosq->userdata, mid);
mosq->in_callback = false;
}
if(mosq->on_unsubscribe_v5){
mosq->in_callback = true;
mosq->on_unsubscribe_v5(mosq, mosq->userdata, mid, properties);
mosq->in_callback = false;
}
pthread_mutex_unlock(&mosq->callback_mutex);
mosquitto_property_free_all(&properties);
#endif
return MOSQ_ERR_SUCCESS;
}

@ -0,0 +1,227 @@
/*
Copyright (c) 2016-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <errno.h>
#include <stdbool.h>
#include "mosquitto.h"
#include "mosquitto_internal.h"
struct userdata__callback {
const char *topic;
int (*callback)(struct mosquitto *, void *, const struct mosquitto_message *);
void *userdata;
int qos;
int rc;
};
struct userdata__simple {
struct mosquitto_message *messages;
int max_msg_count;
int message_count;
bool want_retained;
};
static void on_connect(struct mosquitto *mosq, void *obj, int rc)
{
struct userdata__callback *userdata = obj;
UNUSED(rc);
mosquitto_subscribe(mosq, NULL, userdata->topic, userdata->qos);
}
static void on_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message)
{
int rc;
struct userdata__callback *userdata = obj;
rc = userdata->callback(mosq, userdata->userdata, message);
if(rc){
mosquitto_disconnect(mosq);
}
}
static int on_message_simple(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message)
{
struct userdata__simple *userdata = obj;
int rc;
if(userdata->max_msg_count == 0){
return 0;
}
/* Don't process stale retained messages if 'want_retained' was false */
if(!userdata->want_retained && message->retain){
return 0;
}
userdata->max_msg_count--;
rc = mosquitto_message_copy(&userdata->messages[userdata->message_count], message);
if(rc){
return rc;
}
userdata->message_count++;
if(userdata->max_msg_count == 0){
mosquitto_disconnect(mosq);
}
return 0;
}
libmosq_EXPORT int mosquitto_subscribe_simple(
struct mosquitto_message **messages,
int msg_count,
bool want_retained,
const char *topic,
int qos,
const char *host,
int port,
const char *client_id,
int keepalive,
bool clean_session,
const char *username,
const char *password,
const struct libmosquitto_will *will,
const struct libmosquitto_tls *tls)
{
struct userdata__simple userdata;
int rc;
int i;
if(!topic || msg_count < 1 || !messages){
return MOSQ_ERR_INVAL;
}
*messages = NULL;
userdata.messages = calloc(sizeof(struct mosquitto_message), msg_count);
if(!userdata.messages){
return MOSQ_ERR_NOMEM;
}
userdata.message_count = 0;
userdata.max_msg_count = msg_count;
userdata.want_retained = want_retained;
rc = mosquitto_subscribe_callback(
on_message_simple, &userdata,
topic, qos,
host, port,
client_id, keepalive, clean_session,
username, password,
will, tls);
if(!rc && userdata.max_msg_count == 0){
*messages = userdata.messages;
return MOSQ_ERR_SUCCESS;
}else{
for(i=0; i<msg_count; i++){
mosquitto_message_free_contents(&userdata.messages[i]);
}
free(userdata.messages);
userdata.messages = NULL;
return rc;
}
}
libmosq_EXPORT int mosquitto_subscribe_callback(
int (*callback)(struct mosquitto *, void *, const struct mosquitto_message *),
void *userdata,
const char *topic,
int qos,
const char *host,
int port,
const char *client_id,
int keepalive,
bool clean_session,
const char *username,
const char *password,
const struct libmosquitto_will *will,
const struct libmosquitto_tls *tls)
{
struct mosquitto *mosq;
struct userdata__callback cb_userdata;
int rc;
if(!callback || !topic){
return MOSQ_ERR_INVAL;
}
cb_userdata.topic = topic;
cb_userdata.qos = qos;
cb_userdata.rc = 0;
cb_userdata.userdata = userdata;
cb_userdata.callback = callback;
mosq = mosquitto_new(client_id, clean_session, &cb_userdata);
if(!mosq){
return MOSQ_ERR_NOMEM;
}
if(will){
rc = mosquitto_will_set(mosq, will->topic, will->payloadlen, will->payload, will->qos, will->retain);
if(rc){
mosquitto_destroy(mosq);
return rc;
}
}
if(username){
rc = mosquitto_username_pw_set(mosq, username, password);
if(rc){
mosquitto_destroy(mosq);
return rc;
}
}
if(tls){
rc = mosquitto_tls_set(mosq, tls->cafile, tls->capath, tls->certfile, tls->keyfile, tls->pw_callback);
if(rc){
mosquitto_destroy(mosq);
return rc;
}
rc = mosquitto_tls_opts_set(mosq, tls->cert_reqs, tls->tls_version, tls->ciphers);
if(rc){
mosquitto_destroy(mosq);
return rc;
}
}
mosquitto_connect_callback_set(mosq, on_connect);
mosquitto_message_callback_set(mosq, on_message_callback);
rc = mosquitto_connect(mosq, host, port, keepalive);
if(rc){
mosquitto_destroy(mosq);
return rc;
}
rc = mosquitto_loop_forever(mosq, -1, 1);
mosquitto_destroy(mosq);
if(cb_userdata.rc){
rc = cb_userdata.rc;
}
//if(!rc && cb_userdata.max_msg_count == 0){
//return MOSQ_ERR_SUCCESS;
//}else{
//return rc;
//}
return MOSQ_ERR_SUCCESS;
}

@ -0,0 +1,59 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "mosquitto_internal.h"
#include "mosquitto.h"
#include "memory_mosq.h"
int log__printf(struct mosquitto *mosq, int priority, const char *fmt, ...)
{
va_list va;
char *s;
int len;
assert(mosq);
assert(fmt);
pthread_mutex_lock(&mosq->log_callback_mutex);
if(mosq->on_log){
len = strlen(fmt) + 500;
s = mosquitto__malloc(len*sizeof(char));
if(!s){
pthread_mutex_unlock(&mosq->log_callback_mutex);
return MOSQ_ERR_NOMEM;
}
va_start(va, fmt);
vsnprintf(s, len, fmt, va);
va_end(va);
s[len-1] = '\0'; /* Ensure string is null terminated. */
mosq->on_log(mosq, mosq->userdata, priority, s);
mosquitto__free(s);
}
pthread_mutex_unlock(&mosq->log_callback_mutex);
return MOSQ_ERR_SUCCESS;
}

@ -0,0 +1,23 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#ifndef LOGGING_MOSQ_H
#define LOGGING_MOSQ_H
#include "mosquitto.h"
int log__printf(struct mosquitto *mosq, int priority, const char *fmt, ...);
#endif

@ -0,0 +1,381 @@
/*
Copyright (c) 2010-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <errno.h>
#ifndef WIN32
#include <sys/select.h>
#include <time.h>
#endif
#include "mosquitto.h"
#include "mosquitto_internal.h"
#include "net_mosq.h"
#include "packet_mosq.h"
#include "socks_mosq.h"
#include "tls_mosq.h"
#include "util_mosq.h"
#if !defined(WIN32) && !defined(__SYMBIAN32__)
#define HAVE_PSELECT
#endif
int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets)
{
#ifdef HAVE_PSELECT
struct timespec local_timeout;
#else
struct timeval local_timeout;
#endif
fd_set readfds, writefds;
int fdcount;
int rc;
char pairbuf;
int maxfd = 0;
time_t now;
#ifdef WITH_SRV
int state;
#endif
if(!mosq || max_packets < 1) return MOSQ_ERR_INVAL;
#ifndef WIN32
if(mosq->sock >= FD_SETSIZE || mosq->sockpairR >= FD_SETSIZE){
return MOSQ_ERR_INVAL;
}
#endif
FD_ZERO(&readfds);
FD_ZERO(&writefds);
if(mosq->sock != INVALID_SOCKET){
maxfd = mosq->sock;
FD_SET(mosq->sock, &readfds);
pthread_mutex_lock(&mosq->current_out_packet_mutex);
pthread_mutex_lock(&mosq->out_packet_mutex);
if(mosq->out_packet || mosq->current_out_packet){
FD_SET(mosq->sock, &writefds);
}
#ifdef WITH_TLS
if(mosq->ssl){
if(mosq->want_write){
FD_SET(mosq->sock, &writefds);
}else if(mosq->want_connect){
/* Remove possible FD_SET from above, we don't want to check
* for writing if we are still connecting, unless want_write is
* definitely set. The presence of outgoing packets does not
* matter yet. */
FD_CLR(mosq->sock, &writefds);
}
}
#endif
pthread_mutex_unlock(&mosq->out_packet_mutex);
pthread_mutex_unlock(&mosq->current_out_packet_mutex);
}else{
#ifdef WITH_SRV
if(mosq->achan){
state = mosquitto__get_state(mosq);
if(state == mosq_cs_connect_srv){
rc = ares_fds(mosq->achan, &readfds, &writefds);
if(rc > maxfd){
maxfd = rc;
}
}else{
return MOSQ_ERR_NO_CONN;
}
}
#else
return MOSQ_ERR_NO_CONN;
#endif
}
if(mosq->sockpairR != INVALID_SOCKET){
/* sockpairR is used to break out of select() before the timeout, on a
* call to publish() etc. */
FD_SET(mosq->sockpairR, &readfds);
if(mosq->sockpairR > maxfd){
maxfd = mosq->sockpairR;
}
}
if(timeout < 0){
timeout = 1000;
}
now = mosquitto_time();
if(mosq->next_msg_out && now + timeout/1000 > mosq->next_msg_out){
timeout = (mosq->next_msg_out - now)*1000;
}
if(timeout < 0){
/* There has been a delay somewhere which means we should have already
* sent a message. */
timeout = 0;
}
local_timeout.tv_sec = timeout/1000;
#ifdef HAVE_PSELECT
local_timeout.tv_nsec = (timeout-local_timeout.tv_sec*1000)*1e6;
#else
local_timeout.tv_usec = (timeout-local_timeout.tv_sec*1000)*1000;
#endif
#ifdef HAVE_PSELECT
fdcount = pselect(maxfd+1, &readfds, &writefds, NULL, &local_timeout, NULL);
#else
fdcount = select(maxfd+1, &readfds, &writefds, NULL, &local_timeout);
#endif
if(fdcount == -1){
#ifdef WIN32
errno = WSAGetLastError();
#endif
if(errno == EINTR){
return MOSQ_ERR_SUCCESS;
}else{
return MOSQ_ERR_ERRNO;
}
}else{
if(mosq->sock != INVALID_SOCKET){
if(FD_ISSET(mosq->sock, &readfds)){
rc = mosquitto_loop_read(mosq, max_packets);
if(rc || mosq->sock == INVALID_SOCKET){
return rc;
}
}
if(mosq->sockpairR != INVALID_SOCKET && FD_ISSET(mosq->sockpairR, &readfds)){
#ifndef WIN32
if(read(mosq->sockpairR, &pairbuf, 1) == 0){
}
#else
recv(mosq->sockpairR, &pairbuf, 1, 0);
#endif
/* Fake write possible, to stimulate output write even though
* we didn't ask for it, because at that point the publish or
* other command wasn't present. */
if(mosq->sock != INVALID_SOCKET)
FD_SET(mosq->sock, &writefds);
}
if(mosq->sock != INVALID_SOCKET && FD_ISSET(mosq->sock, &writefds)){
#ifdef WITH_TLS
if(mosq->want_connect){
rc = net__socket_connect_tls(mosq);
if(rc) return rc;
}else
#endif
{
rc = mosquitto_loop_write(mosq, max_packets);
if(rc || mosq->sock == INVALID_SOCKET){
return rc;
}
}
}
}
#ifdef WITH_SRV
if(mosq->achan){
ares_process(mosq->achan, &readfds, &writefds);
}
#endif
}
return mosquitto_loop_misc(mosq);
}
int mosquitto_loop_forever(struct mosquitto *mosq, int timeout, int max_packets)
{
int run = 1;
int rc;
unsigned long reconnect_delay;
#ifndef WIN32
struct timespec req, rem;
#endif
int state;
if(!mosq) return MOSQ_ERR_INVAL;
mosq->reconnects = 0;
while(run){
do{
rc = mosquitto_loop(mosq, timeout, max_packets);
}while(run && rc == MOSQ_ERR_SUCCESS);
/* Quit after fatal errors. */
switch(rc){
case MOSQ_ERR_NOMEM:
case MOSQ_ERR_PROTOCOL:
case MOSQ_ERR_INVAL:
case MOSQ_ERR_NOT_FOUND:
case MOSQ_ERR_TLS:
case MOSQ_ERR_PAYLOAD_SIZE:
case MOSQ_ERR_NOT_SUPPORTED:
case MOSQ_ERR_AUTH:
case MOSQ_ERR_ACL_DENIED:
case MOSQ_ERR_UNKNOWN:
case MOSQ_ERR_EAI:
case MOSQ_ERR_PROXY:
return rc;
case MOSQ_ERR_ERRNO:
break;
}
if(errno == EPROTO){
return rc;
}
do{
rc = MOSQ_ERR_SUCCESS;
state = mosquitto__get_state(mosq);
if(state == mosq_cs_disconnecting || state == mosq_cs_disconnected){
run = 0;
}else{
if(mosq->reconnect_delay_max > mosq->reconnect_delay){
if(mosq->reconnect_exponential_backoff){
reconnect_delay = mosq->reconnect_delay*(mosq->reconnects+1)*(mosq->reconnects+1);
}else{
reconnect_delay = mosq->reconnect_delay*(mosq->reconnects+1);
}
}else{
reconnect_delay = mosq->reconnect_delay;
}
if(reconnect_delay > mosq->reconnect_delay_max){
reconnect_delay = mosq->reconnect_delay_max;
}else{
mosq->reconnects++;
}
#ifdef WIN32
Sleep(reconnect_delay*1000);
#else
req.tv_sec = reconnect_delay;
req.tv_nsec = 0;
while(nanosleep(&req, &rem) == -1 && errno == EINTR){
req = rem;
}
#endif
state = mosquitto__get_state(mosq);
if(state == mosq_cs_disconnecting || state == mosq_cs_disconnected){
run = 0;
}else{
rc = mosquitto_reconnect(mosq);
}
}
}while(run && rc != MOSQ_ERR_SUCCESS);
}
return rc;
}
int mosquitto_loop_misc(struct mosquitto *mosq)
{
if(!mosq) return MOSQ_ERR_INVAL;
if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN;
return mosquitto__check_keepalive(mosq);
}
static int mosquitto__loop_rc_handle(struct mosquitto *mosq, int rc)
{
int state;
if(rc){
net__socket_close(mosq);
state = mosquitto__get_state(mosq);
if(state == mosq_cs_disconnecting || state == mosq_cs_disconnected){
rc = MOSQ_ERR_SUCCESS;
}
pthread_mutex_lock(&mosq->callback_mutex);
if(mosq->on_disconnect){
mosq->in_callback = true;
mosq->on_disconnect(mosq, mosq->userdata, rc);
mosq->in_callback = false;
}
if(mosq->on_disconnect_v5){
mosq->in_callback = true;
mosq->on_disconnect_v5(mosq, mosq->userdata, rc, NULL);
mosq->in_callback = false;
}
pthread_mutex_unlock(&mosq->callback_mutex);
}
return rc;
}
int mosquitto_loop_read(struct mosquitto *mosq, int max_packets)
{
int rc;
int i;
if(max_packets < 1) return MOSQ_ERR_INVAL;
#ifdef WITH_TLS
if(mosq->want_connect){
return net__socket_connect_tls(mosq);
}
#endif
pthread_mutex_lock(&mosq->msgs_out.mutex);
max_packets = mosq->msgs_out.queue_len;
pthread_mutex_unlock(&mosq->msgs_out.mutex);
pthread_mutex_lock(&mosq->msgs_in.mutex);
max_packets += mosq->msgs_in.queue_len;
pthread_mutex_unlock(&mosq->msgs_in.mutex);
if(max_packets < 1) max_packets = 1;
/* Queue len here tells us how many messages are awaiting processing and
* have QoS > 0. We should try to deal with that many in this loop in order
* to keep up. */
for(i=0; i<max_packets || SSL_DATA_PENDING(mosq); i++){
#ifdef WITH_SOCKS
if(mosq->socks5_host){
rc = socks5__read(mosq);
}else
#endif
{
rc = packet__read(mosq);
}
if(rc || errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){
return mosquitto__loop_rc_handle(mosq, rc);
}
}
return rc;
}
int mosquitto_loop_write(struct mosquitto *mosq, int max_packets)
{
int rc;
int i;
if(max_packets < 1) return MOSQ_ERR_INVAL;
pthread_mutex_lock(&mosq->msgs_out.mutex);
max_packets = mosq->msgs_out.queue_len;
pthread_mutex_unlock(&mosq->msgs_out.mutex);
pthread_mutex_lock(&mosq->msgs_in.mutex);
max_packets += mosq->msgs_in.queue_len;
pthread_mutex_unlock(&mosq->msgs_in.mutex);
if(max_packets < 1) max_packets = 1;
/* Queue len here tells us how many messages are awaiting processing and
* have QoS > 0. We should try to deal with that many in this loop in order
* to keep up. */
for(i=0; i<max_packets; i++){
rc = packet__write(mosq);
if(rc || errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){
return mosquitto__loop_rc_handle(mosq, rc);
}
}
return rc;
}

@ -0,0 +1,160 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include "memory_mosq.h"
#ifdef REAL_WITH_MEMORY_TRACKING
# if defined(__APPLE__)
# include <malloc/malloc.h>
# define malloc_usable_size malloc_size
# elif defined(__FreeBSD__)
# include <malloc_np.h>
# else
# include <malloc.h>
# endif
#endif
#ifdef REAL_WITH_MEMORY_TRACKING
static unsigned long memcount = 0;
static unsigned long max_memcount = 0;
#endif
#ifdef WITH_BROKER
static size_t mem_limit = 0;
void memory__set_limit(size_t lim)
{
mem_limit = lim;
}
#endif
void *mosquitto__calloc(size_t nmemb, size_t size)
{
#ifdef REAL_WITH_MEMORY_TRACKING
if(mem_limit && memcount + size > mem_limit){
return NULL;
}
#endif
void *mem = calloc(nmemb, size);
#ifdef REAL_WITH_MEMORY_TRACKING
if(mem){
memcount += malloc_usable_size(mem);
if(memcount > max_memcount){
max_memcount = memcount;
}
}
#endif
return mem;
}
void mosquitto__free(void *mem)
{
#ifdef REAL_WITH_MEMORY_TRACKING
if(!mem){
return;
}
memcount -= malloc_usable_size(mem);
#endif
free(mem);
}
void *mosquitto__malloc(size_t size)
{
#ifdef REAL_WITH_MEMORY_TRACKING
if(mem_limit && memcount + size > mem_limit){
return NULL;
}
#endif
void *mem = malloc(size);
#ifdef REAL_WITH_MEMORY_TRACKING
if(mem){
memcount += malloc_usable_size(mem);
if(memcount > max_memcount){
max_memcount = memcount;
}
}
#endif
return mem;
}
#ifdef REAL_WITH_MEMORY_TRACKING
unsigned long mosquitto__memory_used(void)
{
return memcount;
}
unsigned long mosquitto__max_memory_used(void)
{
return max_memcount;
}
#endif
void *mosquitto__realloc(void *ptr, size_t size)
{
#ifdef REAL_WITH_MEMORY_TRACKING
if(mem_limit && memcount + size > mem_limit){
return NULL;
}
#endif
void *mem;
#ifdef REAL_WITH_MEMORY_TRACKING
if(ptr){
memcount -= malloc_usable_size(ptr);
}
#endif
mem = realloc(ptr, size);
#ifdef REAL_WITH_MEMORY_TRACKING
if(mem){
memcount += malloc_usable_size(mem);
if(memcount > max_memcount){
max_memcount = memcount;
}
}
#endif
return mem;
}
char *mosquitto__strdup(const char *s)
{
#ifdef REAL_WITH_MEMORY_TRACKING
if(mem_limit && memcount + strlen(s) > mem_limit){
return NULL;
}
#endif
char *str = strdup(s);
#ifdef REAL_WITH_MEMORY_TRACKING
if(str){
memcount += malloc_usable_size(str);
if(memcount > max_memcount){
max_memcount = memcount;
}
}
#endif
return str;
}

@ -0,0 +1,41 @@
/*
Copyright (c) 2010-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#ifndef MEMORY_MOSQ_H
#define MEMORY_MOSQ_H
#include <stdio.h>
#include <sys/types.h>
#if defined(WITH_MEMORY_TRACKING) && defined(WITH_BROKER) && defined(__GLIBC__)
#define REAL_WITH_MEMORY_TRACKING
#endif
void *mosquitto__calloc(size_t nmemb, size_t size);
void mosquitto__free(void *mem);
void *mosquitto__malloc(size_t size);
#ifdef REAL_WITH_MEMORY_TRACKING
unsigned long mosquitto__memory_used(void);
unsigned long mosquitto__max_memory_used(void);
#endif
void *mosquitto__realloc(void *ptr, size_t size);
char *mosquitto__strdup(const char *s);
#ifdef WITH_BROKER
void memory__set_limit(size_t lim);
#endif
#endif

@ -0,0 +1,345 @@
/*
Copyright (c) 2010-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <utlist.h>
#include "mosquitto_internal.h"
#include "mosquitto.h"
#include "memory_mosq.h"
#include "messages_mosq.h"
#include "send_mosq.h"
#include "time_mosq.h"
#include "util_mosq.h"
void message__cleanup(struct mosquitto_message_all **message)
{
struct mosquitto_message_all *msg;
if(!message || !*message) return;
msg = *message;
mosquitto__free(msg->msg.topic);
mosquitto__free(msg->msg.payload);
mosquitto_property_free_all(&msg->properties);
mosquitto__free(msg);
}
void message__cleanup_all(struct mosquitto *mosq)
{
struct mosquitto_message_all *tail, *tmp;
assert(mosq);
DL_FOREACH_SAFE(mosq->msgs_in.inflight, tail, tmp){
DL_DELETE(mosq->msgs_in.inflight, tail);
message__cleanup(&tail);
}
DL_FOREACH_SAFE(mosq->msgs_out.inflight, tail, tmp){
DL_DELETE(mosq->msgs_out.inflight, tail);
message__cleanup(&tail);
}
}
int mosquitto_message_copy(struct mosquitto_message *dst, const struct mosquitto_message *src)
{
if(!dst || !src) return MOSQ_ERR_INVAL;
dst->mid = src->mid;
dst->topic = mosquitto__strdup(src->topic);
if(!dst->topic) return MOSQ_ERR_NOMEM;
dst->qos = src->qos;
dst->retain = src->retain;
if(src->payloadlen){
dst->payload = mosquitto__calloc(src->payloadlen+1, sizeof(uint8_t));
if(!dst->payload){
mosquitto__free(dst->topic);
return MOSQ_ERR_NOMEM;
}
memcpy(dst->payload, src->payload, src->payloadlen);
dst->payloadlen = src->payloadlen;
}else{
dst->payloadlen = 0;
dst->payload = NULL;
}
return MOSQ_ERR_SUCCESS;
}
int message__delete(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir, int qos)
{
struct mosquitto_message_all *message;
int rc;
assert(mosq);
rc = message__remove(mosq, mid, dir, &message, qos);
if(rc == MOSQ_ERR_SUCCESS){
message__cleanup(&message);
}
return rc;
}
void mosquitto_message_free(struct mosquitto_message **message)
{
struct mosquitto_message *msg;
if(!message || !*message) return;
msg = *message;
mosquitto__free(msg->topic);
mosquitto__free(msg->payload);
mosquitto__free(msg);
}
void mosquitto_message_free_contents(struct mosquitto_message *message)
{
if(!message) return;
mosquitto__free(message->topic);
mosquitto__free(message->payload);
}
int message__queue(struct mosquitto *mosq, struct mosquitto_message_all *message, enum mosquitto_msg_direction dir)
{
/* mosq->*_message_mutex should be locked before entering this function */
assert(mosq);
assert(message);
assert(message->msg.qos != 0);
if(dir == mosq_md_out){
DL_APPEND(mosq->msgs_out.inflight, message);
mosq->msgs_out.queue_len++;
}else{
DL_APPEND(mosq->msgs_in.inflight, message);
mosq->msgs_in.queue_len++;
}
return message__release_to_inflight(mosq, dir);
}
void message__reconnect_reset(struct mosquitto *mosq)
{
struct mosquitto_message_all *message, *tmp;
assert(mosq);
pthread_mutex_lock(&mosq->msgs_in.mutex);
mosq->msgs_in.inflight_quota = mosq->msgs_in.inflight_maximum;
mosq->msgs_in.queue_len = 0;
DL_FOREACH_SAFE(mosq->msgs_in.inflight, message, tmp){
mosq->msgs_in.queue_len++;
message->timestamp = 0;
if(message->msg.qos != 2){
DL_DELETE(mosq->msgs_in.inflight, message);
message__cleanup(&message);
}else{
/* Message state can be preserved here because it should match
* whatever the client has got. */
util__decrement_receive_quota(mosq);
}
}
pthread_mutex_unlock(&mosq->msgs_in.mutex);
pthread_mutex_lock(&mosq->msgs_out.mutex);
mosq->msgs_out.inflight_quota = mosq->msgs_out.inflight_maximum;
mosq->msgs_out.queue_len = 0;
DL_FOREACH_SAFE(mosq->msgs_out.inflight, message, tmp){
mosq->msgs_out.queue_len++;
message->timestamp = 0;
if(mosq->msgs_out.inflight_quota != 0){
util__decrement_send_quota(mosq);
if(message->msg.qos == 1){
message->state = mosq_ms_publish_qos1;
}else if(message->msg.qos == 2){
if(message->state == mosq_ms_wait_for_pubrec){
message->state = mosq_ms_publish_qos2;
}else if(message->state == mosq_ms_wait_for_pubcomp){
message->state = mosq_ms_resend_pubrel;
}
/* Should be able to preserve state. */
}
}else{
message->state = mosq_ms_invalid;
}
}
pthread_mutex_unlock(&mosq->msgs_out.mutex);
}
int message__release_to_inflight(struct mosquitto *mosq, enum mosquitto_msg_direction dir)
{
/* mosq->*_message_mutex should be locked before entering this function */
struct mosquitto_message_all *cur, *tmp;
int rc = MOSQ_ERR_SUCCESS;
if(dir == mosq_md_out){
DL_FOREACH_SAFE(mosq->msgs_out.inflight, cur, tmp){
if(mosq->msgs_out.inflight_quota > 0){
if(cur->msg.qos > 0 && cur->state == mosq_ms_invalid){
if(cur->msg.qos == 1){
cur->state = mosq_ms_wait_for_puback;
}else if(cur->msg.qos == 2){
cur->state = mosq_ms_wait_for_pubrec;
}
rc = send__publish(mosq, cur->msg.mid, cur->msg.topic, cur->msg.payloadlen, cur->msg.payload, cur->msg.qos, cur->msg.retain, cur->dup, cur->properties, NULL, 0);
if(rc){
return rc;
}
util__decrement_send_quota(mosq);
}
}else{
return MOSQ_ERR_SUCCESS;
}
}
}
return rc;
}
int message__remove(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir, struct mosquitto_message_all **message, int qos)
{
struct mosquitto_message_all *cur, *tmp;
bool found = false;
assert(mosq);
assert(message);
if(dir == mosq_md_out){
pthread_mutex_lock(&mosq->msgs_out.mutex);
DL_FOREACH_SAFE(mosq->msgs_out.inflight, cur, tmp){
if(found == false && cur->msg.mid == mid){
if(cur->msg.qos != qos){
pthread_mutex_unlock(&mosq->msgs_out.mutex);
return MOSQ_ERR_PROTOCOL;
}
DL_DELETE(mosq->msgs_out.inflight, cur);
*message = cur;
mosq->msgs_out.queue_len--;
found = true;
break;
}
}
pthread_mutex_unlock(&mosq->msgs_out.mutex);
if(found){
return MOSQ_ERR_SUCCESS;
}else{
return MOSQ_ERR_NOT_FOUND;
}
}else{
pthread_mutex_lock(&mosq->msgs_in.mutex);
DL_FOREACH_SAFE(mosq->msgs_in.inflight, cur, tmp){
if(cur->msg.mid == mid){
if(cur->msg.qos != qos){
pthread_mutex_unlock(&mosq->msgs_in.mutex);
return MOSQ_ERR_PROTOCOL;
}
DL_DELETE(mosq->msgs_in.inflight, cur);
*message = cur;
mosq->msgs_in.queue_len--;
found = true;
break;
}
}
pthread_mutex_unlock(&mosq->msgs_in.mutex);
if(found){
return MOSQ_ERR_SUCCESS;
}else{
return MOSQ_ERR_NOT_FOUND;
}
}
}
void message__retry_check(struct mosquitto *mosq)
{
struct mosquitto_message_all *msg;
time_t now = mosquitto_time();
assert(mosq);
#ifdef WITH_THREADING
pthread_mutex_lock(&mosq->msgs_out.mutex);
#endif
DL_FOREACH(mosq->msgs_out.inflight, msg){
switch(msg->state){
case mosq_ms_publish_qos1:
case mosq_ms_publish_qos2:
msg->timestamp = now;
msg->dup = true;
send__publish(mosq, msg->msg.mid, msg->msg.topic, msg->msg.payloadlen, msg->msg.payload, msg->msg.qos, msg->msg.retain, msg->dup, msg->properties, NULL, 0);
break;
case mosq_ms_wait_for_pubrel:
msg->timestamp = now;
msg->dup = true;
send__pubrec(mosq, msg->msg.mid, 0);
break;
case mosq_ms_resend_pubrel:
case mosq_ms_wait_for_pubcomp:
msg->timestamp = now;
msg->dup = true;
send__pubrel(mosq, msg->msg.mid);
break;
default:
break;
}
}
#ifdef WITH_THREADING
pthread_mutex_unlock(&mosq->msgs_out.mutex);
#endif
}
void mosquitto_message_retry_set(struct mosquitto *mosq, unsigned int message_retry)
{
UNUSED(mosq);
UNUSED(message_retry);
}
int message__out_update(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_state state, int qos)
{
struct mosquitto_message_all *message, *tmp;
assert(mosq);
pthread_mutex_lock(&mosq->msgs_out.mutex);
DL_FOREACH_SAFE(mosq->msgs_out.inflight, message, tmp){
if(message->msg.mid == mid){
if(message->msg.qos != qos){
pthread_mutex_unlock(&mosq->msgs_out.mutex);
return MOSQ_ERR_PROTOCOL;
}
message->state = state;
message->timestamp = mosquitto_time();
pthread_mutex_unlock(&mosq->msgs_out.mutex);
return MOSQ_ERR_SUCCESS;
}
}
pthread_mutex_unlock(&mosq->msgs_out.mutex);
return MOSQ_ERR_NOT_FOUND;
}
int mosquitto_max_inflight_messages_set(struct mosquitto *mosq, unsigned int max_inflight_messages)
{
return mosquitto_int_option(mosq, MOSQ_OPT_SEND_MAXIMUM, max_inflight_messages);
}

@ -0,0 +1,32 @@
/*
Copyright (c) 2010-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#ifndef MESSAGES_MOSQ_H
#define MESSAGES_MOSQ_H
#include "mosquitto_internal.h"
#include "mosquitto.h"
void message__cleanup_all(struct mosquitto *mosq);
void message__cleanup(struct mosquitto_message_all **message);
int message__delete(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir, int qos);
int message__queue(struct mosquitto *mosq, struct mosquitto_message_all *message, enum mosquitto_msg_direction dir);
void message__reconnect_reset(struct mosquitto *mosq);
int message__release_to_inflight(struct mosquitto *mosq, enum mosquitto_msg_direction dir);
int message__remove(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir, struct mosquitto_message_all **message, int qos);
void message__retry_check(struct mosquitto *mosq);
int message__out_update(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_state state, int qos);
#endif

@ -0,0 +1,613 @@
/*
Copyright (c) 2010-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <errno.h>
#include <signal.h>
#include <string.h>
#ifndef WIN32
#include <sys/time.h>
#include <strings.h>
#endif
#include "mosquitto.h"
#include "mosquitto_internal.h"
#include "memory_mosq.h"
#include "messages_mosq.h"
#include "mqtt_protocol.h"
#include "net_mosq.h"
#include "packet_mosq.h"
#include "will_mosq.h"
void mosquitto__destroy(struct mosquitto *mosq);
int mosquitto_lib_version(int *major, int *minor, int *revision)
{
if(major) *major = LIBMOSQUITTO_MAJOR;
if(minor) *minor = LIBMOSQUITTO_MINOR;
if(revision) *revision = LIBMOSQUITTO_REVISION;
return LIBMOSQUITTO_VERSION_NUMBER;
}
int mosquitto_lib_init(void)
{
#ifdef WIN32
srand(GetTickCount64());
#elif _POSIX_TIMERS>0 && defined(_POSIX_MONOTONIC_CLOCK)
struct timespec tp;
clock_gettime(CLOCK_MONOTONIC, &tp);
srand(tp.tv_nsec);
#elif defined(__APPLE__)
uint64_t ticks;
ticks = mach_absolute_time();
srand((unsigned int)ticks);
#else
struct timeval tv;
gettimeofday(&tv, NULL);
srand(tv.tv_sec*1000 + tv.tv_usec/1000);
#endif
return net__init();
}
int mosquitto_lib_cleanup(void)
{
net__cleanup();
return MOSQ_ERR_SUCCESS;
}
struct mosquitto *mosquitto_new(const char *id, bool clean_start, void *userdata)
{
struct mosquitto *mosq = NULL;
int rc;
if(clean_start == false && id == NULL){
errno = EINVAL;
return NULL;
}
#ifndef WIN32
signal(SIGPIPE, SIG_IGN);
#endif
mosq = (struct mosquitto *)mosquitto__calloc(1, sizeof(struct mosquitto));
if(mosq){
mosq->sock = INVALID_SOCKET;
mosq->sockpairR = INVALID_SOCKET;
mosq->sockpairW = INVALID_SOCKET;
#ifdef WITH_THREADING
mosq->thread_id = pthread_self();
#endif
rc = mosquitto_reinitialise(mosq, id, clean_start, userdata);
if(rc){
mosquitto_destroy(mosq);
if(rc == MOSQ_ERR_INVAL){
errno = EINVAL;
}else if(rc == MOSQ_ERR_NOMEM){
errno = ENOMEM;
}
return NULL;
}
}else{
errno = ENOMEM;
}
return mosq;
}
int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_start, void *userdata)
{
if(!mosq) return MOSQ_ERR_INVAL;
if(clean_start == false && id == NULL){
return MOSQ_ERR_INVAL;
}
mosquitto__destroy(mosq);
memset(mosq, 0, sizeof(struct mosquitto));
if(userdata){
mosq->userdata = userdata;
}else{
mosq->userdata = mosq;
}
mosq->protocol = mosq_p_mqtt311;
mosq->sock = INVALID_SOCKET;
mosq->sockpairR = INVALID_SOCKET;
mosq->sockpairW = INVALID_SOCKET;
mosq->keepalive = 60;
mosq->clean_start = clean_start;
if(id){
if(STREMPTY(id)){
return MOSQ_ERR_INVAL;
}
if(mosquitto_validate_utf8(id, strlen(id))){
return MOSQ_ERR_MALFORMED_UTF8;
}
mosq->id = mosquitto__strdup(id);
}
mosq->in_packet.payload = NULL;
packet__cleanup(&mosq->in_packet);
mosq->out_packet = NULL;
mosq->current_out_packet = NULL;
mosq->last_msg_in = mosquitto_time();
mosq->next_msg_out = mosquitto_time() + mosq->keepalive;
mosq->ping_t = 0;
mosq->last_mid = 0;
mosq->state = mosq_cs_new;
mosq->maximum_qos = 2;
mosq->msgs_in.inflight_maximum = 20;
mosq->msgs_out.inflight_maximum = 20;
mosq->msgs_in.inflight_quota = 20;
mosq->msgs_out.inflight_quota = 20;
mosq->will = NULL;
mosq->on_connect = NULL;
mosq->on_publish = NULL;
mosq->on_message = NULL;
mosq->on_subscribe = NULL;
mosq->on_unsubscribe = NULL;
mosq->host = NULL;
mosq->port = 1883;
mosq->in_callback = false;
mosq->reconnect_delay = 1;
mosq->reconnect_delay_max = 1;
mosq->reconnect_exponential_backoff = false;
mosq->threaded = mosq_ts_none;
#ifdef WITH_TLS
mosq->ssl = NULL;
mosq->ssl_ctx = NULL;
mosq->tls_cert_reqs = SSL_VERIFY_PEER;
mosq->tls_insecure = false;
mosq->want_write = false;
mosq->tls_ocsp_required = false;
#endif
#ifdef WITH_THREADING
pthread_mutex_init(&mosq->callback_mutex, NULL);
pthread_mutex_init(&mosq->log_callback_mutex, NULL);
pthread_mutex_init(&mosq->state_mutex, NULL);
pthread_mutex_init(&mosq->out_packet_mutex, NULL);
pthread_mutex_init(&mosq->current_out_packet_mutex, NULL);
pthread_mutex_init(&mosq->msgtime_mutex, NULL);
pthread_mutex_init(&mosq->msgs_in.mutex, NULL);
pthread_mutex_init(&mosq->msgs_out.mutex, NULL);
pthread_mutex_init(&mosq->mid_mutex, NULL);
mosq->thread_id = pthread_self();
#endif
return MOSQ_ERR_SUCCESS;
}
void mosquitto__destroy(struct mosquitto *mosq)
{
struct mosquitto__packet *packet;
if(!mosq) return;
#ifdef WITH_THREADING
# ifdef HAVE_PTHREAD_CANCEL
if(mosq->threaded == mosq_ts_self && !pthread_equal(mosq->thread_id, pthread_self())){
pthread_cancel(mosq->thread_id);
pthread_join(mosq->thread_id, NULL);
mosq->threaded = mosq_ts_none;
}
# endif
if(mosq->id){
/* If mosq->id is not NULL then the client has already been initialised
* and so the mutexes need destroying. If mosq->id is NULL, the mutexes
* haven't been initialised. */
pthread_mutex_destroy(&mosq->callback_mutex);
pthread_mutex_destroy(&mosq->log_callback_mutex);
pthread_mutex_destroy(&mosq->state_mutex);
pthread_mutex_destroy(&mosq->out_packet_mutex);
pthread_mutex_destroy(&mosq->current_out_packet_mutex);
pthread_mutex_destroy(&mosq->msgtime_mutex);
pthread_mutex_destroy(&mosq->msgs_in.mutex);
pthread_mutex_destroy(&mosq->msgs_out.mutex);
pthread_mutex_destroy(&mosq->mid_mutex);
}
#endif
if(mosq->sock != INVALID_SOCKET){
net__socket_close(mosq);
}
message__cleanup_all(mosq);
will__clear(mosq);
#ifdef WITH_TLS
if(mosq->ssl){
SSL_free(mosq->ssl);
}
if(mosq->ssl_ctx){
SSL_CTX_free(mosq->ssl_ctx);
}
mosquitto__free(mosq->tls_cafile);
mosquitto__free(mosq->tls_capath);
mosquitto__free(mosq->tls_certfile);
mosquitto__free(mosq->tls_keyfile);
if(mosq->tls_pw_callback) mosq->tls_pw_callback = NULL;
mosquitto__free(mosq->tls_version);
mosquitto__free(mosq->tls_ciphers);
mosquitto__free(mosq->tls_psk);
mosquitto__free(mosq->tls_psk_identity);
mosquitto__free(mosq->tls_alpn);
#endif
mosquitto__free(mosq->address);
mosq->address = NULL;
mosquitto__free(mosq->id);
mosq->id = NULL;
mosquitto__free(mosq->username);
mosq->username = NULL;
mosquitto__free(mosq->password);
mosq->password = NULL;
mosquitto__free(mosq->host);
mosq->host = NULL;
mosquitto__free(mosq->bind_address);
mosq->bind_address = NULL;
/* Out packet cleanup */
if(mosq->out_packet && !mosq->current_out_packet){
mosq->current_out_packet = mosq->out_packet;
mosq->out_packet = mosq->out_packet->next;
}
while(mosq->current_out_packet){
packet = mosq->current_out_packet;
/* Free data and reset values */
mosq->current_out_packet = mosq->out_packet;
if(mosq->out_packet){
mosq->out_packet = mosq->out_packet->next;
}
packet__cleanup(packet);
mosquitto__free(packet);
}
packet__cleanup(&mosq->in_packet);
if(mosq->sockpairR != INVALID_SOCKET){
COMPAT_CLOSE(mosq->sockpairR);
mosq->sockpairR = INVALID_SOCKET;
}
if(mosq->sockpairW != INVALID_SOCKET){
COMPAT_CLOSE(mosq->sockpairW);
mosq->sockpairW = INVALID_SOCKET;
}
}
void mosquitto_destroy(struct mosquitto *mosq)
{
if(!mosq) return;
mosquitto__destroy(mosq);
mosquitto__free(mosq);
}
int mosquitto_socket(struct mosquitto *mosq)
{
if(!mosq) return INVALID_SOCKET;
return mosq->sock;
}
bool mosquitto_want_write(struct mosquitto *mosq)
{
bool result = false;
if(mosq->out_packet || mosq->current_out_packet){
result = true;
}
#ifdef WITH_TLS
if(mosq->ssl){
if (mosq->want_write) {
result = true;
}else if(mosq->want_connect){
result = false;
}
}
#endif
return result;
}
const char *mosquitto_strerror(int mosq_errno)
{
switch(mosq_errno){
case MOSQ_ERR_AUTH_CONTINUE:
return "Continue with authentication.";
case MOSQ_ERR_NO_SUBSCRIBERS:
return "No subscribers.";
case MOSQ_ERR_SUB_EXISTS:
return "Subscription already exists.";
case MOSQ_ERR_CONN_PENDING:
return "Connection pending.";
case MOSQ_ERR_SUCCESS:
return "No error.";
case MOSQ_ERR_NOMEM:
return "Out of memory.";
case MOSQ_ERR_PROTOCOL:
return "A network protocol error occurred when communicating with the broker.";
case MOSQ_ERR_INVAL:
return "Invalid function arguments provided.";
case MOSQ_ERR_NO_CONN:
return "The client is not currently connected.";
case MOSQ_ERR_CONN_REFUSED:
return "The connection was refused.";
case MOSQ_ERR_NOT_FOUND:
return "Message not found (internal error).";
case MOSQ_ERR_CONN_LOST:
return "The connection was lost.";
case MOSQ_ERR_TLS:
return "A TLS error occurred.";
case MOSQ_ERR_PAYLOAD_SIZE:
return "Payload too large.";
case MOSQ_ERR_NOT_SUPPORTED:
return "This feature is not supported.";
case MOSQ_ERR_AUTH:
return "Authorisation failed.";
case MOSQ_ERR_ACL_DENIED:
return "Access denied by ACL.";
case MOSQ_ERR_UNKNOWN:
return "Unknown error.";
case MOSQ_ERR_ERRNO:
return strerror(errno);
case MOSQ_ERR_EAI:
return "Lookup error.";
case MOSQ_ERR_PROXY:
return "Proxy error.";
case MOSQ_ERR_MALFORMED_UTF8:
return "Malformed UTF-8";
case MOSQ_ERR_DUPLICATE_PROPERTY:
return "Duplicate property in property list";
case MOSQ_ERR_TLS_HANDSHAKE:
return "TLS handshake failed.";
case MOSQ_ERR_QOS_NOT_SUPPORTED:
return "Requested QoS not supported on server.";
case MOSQ_ERR_OVERSIZE_PACKET:
return "Packet larger than supported by the server.";
case MOSQ_ERR_OCSP:
return "OCSP error.";
default:
return "Unknown error.";
}
}
const char *mosquitto_connack_string(int connack_code)
{
switch(connack_code){
case 0:
return "Connection Accepted.";
case 1:
return "Connection Refused: unacceptable protocol version.";
case 2:
return "Connection Refused: identifier rejected.";
case 3:
return "Connection Refused: broker unavailable.";
case 4:
return "Connection Refused: bad user name or password.";
case 5:
return "Connection Refused: not authorised.";
default:
return "Connection Refused: unknown reason.";
}
}
const char *mosquitto_reason_string(int reason_code)
{
switch(reason_code){
case MQTT_RC_SUCCESS:
return "Success";
case MQTT_RC_GRANTED_QOS1:
return "Granted QoS 1";
case MQTT_RC_GRANTED_QOS2:
return "Granted QoS 2";
case MQTT_RC_DISCONNECT_WITH_WILL_MSG:
return "Disconnect with Will Message";
case MQTT_RC_NO_MATCHING_SUBSCRIBERS:
return "No matching subscribers";
case MQTT_RC_NO_SUBSCRIPTION_EXISTED:
return "No subscription existed";
case MQTT_RC_CONTINUE_AUTHENTICATION:
return "Continue authentication";
case MQTT_RC_REAUTHENTICATE:
return "Re-authenticate";
case MQTT_RC_UNSPECIFIED:
return "Unspecified error";
case MQTT_RC_MALFORMED_PACKET:
return "Malformed Packet";
case MQTT_RC_PROTOCOL_ERROR:
return "Protocol Error";
case MQTT_RC_IMPLEMENTATION_SPECIFIC:
return "Implementation specific error";
case MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION:
return "Unsupported Protocol Version";
case MQTT_RC_CLIENTID_NOT_VALID:
return "Client Identifier not valid";
case MQTT_RC_BAD_USERNAME_OR_PASSWORD:
return "Bad User Name or Password";
case MQTT_RC_NOT_AUTHORIZED:
return "Not authorized";
case MQTT_RC_SERVER_UNAVAILABLE:
return "Server unavailable";
case MQTT_RC_SERVER_BUSY:
return "Server busy";
case MQTT_RC_BANNED:
return "Banned";
case MQTT_RC_SERVER_SHUTTING_DOWN:
return "Server shutting down";
case MQTT_RC_BAD_AUTHENTICATION_METHOD:
return "Bad authentication method";
case MQTT_RC_KEEP_ALIVE_TIMEOUT:
return "Keep Alive timeout";
case MQTT_RC_SESSION_TAKEN_OVER:
return "Session taken over";
case MQTT_RC_TOPIC_FILTER_INVALID:
return "Topic Filter invalid";
case MQTT_RC_TOPIC_NAME_INVALID:
return "Topic Name invalid";
case MQTT_RC_PACKET_ID_IN_USE:
return "Packet Identifier in use";
case MQTT_RC_PACKET_ID_NOT_FOUND:
return "Packet Identifier not found";
case MQTT_RC_RECEIVE_MAXIMUM_EXCEEDED:
return "Receive Maximum exceeded";
case MQTT_RC_TOPIC_ALIAS_INVALID:
return "Topic Alias invalid";
case MQTT_RC_PACKET_TOO_LARGE:
return "Packet too large";
case MQTT_RC_MESSAGE_RATE_TOO_HIGH:
return "Message rate too high";
case MQTT_RC_QUOTA_EXCEEDED:
return "Quota exceeded";
case MQTT_RC_ADMINISTRATIVE_ACTION:
return "Administrative action";
case MQTT_RC_PAYLOAD_FORMAT_INVALID:
return "Payload format invalid";
case MQTT_RC_RETAIN_NOT_SUPPORTED:
return "Retain not supported";
case MQTT_RC_QOS_NOT_SUPPORTED:
return "QoS not supported";
case MQTT_RC_USE_ANOTHER_SERVER:
return "Use another server";
case MQTT_RC_SERVER_MOVED:
return "Server moved";
case MQTT_RC_SHARED_SUBS_NOT_SUPPORTED:
return "Shared Subscriptions not supported";
case MQTT_RC_CONNECTION_RATE_EXCEEDED:
return "Connection rate exceeded";
case MQTT_RC_MAXIMUM_CONNECT_TIME:
return "Maximum connect time";
case MQTT_RC_SUBSCRIPTION_IDS_NOT_SUPPORTED:
return "Subscription identifiers not supported";
case MQTT_RC_WILDCARD_SUBS_NOT_SUPPORTED:
return "Wildcard Subscriptions not supported";
default:
return "Unknown reason";
}
}
int mosquitto_string_to_command(const char *str, int *cmd)
{
if(!strcasecmp(str, "connect")){
*cmd = CMD_CONNECT;
}else if(!strcasecmp(str, "connack")){
*cmd = CMD_CONNACK;
}else if(!strcasecmp(str, "publish")){
*cmd = CMD_PUBLISH;
}else if(!strcasecmp(str, "puback")){
*cmd = CMD_PUBACK;
}else if(!strcasecmp(str, "pubrec")){
*cmd = CMD_PUBREC;
}else if(!strcasecmp(str, "pubrel")){
*cmd = CMD_PUBREL;
}else if(!strcasecmp(str, "pubcomp")){
*cmd = CMD_PUBCOMP;
}else if(!strcasecmp(str, "subscribe")){
*cmd = CMD_SUBSCRIBE;
}else if(!strcasecmp(str, "unsubscribe")){
*cmd = CMD_UNSUBSCRIBE;
}else if(!strcasecmp(str, "disconnect")){
*cmd = CMD_DISCONNECT;
}else if(!strcasecmp(str, "auth")){
*cmd = CMD_AUTH;
}else if(!strcasecmp(str, "will")){
*cmd = CMD_WILL;
}else{
return MOSQ_ERR_INVAL;
}
return MOSQ_ERR_SUCCESS;
}
int mosquitto_sub_topic_tokenise(const char *subtopic, char ***topics, int *count)
{
int len;
int hier_count = 1;
int start, stop;
int hier;
int tlen;
int i, j;
if(!subtopic || !topics || !count) return MOSQ_ERR_INVAL;
len = strlen(subtopic);
for(i=0; i<len; i++){
if(subtopic[i] == '/'){
if(i > len-1){
/* Separator at end of line */
}else{
hier_count++;
}
}
}
(*topics) = mosquitto__calloc(hier_count, sizeof(char *));
if(!(*topics)) return MOSQ_ERR_NOMEM;
start = 0;
stop = 0;
hier = 0;
for(i=0; i<len+1; i++){
if(subtopic[i] == '/' || subtopic[i] == '\0'){
stop = i;
if(start != stop){
tlen = stop-start + 1;
(*topics)[hier] = mosquitto__calloc(tlen, sizeof(char));
if(!(*topics)[hier]){
for(j=0; j<hier; j++){
mosquitto__free((*topics)[j]);
}
mosquitto__free((*topics));
return MOSQ_ERR_NOMEM;
}
for(j=start; j<stop; j++){
(*topics)[hier][j-start] = subtopic[j];
}
}
start = i+1;
hier++;
}
}
*count = hier_count;
return MOSQ_ERR_SUCCESS;
}
int mosquitto_sub_topic_tokens_free(char ***topics, int count)
{
int i;
if(!topics || !(*topics) || count<1) return MOSQ_ERR_INVAL;
for(i=0; i<count; i++){
mosquitto__free((*topics)[i]);
}
mosquitto__free(*topics);
return MOSQ_ERR_SUCCESS;
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,356 @@
/*
Copyright (c) 2010-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
Tatsuzo Osawa - Add epoll.
*/
#ifndef MOSQUITTO_INTERNAL_H
#define MOSQUITTO_INTERNAL_H
#include "config.h"
#ifdef WIN32
# include <winsock2.h>
#endif
#ifdef WITH_TLS
# include <openssl/ssl.h>
#else
# include <time.h>
#endif
#include <stdlib.h>
#if defined(WITH_THREADING) && !defined(WITH_BROKER)
# include <pthread.h>
#else
# include <dummypthread.h>
#endif
#ifdef WITH_SRV
# include <ares.h>
#endif
#ifdef WIN32
# if _MSC_VER < 1600
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
# else
# include <stdint.h>
# endif
#else
# include <stdint.h>
#endif
#include "mosquitto.h"
#include "time_mosq.h"
#ifdef WITH_BROKER
# ifdef __linux__
# include <netdb.h>
# endif
# include "uthash.h"
struct mosquitto_client_msg;
#endif
#ifdef WIN32
typedef SOCKET mosq_sock_t;
#else
typedef int mosq_sock_t;
#endif
enum mosquitto_msg_direction {
mosq_md_in = 0,
mosq_md_out = 1
};
enum mosquitto_msg_state {
mosq_ms_invalid = 0,
mosq_ms_publish_qos0 = 1,
mosq_ms_publish_qos1 = 2,
mosq_ms_wait_for_puback = 3,
mosq_ms_publish_qos2 = 4,
mosq_ms_wait_for_pubrec = 5,
mosq_ms_resend_pubrel = 6,
mosq_ms_wait_for_pubrel = 7,
mosq_ms_resend_pubcomp = 8,
mosq_ms_wait_for_pubcomp = 9,
mosq_ms_send_pubrec = 10,
mosq_ms_queued = 11
};
enum mosquitto_client_state {
mosq_cs_new = 0,
mosq_cs_connected = 1,
mosq_cs_disconnecting = 2,
mosq_cs_active = 3,
mosq_cs_connect_pending = 4,
mosq_cs_connect_srv = 5,
mosq_cs_disconnect_ws = 6,
mosq_cs_disconnected = 7,
mosq_cs_socks5_new = 8,
mosq_cs_socks5_start = 9,
mosq_cs_socks5_request = 10,
mosq_cs_socks5_reply = 11,
mosq_cs_socks5_auth_ok = 12,
mosq_cs_socks5_userpass_reply = 13,
mosq_cs_socks5_send_userpass = 14,
mosq_cs_expiring = 15,
mosq_cs_duplicate = 17, /* client that has been taken over by another with the same id */
mosq_cs_disconnect_with_will = 18,
mosq_cs_disused = 19, /* client that has been added to the disused list to be freed */
mosq_cs_authenticating = 20, /* Client has sent CONNECT but is still undergoing extended authentication */
mosq_cs_reauthenticating = 21, /* Client is undergoing reauthentication and shouldn't do anything else until complete */
};
enum mosquitto__protocol {
mosq_p_invalid = 0,
mosq_p_mqtt31 = 1,
mosq_p_mqtt311 = 2,
mosq_p_mqtts = 3,
mosq_p_mqtt5 = 5,
};
enum mosquitto__threaded_state {
mosq_ts_none, /* No threads in use */
mosq_ts_self, /* Threads started by libmosquitto */
mosq_ts_external /* Threads started by external code */
};
enum mosquitto__transport {
mosq_t_invalid = 0,
mosq_t_tcp = 1,
mosq_t_ws = 2,
mosq_t_sctp = 3
};
struct mosquitto__alias{
char *topic;
uint16_t alias;
};
struct session_expiry_list {
struct mosquitto *context;
struct session_expiry_list *prev;
struct session_expiry_list *next;
};
struct mosquitto__packet{
uint8_t *payload;
struct mosquitto__packet *next;
uint32_t remaining_mult;
uint32_t remaining_length;
uint32_t packet_length;
uint32_t to_process;
uint32_t pos;
uint16_t mid;
uint8_t command;
int8_t remaining_count;
};
struct mosquitto_message_all{
struct mosquitto_message_all *next;
struct mosquitto_message_all *prev;
mosquitto_property *properties;
time_t timestamp;
//enum mosquitto_msg_direction direction;
enum mosquitto_msg_state state;
bool dup;
struct mosquitto_message msg;
uint32_t expiry_interval;
};
#ifdef WITH_TLS
enum mosquitto__keyform {
mosq_k_pem = 0,
mosq_k_engine = 1,
};
#endif
struct will_delay_list {
struct mosquitto *context;
struct will_delay_list *prev;
struct will_delay_list *next;
};
struct mosquitto_msg_data{
#ifdef WITH_BROKER
struct mosquitto_client_msg *inflight;
struct mosquitto_client_msg *queued;
unsigned long msg_bytes;
unsigned long msg_bytes12;
int msg_count;
int msg_count12;
#else
struct mosquitto_message_all *inflight;
int queue_len;
# ifdef WITH_THREADING
pthread_mutex_t mutex;
# endif
#endif
int inflight_quota;
uint16_t inflight_maximum;
};
struct mosquitto {
mosq_sock_t sock;
#ifndef WITH_BROKER
mosq_sock_t sockpairR, sockpairW;
#endif
#if defined(__GLIBC__) && defined(WITH_ADNS)
struct gaicb *adns; /* For getaddrinfo_a */
#endif
enum mosquitto__protocol protocol;
char *address;
char *id;
char *username;
char *password;
uint16_t keepalive;
uint16_t last_mid;
enum mosquitto_client_state state;
time_t last_msg_in;
time_t next_msg_out;
time_t ping_t;
struct mosquitto__packet in_packet;
struct mosquitto__packet *current_out_packet;
struct mosquitto__packet *out_packet;
struct mosquitto_message_all *will;
struct mosquitto__alias *aliases;
struct will_delay_list *will_delay_entry;
uint32_t maximum_packet_size;
int alias_count;
uint32_t will_delay_interval;
time_t will_delay_time;
#ifdef WITH_TLS
SSL *ssl;
SSL_CTX *ssl_ctx;
char *tls_cafile;
char *tls_capath;
char *tls_certfile;
char *tls_keyfile;
int (*tls_pw_callback)(char *buf, int size, int rwflag, void *userdata);
char *tls_version;
char *tls_ciphers;
char *tls_psk;
char *tls_psk_identity;
int tls_cert_reqs;
bool tls_insecure;
bool ssl_ctx_defaults;
bool tls_ocsp_required;
char *tls_engine;
char *tls_engine_kpass_sha1;
enum mosquitto__keyform tls_keyform;
char *tls_alpn;
#endif
bool want_write;
bool want_connect;
#if defined(WITH_THREADING) && !defined(WITH_BROKER)
pthread_mutex_t callback_mutex;
pthread_mutex_t log_callback_mutex;
pthread_mutex_t msgtime_mutex;
pthread_mutex_t out_packet_mutex;
pthread_mutex_t current_out_packet_mutex;
pthread_mutex_t state_mutex;
pthread_mutex_t mid_mutex;
pthread_t thread_id;
#endif
bool clean_start;
uint32_t session_expiry_interval;
time_t session_expiry_time;
#ifdef WITH_BROKER
bool removed_from_by_id; /* True if removed from by_id hash */
bool is_dropping;
bool is_bridge;
struct mosquitto__bridge *bridge;
struct mosquitto_msg_data msgs_in;
struct mosquitto_msg_data msgs_out;
struct mosquitto__acl_user *acl_list;
struct mosquitto__listener *listener;
struct mosquitto__packet *out_packet_last;
struct mosquitto__subhier **subs;
struct mosquitto__subshared_ref **shared_subs;
char *auth_method;
int sub_count;
int shared_sub_count;
int pollfd_index;
# ifdef WITH_WEBSOCKETS
# if defined(LWS_LIBRARY_VERSION_NUMBER)
struct lws *wsi;
# else
struct libwebsocket_context *ws_context;
struct libwebsocket *wsi;
# endif
# endif
bool ws_want_write;
bool assigned_id;
#else
# ifdef WITH_SOCKS
char *socks5_host;
int socks5_port;
char *socks5_username;
char *socks5_password;
# endif
void *userdata;
bool in_callback;
struct mosquitto_msg_data msgs_in;
struct mosquitto_msg_data msgs_out;
void (*on_connect)(struct mosquitto *, void *userdata, int rc);
void (*on_connect_with_flags)(struct mosquitto *, void *userdata, int rc, int flags);
void (*on_connect_v5)(struct mosquitto *, void *userdata, int rc, int flags, const mosquitto_property *props);
void (*on_disconnect)(struct mosquitto *, void *userdata, int rc);
void (*on_disconnect_v5)(struct mosquitto *, void *userdata, int rc, const mosquitto_property *props);
void (*on_publish)(struct mosquitto *, void *userdata, int mid);
void (*on_publish_v5)(struct mosquitto *, void *userdata, int mid, int reason_code, const mosquitto_property *props);
void (*on_message)(struct mosquitto *, void *userdata, const struct mosquitto_message *message);
void (*on_message_v5)(struct mosquitto *, void *userdata, const struct mosquitto_message *message, const mosquitto_property *props);
void (*on_subscribe)(struct mosquitto *, void *userdata, int mid, int qos_count, const int *granted_qos);
void (*on_subscribe_v5)(struct mosquitto *, void *userdata, int mid, int qos_count, const int *granted_qos, const mosquitto_property *props);
void (*on_unsubscribe)(struct mosquitto *, void *userdata, int mid);
void (*on_unsubscribe_v5)(struct mosquitto *, void *userdata, int mid, const mosquitto_property *props);
void (*on_log)(struct mosquitto *, void *userdata, int level, const char *str);
//void (*on_error)();
char *host;
int port;
char *bind_address;
unsigned int reconnects;
unsigned int reconnect_delay;
unsigned int reconnect_delay_max;
bool reconnect_exponential_backoff;
char threaded;
struct mosquitto__packet *out_packet_last;
# ifdef WITH_SRV
ares_channel achan;
# endif
#endif
uint8_t maximum_qos;
#ifdef WITH_BROKER
UT_hash_handle hh_id;
UT_hash_handle hh_sock;
struct mosquitto *for_free_next;
struct session_expiry_list *expiry_list_item;
#endif
#ifdef WITH_EPOLL
uint32_t events;
#endif
};
#define STREMPTY(str) (str[0] == '\0')
void do_client_disconnect(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties);
#endif

@ -0,0 +1,158 @@
/*
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#ifndef MQTT_PROTOCOL_H
#define MQTT_PROTOCOL_H
#define PROTOCOL_NAME_v31 "MQIsdp"
#define PROTOCOL_VERSION_v31 3
#define PROTOCOL_NAME "MQTT"
#define PROTOCOL_VERSION_v311 4
#define PROTOCOL_VERSION_v5 5
/* Message types */
#define CMD_CONNECT 0x10
#define CMD_CONNACK 0x20
#define CMD_PUBLISH 0x30
#define CMD_PUBACK 0x40
#define CMD_PUBREC 0x50
#define CMD_PUBREL 0x60
#define CMD_PUBCOMP 0x70
#define CMD_SUBSCRIBE 0x80
#define CMD_SUBACK 0x90
#define CMD_UNSUBSCRIBE 0xA0
#define CMD_UNSUBACK 0xB0
#define CMD_PINGREQ 0xC0
#define CMD_PINGRESP 0xD0
#define CMD_DISCONNECT 0xE0
#define CMD_AUTH 0xF0
/* Mosquitto only: for distinguishing CONNECT and WILL properties */
#define CMD_WILL 0x100
enum mqtt311_connack_codes {
CONNACK_ACCEPTED = 0,
CONNACK_REFUSED_PROTOCOL_VERSION = 1,
CONNACK_REFUSED_IDENTIFIER_REJECTED = 2,
CONNACK_REFUSED_SERVER_UNAVAILABLE = 3,
CONNACK_REFUSED_BAD_USERNAME_PASSWORD = 4,
CONNACK_REFUSED_NOT_AUTHORIZED = 5,
};
enum mqtt5_return_codes {
MQTT_RC_SUCCESS = 0, /* CONNACK, PUBACK, PUBREC, PUBREL, PUBCOMP, UNSUBACK, AUTH */
MQTT_RC_NORMAL_DISCONNECTION = 0, /* DISCONNECT */
MQTT_RC_GRANTED_QOS0 = 0, /* SUBACK */
MQTT_RC_GRANTED_QOS1 = 1, /* SUBACK */
MQTT_RC_GRANTED_QOS2 = 2, /* SUBACK */
MQTT_RC_DISCONNECT_WITH_WILL_MSG = 4, /* DISCONNECT */
MQTT_RC_NO_MATCHING_SUBSCRIBERS = 16, /* PUBACK, PUBREC */
MQTT_RC_NO_SUBSCRIPTION_EXISTED = 17, /* UNSUBACK */
MQTT_RC_CONTINUE_AUTHENTICATION = 24, /* AUTH */
MQTT_RC_REAUTHENTICATE = 25, /* AUTH */
MQTT_RC_UNSPECIFIED = 128, /* CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT */
MQTT_RC_MALFORMED_PACKET = 129, /* CONNACK, DISCONNECT */
MQTT_RC_PROTOCOL_ERROR = 130, /* DISCONNECT */
MQTT_RC_IMPLEMENTATION_SPECIFIC = 131, /* CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT */
MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION = 132, /* CONNACK */
MQTT_RC_CLIENTID_NOT_VALID = 133, /* CONNACK */
MQTT_RC_BAD_USERNAME_OR_PASSWORD = 134, /* CONNACK */
MQTT_RC_NOT_AUTHORIZED = 135, /* CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT */
MQTT_RC_SERVER_UNAVAILABLE = 136, /* CONNACK */
MQTT_RC_SERVER_BUSY = 137, /* CONNACK, DISCONNECT */
MQTT_RC_BANNED = 138, /* CONNACK */
MQTT_RC_SERVER_SHUTTING_DOWN = 139, /* DISCONNECT */
MQTT_RC_BAD_AUTHENTICATION_METHOD = 140, /* CONNACK */
MQTT_RC_KEEP_ALIVE_TIMEOUT = 141, /* DISCONNECT */
MQTT_RC_SESSION_TAKEN_OVER = 142, /* DISCONNECT */
MQTT_RC_TOPIC_FILTER_INVALID = 143, /* SUBACK, UNSUBACK, DISCONNECT */
MQTT_RC_TOPIC_NAME_INVALID = 144, /* CONNACK, PUBACK, PUBREC, DISCONNECT */
MQTT_RC_PACKET_ID_IN_USE = 145, /* PUBACK, SUBACK, UNSUBACK */
MQTT_RC_PACKET_ID_NOT_FOUND = 146, /* PUBREL, PUBCOMP */
MQTT_RC_RECEIVE_MAXIMUM_EXCEEDED = 147, /* DISCONNECT */
MQTT_RC_TOPIC_ALIAS_INVALID = 148, /* DISCONNECT */
MQTT_RC_PACKET_TOO_LARGE = 149, /* CONNACK, PUBACK, PUBREC, DISCONNECT */
MQTT_RC_MESSAGE_RATE_TOO_HIGH = 150, /* DISCONNECT */
MQTT_RC_QUOTA_EXCEEDED = 151, /* PUBACK, PUBREC, SUBACK, DISCONNECT */
MQTT_RC_ADMINISTRATIVE_ACTION = 152, /* DISCONNECT */
MQTT_RC_PAYLOAD_FORMAT_INVALID = 153, /* CONNACK, DISCONNECT */
MQTT_RC_RETAIN_NOT_SUPPORTED = 154, /* CONNACK, DISCONNECT */
MQTT_RC_QOS_NOT_SUPPORTED = 155, /* CONNACK, DISCONNECT */
MQTT_RC_USE_ANOTHER_SERVER = 156, /* CONNACK, DISCONNECT */
MQTT_RC_SERVER_MOVED = 157, /* CONNACK, DISCONNECT */
MQTT_RC_SHARED_SUBS_NOT_SUPPORTED = 158, /* SUBACK, DISCONNECT */
MQTT_RC_CONNECTION_RATE_EXCEEDED = 159, /* CONNACK, DISCONNECT */
MQTT_RC_MAXIMUM_CONNECT_TIME = 160, /* DISCONNECT */
MQTT_RC_SUBSCRIPTION_IDS_NOT_SUPPORTED = 161, /* SUBACK, DISCONNECT */
MQTT_RC_WILDCARD_SUBS_NOT_SUPPORTED = 162, /* SUBACK, DISCONNECT */
};
enum mqtt5_property {
MQTT_PROP_PAYLOAD_FORMAT_INDICATOR = 1, /* Byte : PUBLISH, Will Properties */
MQTT_PROP_MESSAGE_EXPIRY_INTERVAL = 2, /* 4 byte int : PUBLISH, Will Properties */
MQTT_PROP_CONTENT_TYPE = 3, /* UTF-8 string : PUBLISH, Will Properties */
MQTT_PROP_RESPONSE_TOPIC = 8, /* UTF-8 string : PUBLISH, Will Properties */
MQTT_PROP_CORRELATION_DATA = 9, /* Binary Data : PUBLISH, Will Properties */
MQTT_PROP_SUBSCRIPTION_IDENTIFIER = 11, /* Variable byte int : PUBLISH, SUBSCRIBE */
MQTT_PROP_SESSION_EXPIRY_INTERVAL = 17, /* 4 byte int : CONNECT, CONNACK, DISCONNECT */
MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER = 18, /* UTF-8 string : CONNACK */
MQTT_PROP_SERVER_KEEP_ALIVE = 19, /* 2 byte int : CONNACK */
MQTT_PROP_AUTHENTICATION_METHOD = 21, /* UTF-8 string : CONNECT, CONNACK, AUTH */
MQTT_PROP_AUTHENTICATION_DATA = 22, /* Binary Data : CONNECT, CONNACK, AUTH */
MQTT_PROP_REQUEST_PROBLEM_INFORMATION = 23, /* Byte : CONNECT */
MQTT_PROP_WILL_DELAY_INTERVAL = 24, /* 4 byte int : Will properties */
MQTT_PROP_REQUEST_RESPONSE_INFORMATION = 25,/* Byte : CONNECT */
MQTT_PROP_RESPONSE_INFORMATION = 26, /* UTF-8 string : CONNACK */
MQTT_PROP_SERVER_REFERENCE = 28, /* UTF-8 string : CONNACK, DISCONNECT */
MQTT_PROP_REASON_STRING = 31, /* UTF-8 string : All except Will properties */
MQTT_PROP_RECEIVE_MAXIMUM = 33, /* 2 byte int : CONNECT, CONNACK */
MQTT_PROP_TOPIC_ALIAS_MAXIMUM = 34, /* 2 byte int : CONNECT, CONNACK */
MQTT_PROP_TOPIC_ALIAS = 35, /* 2 byte int : PUBLISH */
MQTT_PROP_MAXIMUM_QOS = 36, /* Byte : CONNACK */
MQTT_PROP_RETAIN_AVAILABLE = 37, /* Byte : CONNACK */
MQTT_PROP_USER_PROPERTY = 38, /* UTF-8 string pair : All */
MQTT_PROP_MAXIMUM_PACKET_SIZE = 39, /* 4 byte int : CONNECT, CONNACK */
MQTT_PROP_WILDCARD_SUB_AVAILABLE = 40, /* Byte : CONNACK */
MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE = 41, /* Byte : CONNACK */
MQTT_PROP_SHARED_SUB_AVAILABLE = 42, /* Byte : CONNACK */
};
enum mqtt5_property_type {
MQTT_PROP_TYPE_BYTE = 1,
MQTT_PROP_TYPE_INT16 = 2,
MQTT_PROP_TYPE_INT32 = 3,
MQTT_PROP_TYPE_VARINT = 4,
MQTT_PROP_TYPE_BINARY = 5,
MQTT_PROP_TYPE_STRING = 6,
MQTT_PROP_TYPE_STRING_PAIR = 7
};
enum mqtt5_sub_options {
MQTT_SUB_OPT_NO_LOCAL = 0x04,
MQTT_SUB_OPT_RETAIN_AS_PUBLISHED = 0x08,
MQTT_SUB_OPT_SEND_RETAIN_ALWAYS = 0x00,
MQTT_SUB_OPT_SEND_RETAIN_NEW = 0x10,
MQTT_SUB_OPT_SEND_RETAIN_NEVER = 0x20,
};
#define MQTT_MAX_PAYLOAD 268435455
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,85 @@
/*
Copyright (c) 2010-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#ifndef NET_MOSQ_H
#define NET_MOSQ_H
#ifndef WIN32
# include <unistd.h>
#else
# include <winsock2.h>
# ifndef _SSIZE_T_DEFINED
typedef SSIZE_T ssize_t;
# define _SSIZE_T_DEFINED
# endif
#endif
#include "mosquitto_internal.h"
#include "mosquitto.h"
#ifdef WITH_BROKER
struct mosquitto_db;
#endif
#ifdef WIN32
# define COMPAT_CLOSE(a) closesocket(a)
# define COMPAT_ECONNRESET WSAECONNRESET
# define COMPAT_EWOULDBLOCK WSAEWOULDBLOCK
#else
# define COMPAT_CLOSE(a) close(a)
# define COMPAT_ECONNRESET ECONNRESET
# define COMPAT_EWOULDBLOCK EWOULDBLOCK
#endif
/* For when not using winsock libraries. */
#ifndef INVALID_SOCKET
#define INVALID_SOCKET -1
#endif
/* Macros for accessing the MSB and LSB of a uint16_t */
#define MOSQ_MSB(A) (uint8_t)((A & 0xFF00) >> 8)
#define MOSQ_LSB(A) (uint8_t)(A & 0x00FF)
int net__init(void);
void net__cleanup(void);
int net__socket_connect(struct mosquitto *mosq, const char *host, uint16_t port, const char *bind_address, bool blocking);
#ifdef WITH_BROKER
int net__socket_close(struct mosquitto_db *db, struct mosquitto *mosq);
#else
int net__socket_close(struct mosquitto *mosq);
#endif
int net__try_connect(const char *host, uint16_t port, mosq_sock_t *sock, const char *bind_address, bool blocking);
int net__try_connect_step1(struct mosquitto *mosq, const char *host);
int net__try_connect_step2(struct mosquitto *mosq, uint16_t port, mosq_sock_t *sock);
int net__socket_connect_step3(struct mosquitto *mosq, const char *host);
int net__socket_nonblock(mosq_sock_t *sock);
int net__socketpair(mosq_sock_t *sp1, mosq_sock_t *sp2);
ssize_t net__read(struct mosquitto *mosq, void *buf, size_t count);
ssize_t net__write(struct mosquitto *mosq, void *buf, size_t count);
#ifdef WITH_TLS
int net__socket_apply_tls(struct mosquitto *mosq);
int net__socket_connect_tls(struct mosquitto *mosq);
int mosquitto__verify_ocsp_status_cb(SSL * ssl, void *arg);
UI_METHOD *net__get_ui_method(void);
#define ENGINE_FINISH(e) if(e) ENGINE_finish(e)
#define ENGINE_SECRET_MODE "SECRET_MODE"
#define ENGINE_SECRET_MODE_SHA 0x1000
#define ENGINE_PIN "PIN"
#endif
#endif

@ -0,0 +1,159 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG), Dr. Lars Voelker <lars.voelker@bmw.de>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Dr. Lars Voelker, BMW AG
*/
/*
COPYRIGHT AND PERMISSION NOTICE of curl on which the ocsp code is based:
Copyright (c) 1996 - 2016, Daniel Stenberg, <daniel@haxx.se>, and many
contributors, see the THANKS file.
All rights reserved.
Permission to use, copy, modify, and distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright
notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of a copyright holder shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization of the copyright holder.
*/
#ifdef WITH_TLS
#include <logging_mosq.h>
#include <mosquitto_internal.h>
#include <net_mosq.h>
#include <openssl/safestack.h>
#include <openssl/tls1.h>
#include <openssl/ssl.h>
#include <openssl/ocsp.h>
int mosquitto__verify_ocsp_status_cb(SSL * ssl, void *arg)
{
struct mosquitto *mosq = (struct mosquitto *)arg;
int ocsp_status, result2, i;
unsigned char *p;
const unsigned char *cp;
OCSP_RESPONSE *rsp = NULL;
OCSP_BASICRESP *br = NULL;
X509_STORE *st = NULL;
STACK_OF(X509) *ch = NULL;
long len = SSL_get_tlsext_status_ocsp_resp(mosq->ssl, &p);
log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: SSL_get_tlsext_status_ocsp_resp returned %ld bytes", len);
// the following functions expect a const pointer
cp = (const unsigned char *)p;
if (!cp || len <= 0) {
log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: no response");
goto end;
}
rsp = d2i_OCSP_RESPONSE(NULL, &cp, len);
if (rsp==NULL) {
log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: invalid response");
goto end;
}
ocsp_status = OCSP_response_status(rsp);
if(ocsp_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: invalid status: %s (%d)",
OCSP_response_status_str(ocsp_status), ocsp_status);
goto end;
}
br = OCSP_response_get1_basic(rsp);
if (!br) {
log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: invalid response");
goto end;
}
ch = SSL_get_peer_cert_chain(mosq->ssl);
if (sk_X509_num(ch) <= 0) {
log__printf(mosq, MOSQ_LOG_ERR, "OCSP: we did not receive certificates of the server (num: %d)", sk_X509_num(ch));
goto end;
}
st = SSL_CTX_get_cert_store(mosq->ssl_ctx);
// Note:
// Other checkers often fix problems in OpenSSL before 1.0.2a (e.g. libcurl).
// For all currently supported versions of the OpenSSL project, this is not needed anymore.
if ((result2=OCSP_basic_verify(br, ch, st, 0)) <= 0) {
log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: response verification failed (error: %d)", result2);
goto end;
}
for(i = 0; i < OCSP_resp_count(br); i++) {
int cert_status, crl_reason;
OCSP_SINGLERESP *single = NULL;
ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
single = OCSP_resp_get0(br, i);
if(!single)
continue;
cert_status = OCSP_single_get0_status(single, &crl_reason, &rev, &thisupd, &nextupd);
log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: SSL certificate status: %s (%d)",
OCSP_cert_status_str(cert_status), cert_status);
switch(cert_status) {
case V_OCSP_CERTSTATUS_GOOD:
// Note: A OCSP stapling result will be accepted up to 5 minutes after it expired!
if(!OCSP_check_validity(thisupd, nextupd, 300L, -1L)) {
log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: OCSP response has expired");
goto end;
}
break;
case V_OCSP_CERTSTATUS_REVOKED:
log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: SSL certificate revocation reason: %s (%d)",
OCSP_crl_reason_str(crl_reason), crl_reason);
goto end;
case V_OCSP_CERTSTATUS_UNKNOWN:
goto end;
default:
log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: SSL certificate revocation status unknown");
goto end;
}
}
if (br!=NULL) OCSP_BASICRESP_free(br);
if (rsp!=NULL) OCSP_RESPONSE_free(rsp);
return 1; // OK
end:
if (br!=NULL) OCSP_BASICRESP_free(br);
if (rsp!=NULL) OCSP_RESPONSE_free(rsp);
return 0; // Not OK
}
#endif

@ -0,0 +1,493 @@
/*
Copyright (c) 2010-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#ifndef WIN32
# include <strings.h>
#endif
#include <string.h>
#ifdef WITH_TLS
# ifdef WIN32
# include <winsock2.h>
# endif
# include <openssl/engine.h>
#endif
#include "mosquitto.h"
#include "mosquitto_internal.h"
#include "memory_mosq.h"
#include "mqtt_protocol.h"
#include "util_mosq.h"
#include "will_mosq.h"
int mosquitto_will_set(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain)
{
return mosquitto_will_set_v5(mosq, topic, payloadlen, payload, qos, retain, NULL);
}
int mosquitto_will_set_v5(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain, mosquitto_property *properties)
{
int rc;
if(!mosq) return MOSQ_ERR_INVAL;
if(properties){
rc = mosquitto_property_check_all(CMD_WILL, properties);
if(rc) return rc;
}
return will__set(mosq, topic, payloadlen, payload, qos, retain, properties);
}
int mosquitto_will_clear(struct mosquitto *mosq)
{
if(!mosq) return MOSQ_ERR_INVAL;
return will__clear(mosq);
}
int mosquitto_username_pw_set(struct mosquitto *mosq, const char *username, const char *password)
{
if(!mosq) return MOSQ_ERR_INVAL;
if(mosq->protocol == mosq_p_mqtt311 || mosq->protocol == mosq_p_mqtt31){
if(password != NULL && username == NULL){
return MOSQ_ERR_INVAL;
}
}
mosquitto__free(mosq->username);
mosq->username = NULL;
mosquitto__free(mosq->password);
mosq->password = NULL;
if(username){
if(mosquitto_validate_utf8(username, strlen(username))){
return MOSQ_ERR_MALFORMED_UTF8;
}
mosq->username = mosquitto__strdup(username);
if(!mosq->username) return MOSQ_ERR_NOMEM;
}
if(password){
mosq->password = mosquitto__strdup(password);
if(!mosq->password){
mosquitto__free(mosq->username);
mosq->username = NULL;
return MOSQ_ERR_NOMEM;
}
}
return MOSQ_ERR_SUCCESS;
}
int mosquitto_reconnect_delay_set(struct mosquitto *mosq, unsigned int reconnect_delay, unsigned int reconnect_delay_max, bool reconnect_exponential_backoff)
{
if(!mosq) return MOSQ_ERR_INVAL;
if(reconnect_delay == 0) reconnect_delay = 1;
mosq->reconnect_delay = reconnect_delay;
mosq->reconnect_delay_max = reconnect_delay_max;
mosq->reconnect_exponential_backoff = reconnect_exponential_backoff;
return MOSQ_ERR_SUCCESS;
}
int mosquitto_tls_set(struct mosquitto *mosq, const char *cafile, const char *capath, const char *certfile, const char *keyfile, int (*pw_callback)(char *buf, int size, int rwflag, void *userdata))
{
#ifdef WITH_TLS
FILE *fptr;
if(!mosq || (!cafile && !capath) || (certfile && !keyfile) || (!certfile && keyfile)) return MOSQ_ERR_INVAL;
mosquitto__free(mosq->tls_cafile);
mosq->tls_cafile = NULL;
if(cafile){
fptr = mosquitto__fopen(cafile, "rt", false);
if(fptr){
fclose(fptr);
}else{
return MOSQ_ERR_INVAL;
}
mosq->tls_cafile = mosquitto__strdup(cafile);
if(!mosq->tls_cafile){
return MOSQ_ERR_NOMEM;
}
}
mosquitto__free(mosq->tls_capath);
mosq->tls_capath = NULL;
if(capath){
mosq->tls_capath = mosquitto__strdup(capath);
if(!mosq->tls_capath){
return MOSQ_ERR_NOMEM;
}
}
mosquitto__free(mosq->tls_certfile);
mosq->tls_certfile = NULL;
if(certfile){
fptr = mosquitto__fopen(certfile, "rt", false);
if(fptr){
fclose(fptr);
}else{
mosquitto__free(mosq->tls_cafile);
mosq->tls_cafile = NULL;
mosquitto__free(mosq->tls_capath);
mosq->tls_capath = NULL;
return MOSQ_ERR_INVAL;
}
mosq->tls_certfile = mosquitto__strdup(certfile);
if(!mosq->tls_certfile){
return MOSQ_ERR_NOMEM;
}
}
mosquitto__free(mosq->tls_keyfile);
mosq->tls_keyfile = NULL;
if(keyfile){
fptr = mosquitto__fopen(keyfile, "rt", false);
if(fptr){
fclose(fptr);
}else{
mosquitto__free(mosq->tls_cafile);
mosq->tls_cafile = NULL;
mosquitto__free(mosq->tls_capath);
mosq->tls_capath = NULL;
mosquitto__free(mosq->tls_certfile);
mosq->tls_certfile = NULL;
return MOSQ_ERR_INVAL;
}
mosq->tls_keyfile = mosquitto__strdup(keyfile);
if(!mosq->tls_keyfile){
return MOSQ_ERR_NOMEM;
}
}
mosq->tls_pw_callback = pw_callback;
return MOSQ_ERR_SUCCESS;
#else
return MOSQ_ERR_NOT_SUPPORTED;
#endif
}
int mosquitto_tls_opts_set(struct mosquitto *mosq, int cert_reqs, const char *tls_version, const char *ciphers)
{
#ifdef WITH_TLS
if(!mosq) return MOSQ_ERR_INVAL;
mosq->tls_cert_reqs = cert_reqs;
if(tls_version){
if(!strcasecmp(tls_version, "tlsv1.3")
|| !strcasecmp(tls_version, "tlsv1.2")
|| !strcasecmp(tls_version, "tlsv1.1")){
mosq->tls_version = mosquitto__strdup(tls_version);
if(!mosq->tls_version) return MOSQ_ERR_NOMEM;
}else{
return MOSQ_ERR_INVAL;
}
}else{
mosq->tls_version = mosquitto__strdup("tlsv1.2");
if(!mosq->tls_version) return MOSQ_ERR_NOMEM;
}
if(ciphers){
mosq->tls_ciphers = mosquitto__strdup(ciphers);
if(!mosq->tls_ciphers) return MOSQ_ERR_NOMEM;
}else{
mosq->tls_ciphers = NULL;
}
return MOSQ_ERR_SUCCESS;
#else
return MOSQ_ERR_NOT_SUPPORTED;
#endif
}
int mosquitto_tls_insecure_set(struct mosquitto *mosq, bool value)
{
#ifdef WITH_TLS
if(!mosq) return MOSQ_ERR_INVAL;
mosq->tls_insecure = value;
return MOSQ_ERR_SUCCESS;
#else
return MOSQ_ERR_NOT_SUPPORTED;
#endif
}
int mosquitto_string_option(struct mosquitto *mosq, enum mosq_opt_t option, const char *value)
{
#ifdef WITH_TLS
ENGINE *eng;
char *str;
#endif
if(!mosq) return MOSQ_ERR_INVAL;
switch(option){
case MOSQ_OPT_TLS_ENGINE:
#ifdef WITH_TLS
# if !defined(OPENSSL_NO_ENGINE)
eng = ENGINE_by_id(value);
if(!eng){
return MOSQ_ERR_INVAL;
}
ENGINE_free(eng); /* release the structural reference from ENGINE_by_id() */
mosq->tls_engine = mosquitto__strdup(value);
if(!mosq->tls_engine){
return MOSQ_ERR_NOMEM;
}
return MOSQ_ERR_SUCCESS;
#endif
#else
return MOSQ_ERR_NOT_SUPPORTED;
#endif
break;
case MOSQ_OPT_TLS_KEYFORM:
#ifdef WITH_TLS
if(!value) return MOSQ_ERR_INVAL;
if(!strcasecmp(value, "pem")){
mosq->tls_keyform = mosq_k_pem;
}else if (!strcasecmp(value, "engine")){
mosq->tls_keyform = mosq_k_engine;
}else{
return MOSQ_ERR_INVAL;
}
return MOSQ_ERR_SUCCESS;
#else
return MOSQ_ERR_NOT_SUPPORTED;
#endif
break;
case MOSQ_OPT_TLS_ENGINE_KPASS_SHA1:
#ifdef WITH_TLS
if(mosquitto__hex2bin_sha1(value, (unsigned char**)&str) != MOSQ_ERR_SUCCESS){
return MOSQ_ERR_INVAL;
}
mosq->tls_engine_kpass_sha1 = str;
return MOSQ_ERR_SUCCESS;
#else
return MOSQ_ERR_NOT_SUPPORTED;
#endif
break;
case MOSQ_OPT_TLS_ALPN:
#ifdef WITH_TLS
mosq->tls_alpn = mosquitto__strdup(value);
if(!mosq->tls_alpn){
return MOSQ_ERR_NOMEM;
}
return MOSQ_ERR_SUCCESS;
#else
return MOSQ_ERR_NOT_SUPPORTED;
#endif
break;
default:
return MOSQ_ERR_INVAL;
}
}
int mosquitto_tls_psk_set(struct mosquitto *mosq, const char *psk, const char *identity, const char *ciphers)
{
#ifdef FINAL_WITH_TLS_PSK
if(!mosq || !psk || !identity) return MOSQ_ERR_INVAL;
/* Check for hex only digits */
if(strspn(psk, "0123456789abcdefABCDEF") < strlen(psk)){
return MOSQ_ERR_INVAL;
}
mosq->tls_psk = mosquitto__strdup(psk);
if(!mosq->tls_psk) return MOSQ_ERR_NOMEM;
mosq->tls_psk_identity = mosquitto__strdup(identity);
if(!mosq->tls_psk_identity){
mosquitto__free(mosq->tls_psk);
return MOSQ_ERR_NOMEM;
}
if(ciphers){
mosq->tls_ciphers = mosquitto__strdup(ciphers);
if(!mosq->tls_ciphers) return MOSQ_ERR_NOMEM;
}else{
mosq->tls_ciphers = NULL;
}
return MOSQ_ERR_SUCCESS;
#else
return MOSQ_ERR_NOT_SUPPORTED;
#endif
}
int mosquitto_opts_set(struct mosquitto *mosq, enum mosq_opt_t option, void *value)
{
int ival;
if(!mosq || !value) return MOSQ_ERR_INVAL;
switch(option){
case MOSQ_OPT_PROTOCOL_VERSION:
ival = *((int *)value);
return mosquitto_int_option(mosq, option, ival);
case MOSQ_OPT_SSL_CTX:
#ifdef WITH_TLS
mosq->ssl_ctx = (SSL_CTX *)value;
if(mosq->ssl_ctx){
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER)
SSL_CTX_up_ref(mosq->ssl_ctx);
#else
CRYPTO_add(&(mosq->ssl_ctx)->references, 1, CRYPTO_LOCK_SSL_CTX);
#endif
}
break;
#else
return MOSQ_ERR_NOT_SUPPORTED;
#endif
default:
return MOSQ_ERR_INVAL;
}
return MOSQ_ERR_SUCCESS;
}
int mosquitto_int_option(struct mosquitto *mosq, enum mosq_opt_t option, int value)
{
if(!mosq) return MOSQ_ERR_INVAL;
switch(option){
case MOSQ_OPT_PROTOCOL_VERSION:
if(value == MQTT_PROTOCOL_V31){
mosq->protocol = mosq_p_mqtt31;
}else if(value == MQTT_PROTOCOL_V311){
mosq->protocol = mosq_p_mqtt311;
}else if(value == MQTT_PROTOCOL_V5){
mosq->protocol = mosq_p_mqtt5;
}else{
return MOSQ_ERR_INVAL;
}
break;
case MOSQ_OPT_RECEIVE_MAXIMUM:
if(value < 0 || value > 65535){
return MOSQ_ERR_INVAL;
}
if(value == 0){
mosq->msgs_in.inflight_maximum = 65535;
}else{
mosq->msgs_in.inflight_maximum = value;
}
break;
case MOSQ_OPT_SEND_MAXIMUM:
if(value < 0 || value > 65535){
return MOSQ_ERR_INVAL;
}
if(value == 0){
mosq->msgs_out.inflight_maximum = 65535;
}else{
mosq->msgs_out.inflight_maximum = value;
}
break;
case MOSQ_OPT_SSL_CTX_WITH_DEFAULTS:
#if defined(WITH_TLS) && OPENSSL_VERSION_NUMBER >= 0x10100000L
if(value){
mosq->ssl_ctx_defaults = true;
}else{
mosq->ssl_ctx_defaults = false;
}
break;
#else
return MOSQ_ERR_NOT_SUPPORTED;
#endif
case MOSQ_OPT_TLS_OCSP_REQUIRED:
#ifdef WITH_TLS
mosq->tls_ocsp_required = (bool)value;
#else
return MOSQ_ERR_NOT_SUPPORTED;
#endif
break;
default:
return MOSQ_ERR_INVAL;
}
return MOSQ_ERR_SUCCESS;
}
int mosquitto_void_option(struct mosquitto *mosq, enum mosq_opt_t option, void *value)
{
if(!mosq || !value) return MOSQ_ERR_INVAL;
switch(option){
case MOSQ_OPT_SSL_CTX:
#ifdef WITH_TLS
mosq->ssl_ctx = (SSL_CTX *)value;
if(mosq->ssl_ctx){
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER)
SSL_CTX_up_ref(mosq->ssl_ctx);
#else
CRYPTO_add(&(mosq->ssl_ctx)->references, 1, CRYPTO_LOCK_SSL_CTX);
#endif
}
break;
#else
return MOSQ_ERR_NOT_SUPPORTED;
#endif
default:
return MOSQ_ERR_INVAL;
}
return MOSQ_ERR_SUCCESS;
}
void mosquitto_user_data_set(struct mosquitto *mosq, void *userdata)
{
if(mosq){
mosq->userdata = userdata;
}
}
void *mosquitto_userdata(struct mosquitto *mosq)
{
return mosq->userdata;
}

@ -0,0 +1,271 @@
/*
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#include <errno.h>
#include <string.h>
#ifdef WITH_BROKER
# include "mosquitto_broker_internal.h"
# ifdef WITH_WEBSOCKETS
# include <libwebsockets.h>
# endif
#else
# include "read_handle.h"
#endif
#include "memory_mosq.h"
#include "mqtt_protocol.h"
#include "net_mosq.h"
#include "packet_mosq.h"
#include "read_handle.h"
#ifdef WITH_BROKER
# include "sys_tree.h"
#else
# define G_BYTES_RECEIVED_INC(A)
# define G_BYTES_SENT_INC(A)
# define G_MSGS_SENT_INC(A)
# define G_PUB_MSGS_SENT_INC(A)
#endif
int packet__read_byte(struct mosquitto__packet *packet, uint8_t *byte)
{
assert(packet);
if(packet->pos+1 > packet->remaining_length) return MOSQ_ERR_PROTOCOL;
*byte = packet->payload[packet->pos];
packet->pos++;
return MOSQ_ERR_SUCCESS;
}
void packet__write_byte(struct mosquitto__packet *packet, uint8_t byte)
{
assert(packet);
assert(packet->pos+1 <= packet->packet_length);
packet->payload[packet->pos] = byte;
packet->pos++;
}
int packet__read_bytes(struct mosquitto__packet *packet, void *bytes, uint32_t count)
{
assert(packet);
if(packet->pos+count > packet->remaining_length) return MOSQ_ERR_PROTOCOL;
memcpy(bytes, &(packet->payload[packet->pos]), count);
packet->pos += count;
return MOSQ_ERR_SUCCESS;
}
void packet__write_bytes(struct mosquitto__packet *packet, const void *bytes, uint32_t count)
{
assert(packet);
assert(packet->pos+count <= packet->packet_length);
memcpy(&(packet->payload[packet->pos]), bytes, count);
packet->pos += count;
}
int packet__read_binary(struct mosquitto__packet *packet, uint8_t **data, int *length)
{
uint16_t slen;
int rc;
assert(packet);
rc = packet__read_uint16(packet, &slen);
if(rc) return rc;
if(slen == 0){
*data = NULL;
*length = 0;
return MOSQ_ERR_SUCCESS;
}
if(packet->pos+slen > packet->remaining_length) return MOSQ_ERR_PROTOCOL;
*data = mosquitto__malloc(slen+1);
if(*data){
memcpy(*data, &(packet->payload[packet->pos]), slen);
((uint8_t *)(*data))[slen] = '\0';
packet->pos += slen;
}else{
return MOSQ_ERR_NOMEM;
}
*length = slen;
return MOSQ_ERR_SUCCESS;
}
int packet__read_string(struct mosquitto__packet *packet, char **str, int *length)
{
int rc;
rc = packet__read_binary(packet, (uint8_t **)str, length);
if(rc) return rc;
if(*length == 0) return MOSQ_ERR_SUCCESS;
if(mosquitto_validate_utf8(*str, *length)){
mosquitto__free(*str);
*str = NULL;
*length = -1;
return MOSQ_ERR_MALFORMED_UTF8;
}
return MOSQ_ERR_SUCCESS;
}
void packet__write_string(struct mosquitto__packet *packet, const char *str, uint16_t length)
{
assert(packet);
packet__write_uint16(packet, length);
packet__write_bytes(packet, str, length);
}
int packet__read_uint16(struct mosquitto__packet *packet, uint16_t *word)
{
uint8_t msb, lsb;
assert(packet);
if(packet->pos+2 > packet->remaining_length) return MOSQ_ERR_PROTOCOL;
msb = packet->payload[packet->pos];
packet->pos++;
lsb = packet->payload[packet->pos];
packet->pos++;
*word = (msb<<8) + lsb;
return MOSQ_ERR_SUCCESS;
}
void packet__write_uint16(struct mosquitto__packet *packet, uint16_t word)
{
packet__write_byte(packet, MOSQ_MSB(word));
packet__write_byte(packet, MOSQ_LSB(word));
}
int packet__read_uint32(struct mosquitto__packet *packet, uint32_t *word)
{
uint32_t val = 0;
int i;
assert(packet);
if(packet->pos+4 > packet->remaining_length) return MOSQ_ERR_PROTOCOL;
for(i=0; i<4; i++){
val = (val << 8) + packet->payload[packet->pos];
packet->pos++;
}
*word = val;
return MOSQ_ERR_SUCCESS;
}
void packet__write_uint32(struct mosquitto__packet *packet, uint32_t word)
{
packet__write_byte(packet, (word & 0xFF000000) >> 24);
packet__write_byte(packet, (word & 0x00FF0000) >> 16);
packet__write_byte(packet, (word & 0x0000FF00) >> 8);
packet__write_byte(packet, (word & 0x000000FF));
}
int packet__read_varint(struct mosquitto__packet *packet, int32_t *word, int8_t *bytes)
{
int i;
uint8_t byte;
int remaining_mult = 1;
int32_t lword = 0;
uint8_t lbytes = 0;
for(i=0; i<4; i++){
if(packet->pos < packet->remaining_length){
lbytes++;
byte = packet->payload[packet->pos];
lword += (byte & 127) * remaining_mult;
remaining_mult *= 128;
packet->pos++;
if((byte & 128) == 0){
if(lbytes > 1 && byte == 0){
/* Catch overlong encodings */
return MOSQ_ERR_PROTOCOL;
}else{
*word = lword;
if(bytes) (*bytes) = lbytes;
return MOSQ_ERR_SUCCESS;
}
}
}else{
return MOSQ_ERR_PROTOCOL;
}
}
return MOSQ_ERR_PROTOCOL;
}
int packet__write_varint(struct mosquitto__packet *packet, int32_t word)
{
uint8_t byte;
int count = 0;
do{
byte = word % 128;
word = word / 128;
/* If there are more digits to encode, set the top bit of this digit */
if(word > 0){
byte = byte | 0x80;
}
packet__write_byte(packet, byte);
count++;
}while(word > 0 && count < 5);
if(count == 5){
return MOSQ_ERR_PROTOCOL;
}
return MOSQ_ERR_SUCCESS;
}
int packet__varint_bytes(int32_t word)
{
if(word < 128){
return 1;
}else if(word < 16384){
return 2;
}else if(word < 2097152){
return 3;
}else if(word < 268435456){
return 4;
}else{
return 5;
}
}

@ -0,0 +1,498 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#include <errno.h>
#include <string.h>
#ifdef WITH_BROKER
# include "mosquitto_broker_internal.h"
# ifdef WITH_WEBSOCKETS
# include <libwebsockets.h>
# endif
#else
# include "read_handle.h"
#endif
#include "memory_mosq.h"
#include "mqtt_protocol.h"
#include "net_mosq.h"
#include "packet_mosq.h"
#include "read_handle.h"
#include "util_mosq.h"
#ifdef WITH_BROKER
# include "sys_tree.h"
# include "send_mosq.h"
#else
# define G_BYTES_RECEIVED_INC(A)
# define G_BYTES_SENT_INC(A)
# define G_MSGS_SENT_INC(A)
# define G_PUB_MSGS_SENT_INC(A)
#endif
int packet__alloc(struct mosquitto__packet *packet)
{
uint8_t remaining_bytes[5], byte;
uint32_t remaining_length;
int i;
assert(packet);
remaining_length = packet->remaining_length;
packet->payload = NULL;
packet->remaining_count = 0;
do{
byte = remaining_length % 128;
remaining_length = remaining_length / 128;
/* If there are more digits to encode, set the top bit of this digit */
if(remaining_length > 0){
byte = byte | 0x80;
}
remaining_bytes[packet->remaining_count] = byte;
packet->remaining_count++;
}while(remaining_length > 0 && packet->remaining_count < 5);
if(packet->remaining_count == 5) return MOSQ_ERR_PAYLOAD_SIZE;
packet->packet_length = packet->remaining_length + 1 + packet->remaining_count;
#ifdef WITH_WEBSOCKETS
packet->payload = mosquitto__malloc(sizeof(uint8_t)*packet->packet_length + LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING);
#else
packet->payload = mosquitto__malloc(sizeof(uint8_t)*packet->packet_length);
#endif
if(!packet->payload) return MOSQ_ERR_NOMEM;
packet->payload[0] = packet->command;
for(i=0; i<packet->remaining_count; i++){
packet->payload[i+1] = remaining_bytes[i];
}
packet->pos = 1 + packet->remaining_count;
return MOSQ_ERR_SUCCESS;
}
void packet__cleanup(struct mosquitto__packet *packet)
{
if(!packet) return;
/* Free data and reset values */
packet->command = 0;
packet->remaining_count = 0;
packet->remaining_mult = 1;
packet->remaining_length = 0;
mosquitto__free(packet->payload);
packet->payload = NULL;
packet->to_process = 0;
packet->pos = 0;
}
void packet__cleanup_all(struct mosquitto *mosq)
{
struct mosquitto__packet *packet;
pthread_mutex_lock(&mosq->current_out_packet_mutex);
pthread_mutex_lock(&mosq->out_packet_mutex);
/* Out packet cleanup */
if(mosq->out_packet && !mosq->current_out_packet){
mosq->current_out_packet = mosq->out_packet;
mosq->out_packet = mosq->out_packet->next;
}
while(mosq->current_out_packet){
packet = mosq->current_out_packet;
/* Free data and reset values */
mosq->current_out_packet = mosq->out_packet;
if(mosq->out_packet){
mosq->out_packet = mosq->out_packet->next;
}
packet__cleanup(packet);
mosquitto__free(packet);
}
packet__cleanup(&mosq->in_packet);
pthread_mutex_unlock(&mosq->out_packet_mutex);
pthread_mutex_unlock(&mosq->current_out_packet_mutex);
}
int packet__queue(struct mosquitto *mosq, struct mosquitto__packet *packet)
{
#ifndef WITH_BROKER
char sockpair_data = 0;
#endif
assert(mosq);
assert(packet);
packet->pos = 0;
packet->to_process = packet->packet_length;
packet->next = NULL;
pthread_mutex_lock(&mosq->out_packet_mutex);
if(mosq->out_packet){
mosq->out_packet_last->next = packet;
}else{
mosq->out_packet = packet;
}
mosq->out_packet_last = packet;
pthread_mutex_unlock(&mosq->out_packet_mutex);
#ifdef WITH_BROKER
# ifdef WITH_WEBSOCKETS
if(mosq->wsi){
libwebsocket_callback_on_writable(mosq->ws_context, mosq->wsi);
return MOSQ_ERR_SUCCESS;
}else{
return packet__write(mosq);
}
# else
return packet__write(mosq);
# endif
#else
/* Write a single byte to sockpairW (connected to sockpairR) to break out
* of select() if in threaded mode. */
if(mosq->sockpairW != INVALID_SOCKET){
#ifndef WIN32
if(write(mosq->sockpairW, &sockpair_data, 1)){
}
#else
send(mosq->sockpairW, &sockpair_data, 1, 0);
#endif
}
if(mosq->in_callback == false && mosq->threaded == mosq_ts_none){
return packet__write(mosq);
}else{
return MOSQ_ERR_SUCCESS;
}
#endif
}
int packet__check_oversize(struct mosquitto *mosq, uint32_t remaining_length)
{
uint32_t len;
if(mosq->maximum_packet_size == 0) return MOSQ_ERR_SUCCESS;
len = remaining_length + packet__varint_bytes(remaining_length);
if(len > mosq->maximum_packet_size){
return MOSQ_ERR_OVERSIZE_PACKET;
}else{
return MOSQ_ERR_SUCCESS;
}
}
int packet__write(struct mosquitto *mosq)
{
ssize_t write_length;
struct mosquitto__packet *packet;
int state;
if(!mosq) return MOSQ_ERR_INVAL;
if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN;
pthread_mutex_lock(&mosq->current_out_packet_mutex);
pthread_mutex_lock(&mosq->out_packet_mutex);
if(mosq->out_packet && !mosq->current_out_packet){
mosq->current_out_packet = mosq->out_packet;
mosq->out_packet = mosq->out_packet->next;
if(!mosq->out_packet){
mosq->out_packet_last = NULL;
}
}
pthread_mutex_unlock(&mosq->out_packet_mutex);
state = mosquitto__get_state(mosq);
#if defined(WITH_TLS) && !defined(WITH_BROKER)
if((state == mosq_cs_connect_pending) || mosq->want_connect){
#else
if(state == mosq_cs_connect_pending){
#endif
pthread_mutex_unlock(&mosq->current_out_packet_mutex);
return MOSQ_ERR_SUCCESS;
}
while(mosq->current_out_packet){
packet = mosq->current_out_packet;
while(packet->to_process > 0){
write_length = net__write(mosq, &(packet->payload[packet->pos]), packet->to_process);
if(write_length > 0){
G_BYTES_SENT_INC(write_length);
packet->to_process -= write_length;
packet->pos += write_length;
}else{
#ifdef WIN32
errno = WSAGetLastError();
#endif
if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK
#ifdef WIN32
|| errno == WSAENOTCONN
#endif
){
pthread_mutex_unlock(&mosq->current_out_packet_mutex);
return MOSQ_ERR_SUCCESS;
}else{
pthread_mutex_unlock(&mosq->current_out_packet_mutex);
switch(errno){
case COMPAT_ECONNRESET:
return MOSQ_ERR_CONN_LOST;
default:
return MOSQ_ERR_ERRNO;
}
}
}
}
G_MSGS_SENT_INC(1);
if(((packet->command)&0xF6) == CMD_PUBLISH){
G_PUB_MSGS_SENT_INC(1);
#ifndef WITH_BROKER
pthread_mutex_lock(&mosq->callback_mutex);
if(mosq->on_publish){
/* This is a QoS=0 message */
mosq->in_callback = true;
mosq->on_publish(mosq, mosq->userdata, packet->mid);
mosq->in_callback = false;
}
if(mosq->on_publish_v5){
/* This is a QoS=0 message */
mosq->in_callback = true;
mosq->on_publish_v5(mosq, mosq->userdata, packet->mid, 0, NULL);
mosq->in_callback = false;
}
pthread_mutex_unlock(&mosq->callback_mutex);
}else if(((packet->command)&0xF0) == CMD_DISCONNECT){
do_client_disconnect(mosq, MOSQ_ERR_SUCCESS, NULL);
packet__cleanup(packet);
mosquitto__free(packet);
return MOSQ_ERR_SUCCESS;
#endif
}
/* Free data and reset values */
pthread_mutex_lock(&mosq->out_packet_mutex);
mosq->current_out_packet = mosq->out_packet;
if(mosq->out_packet){
mosq->out_packet = mosq->out_packet->next;
if(!mosq->out_packet){
mosq->out_packet_last = NULL;
}
}
pthread_mutex_unlock(&mosq->out_packet_mutex);
packet__cleanup(packet);
mosquitto__free(packet);
pthread_mutex_lock(&mosq->msgtime_mutex);
mosq->next_msg_out = mosquitto_time() + mosq->keepalive;
pthread_mutex_unlock(&mosq->msgtime_mutex);
}
pthread_mutex_unlock(&mosq->current_out_packet_mutex);
return MOSQ_ERR_SUCCESS;
}
#ifdef WITH_BROKER
int packet__read(struct mosquitto_db *db, struct mosquitto *mosq)
#else
int packet__read(struct mosquitto *mosq)
#endif
{
uint8_t byte;
ssize_t read_length;
int rc = 0;
int state;
if(!mosq){
return MOSQ_ERR_INVAL;
}
if(mosq->sock == INVALID_SOCKET){
return MOSQ_ERR_NO_CONN;
}
state = mosquitto__get_state(mosq);
if(state == mosq_cs_connect_pending){
return MOSQ_ERR_SUCCESS;
}
/* This gets called if pselect() indicates that there is network data
* available - ie. at least one byte. What we do depends on what data we
* already have.
* If we've not got a command, attempt to read one and save it. This should
* always work because it's only a single byte.
* Then try to read the remaining length. This may fail because it is may
* be more than one byte - will need to save data pending next read if it
* does fail.
* Then try to read the remaining payload, where 'payload' here means the
* combined variable header and actual payload. This is the most likely to
* fail due to longer length, so save current data and current position.
* After all data is read, send to mosquitto__handle_packet() to deal with.
* Finally, free the memory and reset everything to starting conditions.
*/
if(!mosq->in_packet.command){
read_length = net__read(mosq, &byte, 1);
if(read_length == 1){
mosq->in_packet.command = byte;
#ifdef WITH_BROKER
G_BYTES_RECEIVED_INC(1);
/* Clients must send CONNECT as their first command. */
if(!(mosq->bridge) && mosq->state == mosq_cs_connected && (byte&0xF0) != CMD_CONNECT){
return MOSQ_ERR_PROTOCOL;
}
#endif
}else{
if(read_length == 0){
return MOSQ_ERR_CONN_LOST; /* EOF */
}
#ifdef WIN32
errno = WSAGetLastError();
#endif
if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){
return MOSQ_ERR_SUCCESS;
}else{
switch(errno){
case COMPAT_ECONNRESET:
return MOSQ_ERR_CONN_LOST;
default:
return MOSQ_ERR_ERRNO;
}
}
}
}
/* remaining_count is the number of bytes that the remaining_length
* parameter occupied in this incoming packet. We don't use it here as such
* (it is used when allocating an outgoing packet), but we must be able to
* determine whether all of the remaining_length parameter has been read.
* remaining_count has three states here:
* 0 means that we haven't read any remaining_length bytes
* <0 means we have read some remaining_length bytes but haven't finished
* >0 means we have finished reading the remaining_length bytes.
*/
if(mosq->in_packet.remaining_count <= 0){
do{
read_length = net__read(mosq, &byte, 1);
if(read_length == 1){
mosq->in_packet.remaining_count--;
/* Max 4 bytes length for remaining length as defined by protocol.
* Anything more likely means a broken/malicious client.
*/
if(mosq->in_packet.remaining_count < -4){
return MOSQ_ERR_PROTOCOL;
}
G_BYTES_RECEIVED_INC(1);
mosq->in_packet.remaining_length += (byte & 127) * mosq->in_packet.remaining_mult;
mosq->in_packet.remaining_mult *= 128;
}else{
if(read_length == 0){
return MOSQ_ERR_CONN_LOST; /* EOF */
}
#ifdef WIN32
errno = WSAGetLastError();
#endif
if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){
return MOSQ_ERR_SUCCESS;
}else{
switch(errno){
case COMPAT_ECONNRESET:
return MOSQ_ERR_CONN_LOST;
default:
return MOSQ_ERR_ERRNO;
}
}
}
}while((byte & 128) != 0);
/* We have finished reading remaining_length, so make remaining_count
* positive. */
mosq->in_packet.remaining_count *= -1;
#ifdef WITH_BROKER
if(db->config->max_packet_size > 0 && mosq->in_packet.remaining_length+1 > db->config->max_packet_size){
log__printf(NULL, MOSQ_LOG_INFO, "Client %s sent too large packet %d, disconnecting.", mosq->id, mosq->in_packet.remaining_length+1);
if(mosq->protocol == mosq_p_mqtt5){
send__disconnect(mosq, MQTT_RC_PACKET_TOO_LARGE, NULL);
}
return MOSQ_ERR_OVERSIZE_PACKET;
}
#else
// FIXME - client case for incoming message received from broker too large
#endif
if(mosq->in_packet.remaining_length > 0){
mosq->in_packet.payload = mosquitto__malloc(mosq->in_packet.remaining_length*sizeof(uint8_t));
if(!mosq->in_packet.payload){
return MOSQ_ERR_NOMEM;
}
mosq->in_packet.to_process = mosq->in_packet.remaining_length;
}
}
while(mosq->in_packet.to_process>0){
read_length = net__read(mosq, &(mosq->in_packet.payload[mosq->in_packet.pos]), mosq->in_packet.to_process);
if(read_length > 0){
G_BYTES_RECEIVED_INC(read_length);
mosq->in_packet.to_process -= read_length;
mosq->in_packet.pos += read_length;
}else{
#ifdef WIN32
errno = WSAGetLastError();
#endif
if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){
if(mosq->in_packet.to_process > 1000){
/* Update last_msg_in time if more than 1000 bytes left to
* receive. Helps when receiving large messages.
* This is an arbitrary limit, but with some consideration.
* If a client can't send 1000 bytes in a second it
* probably shouldn't be using a 1 second keep alive. */
pthread_mutex_lock(&mosq->msgtime_mutex);
mosq->last_msg_in = mosquitto_time();
pthread_mutex_unlock(&mosq->msgtime_mutex);
}
return MOSQ_ERR_SUCCESS;
}else{
switch(errno){
case COMPAT_ECONNRESET:
return MOSQ_ERR_CONN_LOST;
default:
return MOSQ_ERR_ERRNO;
}
}
}
}
/* All data for this packet is read. */
mosq->in_packet.pos = 0;
#ifdef WITH_BROKER
G_MSGS_RECEIVED_INC(1);
if(((mosq->in_packet.command)&0xF5) == CMD_PUBLISH){
G_PUB_MSGS_RECEIVED_INC(1);
}
rc = handle__packet(db, mosq);
#else
rc = handle__packet(mosq);
#endif
/* Free data and reset values */
packet__cleanup(&mosq->in_packet);
pthread_mutex_lock(&mosq->msgtime_mutex);
mosq->last_msg_in = mosquitto_time();
pthread_mutex_unlock(&mosq->msgtime_mutex);
return rc;
}

@ -0,0 +1,57 @@
/*
Copyright (c) 2010-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#ifndef PACKET_MOSQ_H
#define PACKET_MOSQ_H
#include "mosquitto_internal.h"
#include "mosquitto.h"
#ifdef WITH_BROKER
struct mosquitto_db;
#endif
int packet__alloc(struct mosquitto__packet *packet);
void packet__cleanup(struct mosquitto__packet *packet);
void packet__cleanup_all(struct mosquitto *mosq);
int packet__queue(struct mosquitto *mosq, struct mosquitto__packet *packet);
int packet__check_oversize(struct mosquitto *mosq, uint32_t remaining_length);
int packet__read_byte(struct mosquitto__packet *packet, uint8_t *byte);
int packet__read_bytes(struct mosquitto__packet *packet, void *bytes, uint32_t count);
int packet__read_binary(struct mosquitto__packet *packet, uint8_t **data, int *length);
int packet__read_string(struct mosquitto__packet *packet, char **str, int *length);
int packet__read_uint16(struct mosquitto__packet *packet, uint16_t *word);
int packet__read_uint32(struct mosquitto__packet *packet, uint32_t *word);
int packet__read_varint(struct mosquitto__packet *packet, int32_t *word, int8_t *bytes);
void packet__write_byte(struct mosquitto__packet *packet, uint8_t byte);
void packet__write_bytes(struct mosquitto__packet *packet, const void *bytes, uint32_t count);
void packet__write_string(struct mosquitto__packet *packet, const char *str, uint16_t length);
void packet__write_uint16(struct mosquitto__packet *packet, uint16_t word);
void packet__write_uint32(struct mosquitto__packet *packet, uint32_t word);
int packet__write_varint(struct mosquitto__packet *packet, int32_t word);
int packet__varint_bytes(int32_t word);
int packet__write(struct mosquitto *mosq);
#ifdef WITH_BROKER
int packet__read(struct mosquitto_db *db, struct mosquitto *mosq);
#else
int packet__read(struct mosquitto *mosq);
#endif
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,50 @@
/*
Copyright (c) 2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#ifndef PROPERTY_MOSQ_H
#define PROPERTY_MOSQ_H
#include "mosquitto_internal.h"
#include "mosquitto.h"
struct mqtt__string {
char *v;
int len;
};
struct mqtt5__property {
struct mqtt5__property *next;
union {
uint8_t i8;
uint16_t i16;
uint32_t i32;
uint32_t varint;
struct mqtt__string bin;
struct mqtt__string s;
} value;
struct mqtt__string name;
int32_t identifier;
bool client_generated;
};
int property__read_all(int command, struct mosquitto__packet *packet, mosquitto_property **property);
int property__write_all(struct mosquitto__packet *packet, const mosquitto_property *property, bool write_len);
void property__free(mosquitto_property **property);
int property__get_length(const mosquitto_property *property);
int property__get_length_all(const mosquitto_property *property);
#endif

@ -0,0 +1,70 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "mosquitto.h"
#include "logging_mosq.h"
#include "memory_mosq.h"
#include "messages_mosq.h"
#include "mqtt_protocol.h"
#include "net_mosq.h"
#include "packet_mosq.h"
#include "read_handle.h"
#include "send_mosq.h"
#include "time_mosq.h"
#include "util_mosq.h"
int handle__packet(struct mosquitto *mosq)
{
assert(mosq);
switch((mosq->in_packet.command)&0xF0){
case CMD_PINGREQ:
return handle__pingreq(mosq);
case CMD_PINGRESP:
return handle__pingresp(mosq);
case CMD_PUBACK:
return handle__pubackcomp(mosq, "PUBACK");
case CMD_PUBCOMP:
return handle__pubackcomp(mosq, "PUBCOMP");
case CMD_PUBLISH:
return handle__publish(mosq);
case CMD_PUBREC:
return handle__pubrec(NULL, mosq);
case CMD_PUBREL:
return handle__pubrel(NULL, mosq);
case CMD_CONNACK:
return handle__connack(mosq);
case CMD_SUBACK:
return handle__suback(mosq);
case CMD_UNSUBACK:
return handle__unsuback(mosq);
case CMD_DISCONNECT:
return handle__disconnect(mosq);
case CMD_AUTH:
return handle__auth(mosq);
default:
/* If we don't recognise the command, return an error straight away. */
log__printf(mosq, MOSQ_LOG_ERR, "Error: Unrecognised command %d\n", (mosq->in_packet.command)&0xF0);
return MOSQ_ERR_PROTOCOL;
}
}

@ -0,0 +1,40 @@
/*
Copyright (c) 2010-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#ifndef READ_HANDLE_H
#define READ_HANDLE_H
#include "mosquitto.h"
struct mosquitto_db;
int handle__pingreq(struct mosquitto *mosq);
int handle__pingresp(struct mosquitto *mosq);
#ifdef WITH_BROKER
int handle__pubackcomp(struct mosquitto_db *db, struct mosquitto *mosq, const char *type);
#else
int handle__packet(struct mosquitto *mosq);
int handle__connack(struct mosquitto *mosq);
int handle__disconnect(struct mosquitto *mosq);
int handle__pubackcomp(struct mosquitto *mosq, const char *type);
int handle__publish(struct mosquitto *mosq);
int handle__auth(struct mosquitto *mosq);
#endif
int handle__pubrec(struct mosquitto_db *db, struct mosquitto *mosq);
int handle__pubrel(struct mosquitto_db *db, struct mosquitto *mosq);
int handle__suback(struct mosquitto *mosq);
int handle__unsuback(struct mosquitto *mosq);
#endif

@ -0,0 +1,205 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#include <string.h>
#ifdef WITH_BROKER
# include "mosquitto_broker_internal.h"
#endif
#include "logging_mosq.h"
#include "memory_mosq.h"
#include "mosquitto.h"
#include "mosquitto_internal.h"
#include "mqtt_protocol.h"
#include "packet_mosq.h"
#include "property_mosq.h"
int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session, const mosquitto_property *properties)
{
struct mosquitto__packet *packet = NULL;
int payloadlen;
uint8_t will = 0;
uint8_t byte;
int rc;
uint8_t version;
char *clientid, *username, *password;
int headerlen;
int proplen = 0, will_proplen, varbytes;
mosquitto_property *local_props = NULL;
uint16_t receive_maximum;
assert(mosq);
if(mosq->protocol == mosq_p_mqtt31 && !mosq->id) return MOSQ_ERR_PROTOCOL;
#if defined(WITH_BROKER) && defined(WITH_BRIDGE)
if(mosq->bridge){
clientid = mosq->bridge->remote_clientid;
username = mosq->bridge->remote_username;
password = mosq->bridge->remote_password;
}else{
clientid = mosq->id;
username = mosq->username;
password = mosq->password;
}
#else
clientid = mosq->id;
username = mosq->username;
password = mosq->password;
#endif
if(mosq->protocol == mosq_p_mqtt5){
/* Generate properties from options */
if(!mosquitto_property_read_int16(properties, MQTT_PROP_RECEIVE_MAXIMUM, &receive_maximum, false)){
rc = mosquitto_property_add_int16(&local_props, MQTT_PROP_RECEIVE_MAXIMUM, mosq->msgs_in.inflight_maximum);
if(rc) return rc;
}else{
mosq->msgs_in.inflight_maximum = receive_maximum;
mosq->msgs_in.inflight_quota = receive_maximum;
}
version = MQTT_PROTOCOL_V5;
headerlen = 10;
proplen = 0;
proplen += property__get_length_all(properties);
proplen += property__get_length_all(local_props);
varbytes = packet__varint_bytes(proplen);
headerlen += proplen + varbytes;
}else if(mosq->protocol == mosq_p_mqtt311){
version = MQTT_PROTOCOL_V311;
headerlen = 10;
}else if(mosq->protocol == mosq_p_mqtt31){
version = MQTT_PROTOCOL_V31;
headerlen = 12;
}else{
return MOSQ_ERR_INVAL;
}
packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet));
if(!packet) return MOSQ_ERR_NOMEM;
if(clientid){
payloadlen = 2+strlen(clientid);
}else{
payloadlen = 2;
}
if(mosq->will){
will = 1;
assert(mosq->will->msg.topic);
payloadlen += 2+strlen(mosq->will->msg.topic) + 2+mosq->will->msg.payloadlen;
if(mosq->protocol == mosq_p_mqtt5){
will_proplen = property__get_length_all(mosq->will->properties);
varbytes = packet__varint_bytes(will_proplen);
payloadlen += will_proplen + varbytes;
}
}
/* After this check we can be sure that the username and password are
* always valid for the current protocol, so there is no need to check
* username before checking password. */
if(mosq->protocol == mosq_p_mqtt31 || mosq->protocol == mosq_p_mqtt311){
if(password != NULL && username == NULL){
return MOSQ_ERR_INVAL;
}
}
if(username){
payloadlen += 2+strlen(username);
}
if(password){
payloadlen += 2+strlen(password);
}
packet->command = CMD_CONNECT;
packet->remaining_length = headerlen + payloadlen;
rc = packet__alloc(packet);
if(rc){
mosquitto__free(packet);
return rc;
}
/* Variable header */
if(version == MQTT_PROTOCOL_V31){
packet__write_string(packet, PROTOCOL_NAME_v31, strlen(PROTOCOL_NAME_v31));
}else{
packet__write_string(packet, PROTOCOL_NAME, strlen(PROTOCOL_NAME));
}
#if defined(WITH_BROKER) && defined(WITH_BRIDGE)
if(mosq->bridge && mosq->bridge->try_private && mosq->bridge->try_private_accepted){
version |= 0x80;
}else{
}
#endif
packet__write_byte(packet, version);
byte = (clean_session&0x1)<<1;
if(will){
byte = byte | ((mosq->will->msg.retain&0x1)<<5) | ((mosq->will->msg.qos&0x3)<<3) | ((will&0x1)<<2);
}
if(username){
byte = byte | 0x1<<7;
}
if(mosq->password){
byte = byte | 0x1<<6;
}
packet__write_byte(packet, byte);
packet__write_uint16(packet, keepalive);
if(mosq->protocol == mosq_p_mqtt5){
/* Write properties */
packet__write_varint(packet, proplen);
property__write_all(packet, properties, false);
property__write_all(packet, local_props, false);
}
mosquitto_property_free_all(&local_props);
/* Payload */
if(clientid){
packet__write_string(packet, clientid, strlen(clientid));
}else{
packet__write_uint16(packet, 0);
}
if(will){
if(mosq->protocol == mosq_p_mqtt5){
/* Write will properties */
property__write_all(packet, mosq->will->properties, true);
}
packet__write_string(packet, mosq->will->msg.topic, strlen(mosq->will->msg.topic));
packet__write_string(packet, (const char *)mosq->will->msg.payload, mosq->will->msg.payloadlen);
}
if(username){
packet__write_string(packet, username, strlen(username));
}
if(password){
packet__write_string(packet, password, strlen(password));
}
mosq->keepalive = keepalive;
#ifdef WITH_BROKER
# ifdef WITH_BRIDGE
log__printf(mosq, MOSQ_LOG_DEBUG, "Bridge %s sending CONNECT", clientid);
# endif
#else
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending CONNECT", clientid);
#endif
return packet__queue(mosq, packet);
}

@ -0,0 +1,85 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#ifdef WITH_BROKER
# include "mosquitto_broker_internal.h"
#endif
#include "mosquitto.h"
#include "mosquitto_internal.h"
#include "logging_mosq.h"
#include "memory_mosq.h"
#include "mqtt_protocol.h"
#include "packet_mosq.h"
#include "property_mosq.h"
#include "send_mosq.h"
int send__disconnect(struct mosquitto *mosq, uint8_t reason_code, const mosquitto_property *properties)
{
struct mosquitto__packet *packet = NULL;
int rc;
int proplen, varbytes;
assert(mosq);
#ifdef WITH_BROKER
# ifdef WITH_BRIDGE
if(mosq->bridge){
log__printf(mosq, MOSQ_LOG_DEBUG, "Bridge %s sending DISCONNECT", mosq->id);
}else
# else
{
log__printf(mosq, MOSQ_LOG_DEBUG, "Sending DISCONNECT to %s (rc%d)", mosq->id, reason_code);
}
# endif
#else
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending DISCONNECT", mosq->id);
#endif
assert(mosq);
packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet));
if(!packet) return MOSQ_ERR_NOMEM;
packet->command = CMD_DISCONNECT;
if(mosq->protocol == mosq_p_mqtt5 && (reason_code != 0 || properties)){
packet->remaining_length = 1;
if(properties){
proplen = property__get_length_all(properties);
varbytes = packet__varint_bytes(proplen);
packet->remaining_length += proplen + varbytes;
}
}else{
packet->remaining_length = 0;
}
rc = packet__alloc(packet);
if(rc){
mosquitto__free(packet);
return rc;
}
if(mosq->protocol == mosq_p_mqtt5 && (reason_code != 0 || properties)){
packet__write_byte(packet, reason_code);
if(properties){
property__write_all(packet, properties, true);
}
}
return packet__queue(mosq, packet);
}

@ -0,0 +1,188 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#ifdef WITH_BROKER
# include "mosquitto_broker_internal.h"
# include "sys_tree.h"
#else
# define G_PUB_BYTES_SENT_INC(A)
#endif
#include "mosquitto.h"
#include "mosquitto_internal.h"
#include "logging_mosq.h"
#include "mqtt_protocol.h"
#include "memory_mosq.h"
#include "net_mosq.h"
#include "packet_mosq.h"
#include "property_mosq.h"
#include "send_mosq.h"
#include "time_mosq.h"
#include "util_mosq.h"
int send__pingreq(struct mosquitto *mosq)
{
int rc;
assert(mosq);
#ifdef WITH_BROKER
log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PINGREQ to %s", mosq->id);
#else
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PINGREQ", mosq->id);
#endif
rc = send__simple_command(mosq, CMD_PINGREQ);
if(rc == MOSQ_ERR_SUCCESS){
mosq->ping_t = mosquitto_time();
}
return rc;
}
int send__pingresp(struct mosquitto *mosq)
{
#ifdef WITH_BROKER
log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PINGRESP to %s", mosq->id);
#else
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PINGRESP", mosq->id);
#endif
return send__simple_command(mosq, CMD_PINGRESP);
}
int send__puback(struct mosquitto *mosq, uint16_t mid, uint8_t reason_code)
{
#ifdef WITH_BROKER
log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBACK to %s (m%d, rc%d)", mosq->id, mid, reason_code);
#else
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBACK (m%d, rc%d)", mosq->id, mid, reason_code);
#endif
util__increment_receive_quota(mosq);
/* We don't use Reason String or User Property yet. */
return send__command_with_mid(mosq, CMD_PUBACK, mid, false, reason_code, NULL);
}
int send__pubcomp(struct mosquitto *mosq, uint16_t mid)
{
#ifdef WITH_BROKER
log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBCOMP to %s (m%d)", mosq->id, mid);
#else
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBCOMP (m%d)", mosq->id, mid);
#endif
util__increment_receive_quota(mosq);
/* We don't use Reason String or User Property yet. */
return send__command_with_mid(mosq, CMD_PUBCOMP, mid, false, 0, NULL);
}
int send__pubrec(struct mosquitto *mosq, uint16_t mid, uint8_t reason_code)
{
#ifdef WITH_BROKER
log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBREC to %s (m%d, rc%d)", mosq->id, mid, reason_code);
#else
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBREC (m%d, rc%d)", mosq->id, mid, reason_code);
#endif
if(reason_code >= 0x80 && mosq->protocol == mosq_p_mqtt5){
util__increment_receive_quota(mosq);
}
/* We don't use Reason String or User Property yet. */
return send__command_with_mid(mosq, CMD_PUBREC, mid, false, reason_code, NULL);
}
int send__pubrel(struct mosquitto *mosq, uint16_t mid)
{
#ifdef WITH_BROKER
log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBREL to %s (m%d)", mosq->id, mid);
#else
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBREL (m%d)", mosq->id, mid);
#endif
/* We don't use Reason String or User Property yet. */
return send__command_with_mid(mosq, CMD_PUBREL|2, mid, false, 0, NULL);
}
/* For PUBACK, PUBCOMP, PUBREC, and PUBREL */
int send__command_with_mid(struct mosquitto *mosq, uint8_t command, uint16_t mid, bool dup, uint8_t reason_code, const mosquitto_property *properties)
{
struct mosquitto__packet *packet = NULL;
int rc;
int proplen, varbytes;
assert(mosq);
packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet));
if(!packet) return MOSQ_ERR_NOMEM;
packet->command = command;
if(dup){
packet->command |= 8;
}
packet->remaining_length = 2;
if(mosq->protocol == mosq_p_mqtt5){
if(reason_code != 0 || properties){
packet->remaining_length += 1;
}
if(properties){
proplen = property__get_length_all(properties);
varbytes = packet__varint_bytes(proplen);
packet->remaining_length += varbytes + proplen;
}
}
rc = packet__alloc(packet);
if(rc){
mosquitto__free(packet);
return rc;
}
packet__write_uint16(packet, mid);
if(mosq->protocol == mosq_p_mqtt5){
if(reason_code != 0 || properties){
packet__write_byte(packet, reason_code);
}
if(properties){
property__write_all(packet, properties, true);
}
}
return packet__queue(mosq, packet);
}
/* For DISCONNECT, PINGREQ and PINGRESP */
int send__simple_command(struct mosquitto *mosq, uint8_t command)
{
struct mosquitto__packet *packet = NULL;
int rc;
assert(mosq);
packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet));
if(!packet) return MOSQ_ERR_NOMEM;
packet->command = command;
packet->remaining_length = 0;
rc = packet__alloc(packet);
if(rc){
mosquitto__free(packet);
return rc;
}
return packet__queue(mosq, packet);
}

@ -0,0 +1,38 @@
/*
Copyright (c) 2010-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#ifndef SEND_MOSQ_H
#define SEND_MOSQ_H
#include "mosquitto.h"
#include "property_mosq.h"
int send__simple_command(struct mosquitto *mosq, uint8_t command);
int send__command_with_mid(struct mosquitto *mosq, uint8_t command, uint16_t mid, bool dup, uint8_t reason_code, const mosquitto_property *properties);
int send__real_publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup, const mosquitto_property *cmsg_props, const mosquitto_property *store_props, uint32_t expiry_interval);
int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session, const mosquitto_property *properties);
int send__disconnect(struct mosquitto *mosq, uint8_t reason_code, const mosquitto_property *properties);
int send__pingreq(struct mosquitto *mosq);
int send__pingresp(struct mosquitto *mosq);
int send__puback(struct mosquitto *mosq, uint16_t mid, uint8_t reason_code);
int send__pubcomp(struct mosquitto *mosq, uint16_t mid);
int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup, const mosquitto_property *cmsg_props, const mosquitto_property *store_props, uint32_t expiry_interval);
int send__pubrec(struct mosquitto *mosq, uint16_t mid, uint8_t reason_code);
int send__pubrel(struct mosquitto *mosq, uint16_t mid);
int send__subscribe(struct mosquitto *mosq, int *mid, int topic_count, char *const *const topic, int topic_qos, const mosquitto_property *properties);
int send__unsubscribe(struct mosquitto *mosq, int *mid, int topic_count, char *const *const topic, const mosquitto_property *properties);
#endif

@ -0,0 +1,215 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#include <string.h>
#ifdef WITH_BROKER
# include "mosquitto_broker_internal.h"
# include "sys_tree.h"
#else
# define G_PUB_BYTES_SENT_INC(A)
#endif
#include "mosquitto.h"
#include "mosquitto_internal.h"
#include "logging_mosq.h"
#include "mqtt_protocol.h"
#include "memory_mosq.h"
#include "net_mosq.h"
#include "packet_mosq.h"
#include "property_mosq.h"
#include "send_mosq.h"
int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup, const mosquitto_property *cmsg_props, const mosquitto_property *store_props, uint32_t expiry_interval)
{
#ifdef WITH_BROKER
size_t len;
#ifdef WITH_BRIDGE
int i;
struct mosquitto__bridge_topic *cur_topic;
bool match;
int rc;
char *mapped_topic = NULL;
char *topic_temp = NULL;
#endif
#endif
assert(mosq);
#if defined(WITH_BROKER) && defined(WITH_WEBSOCKETS)
if(mosq->sock == INVALID_SOCKET && !mosq->wsi) return MOSQ_ERR_NO_CONN;
#else
if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN;
#endif
#ifdef WITH_BROKER
if(mosq->listener && mosq->listener->mount_point){
len = strlen(mosq->listener->mount_point);
if(len < strlen(topic)){
topic += len;
}else{
/* Invalid topic string. Should never happen, but silently swallow the message anyway. */
return MOSQ_ERR_SUCCESS;
}
}
#ifdef WITH_BRIDGE
if(mosq->bridge && mosq->bridge->topics && mosq->bridge->topic_remapping){
for(i=0; i<mosq->bridge->topic_count; i++){
cur_topic = &mosq->bridge->topics[i];
if((cur_topic->direction == bd_both || cur_topic->direction == bd_out)
&& (cur_topic->remote_prefix || cur_topic->local_prefix)){
/* Topic mapping required on this topic if the message matches */
rc = mosquitto_topic_matches_sub(cur_topic->local_topic, topic, &match);
if(rc){
return rc;
}
if(match){
mapped_topic = mosquitto__strdup(topic);
if(!mapped_topic) return MOSQ_ERR_NOMEM;
if(cur_topic->local_prefix){
/* This prefix needs removing. */
if(!strncmp(cur_topic->local_prefix, mapped_topic, strlen(cur_topic->local_prefix))){
topic_temp = mosquitto__strdup(mapped_topic+strlen(cur_topic->local_prefix));
mosquitto__free(mapped_topic);
if(!topic_temp){
return MOSQ_ERR_NOMEM;
}
mapped_topic = topic_temp;
}
}
if(cur_topic->remote_prefix){
/* This prefix needs adding. */
len = strlen(mapped_topic) + strlen(cur_topic->remote_prefix)+1;
topic_temp = mosquitto__malloc(len+1);
if(!topic_temp){
mosquitto__free(mapped_topic);
return MOSQ_ERR_NOMEM;
}
snprintf(topic_temp, len, "%s%s", cur_topic->remote_prefix, mapped_topic);
topic_temp[len] = '\0';
mosquitto__free(mapped_topic);
mapped_topic = topic_temp;
}
log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBLISH to %s (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))", mosq->id, dup, qos, retain, mid, mapped_topic, (long)payloadlen);
G_PUB_BYTES_SENT_INC(payloadlen);
rc = send__real_publish(mosq, mid, mapped_topic, payloadlen, payload, qos, retain, dup, cmsg_props, store_props, expiry_interval);
mosquitto__free(mapped_topic);
return rc;
}
}
}
}
#endif
log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBLISH to %s (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))", mosq->id, dup, qos, retain, mid, topic, (long)payloadlen);
G_PUB_BYTES_SENT_INC(payloadlen);
#else
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBLISH (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))", mosq->id, dup, qos, retain, mid, topic, (long)payloadlen);
#endif
return send__real_publish(mosq, mid, topic, payloadlen, payload, qos, retain, dup, cmsg_props, store_props, expiry_interval);
}
int send__real_publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup, const mosquitto_property *cmsg_props, const mosquitto_property *store_props, uint32_t expiry_interval)
{
struct mosquitto__packet *packet = NULL;
int packetlen;
int proplen = 0, varbytes;
int rc;
mosquitto_property expiry_prop;
assert(mosq);
if(topic){
packetlen = 2+strlen(topic) + payloadlen;
}else{
packetlen = 2 + payloadlen;
}
if(qos > 0) packetlen += 2; /* For message id */
if(mosq->protocol == mosq_p_mqtt5){
proplen = 0;
proplen += property__get_length_all(cmsg_props);
proplen += property__get_length_all(store_props);
if(expiry_interval > 0){
expiry_prop.next = NULL;
expiry_prop.value.i32 = expiry_interval;
expiry_prop.identifier = MQTT_PROP_MESSAGE_EXPIRY_INTERVAL;
expiry_prop.client_generated = false;
proplen += property__get_length_all(&expiry_prop);
}
varbytes = packet__varint_bytes(proplen);
if(varbytes > 4){
/* FIXME - Properties too big, don't publish any - should remove some first really */
cmsg_props = NULL;
store_props = NULL;
expiry_interval = 0;
}else{
packetlen += proplen + varbytes;
}
}
if(packet__check_oversize(mosq, packetlen)){
#ifdef WITH_BROKER
log__printf(NULL, MOSQ_LOG_NOTICE, "Dropping too large outgoing PUBLISH for %s (%d bytes)", mosq->id, packetlen);
#else
log__printf(NULL, MOSQ_LOG_NOTICE, "Dropping too large outgoing PUBLISH (%d bytes)", packetlen);
#endif
return MOSQ_ERR_OVERSIZE_PACKET;
}
packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet));
if(!packet) return MOSQ_ERR_NOMEM;
packet->mid = mid;
packet->command = CMD_PUBLISH | ((dup&0x1)<<3) | (qos<<1) | retain;
packet->remaining_length = packetlen;
rc = packet__alloc(packet);
if(rc){
mosquitto__free(packet);
return rc;
}
/* Variable header (topic string) */
if(topic){
packet__write_string(packet, topic, strlen(topic));
}else{
packet__write_uint16(packet, 0);
}
if(qos > 0){
packet__write_uint16(packet, mid);
}
if(mosq->protocol == mosq_p_mqtt5){
packet__write_varint(packet, proplen);
property__write_all(packet, cmsg_props, false);
property__write_all(packet, store_props, false);
if(expiry_interval > 0){
property__write_all(packet, &expiry_prop, false);
}
}
/* Payload */
if(payloadlen){
packet__write_bytes(packet, payload, payloadlen);
}
return packet__queue(mosq, packet);
}

@ -0,0 +1,96 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#include <string.h>
#ifdef WITH_BROKER
# include "mosquitto_broker_internal.h"
#endif
#include "mosquitto.h"
#include "mosquitto_internal.h"
#include "logging_mosq.h"
#include "memory_mosq.h"
#include "mqtt_protocol.h"
#include "packet_mosq.h"
#include "property_mosq.h"
#include "util_mosq.h"
int send__subscribe(struct mosquitto *mosq, int *mid, int topic_count, const char **topic, int topic_qos, const mosquitto_property *properties)
{
struct mosquitto__packet *packet = NULL;
uint32_t packetlen;
uint16_t local_mid;
int rc;
int i;
int proplen, varbytes;
assert(mosq);
assert(topic);
packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet));
if(!packet) return MOSQ_ERR_NOMEM;
packetlen = 2;
if(mosq->protocol == mosq_p_mqtt5){
proplen = property__get_length_all(properties);
varbytes = packet__varint_bytes(proplen);
packetlen += proplen + varbytes;
}
for(i=0; i<topic_count; i++){
packetlen += 2+strlen(topic[i]) + 1;
}
packet->command = CMD_SUBSCRIBE | (1<<1);
packet->remaining_length = packetlen;
rc = packet__alloc(packet);
if(rc){
mosquitto__free(packet);
return rc;
}
/* Variable header */
local_mid = mosquitto__mid_generate(mosq);
if(mid) *mid = (int)local_mid;
packet__write_uint16(packet, local_mid);
if(mosq->protocol == mosq_p_mqtt5){
property__write_all(packet, properties, true);
}
/* Payload */
for(i=0; i<topic_count; i++){
packet__write_string(packet, topic[i], strlen(topic[i]));
packet__write_byte(packet, topic_qos);
}
#ifdef WITH_BROKER
# ifdef WITH_BRIDGE
log__printf(mosq, MOSQ_LOG_DEBUG, "Bridge %s sending SUBSCRIBE (Mid: %d, Topic: %s, QoS: %d, Options: 0x%02x)", mosq->id, local_mid, topic[0], topic_qos&0x03, topic_qos&0xFC);
# endif
#else
for(i=0; i<topic_count; i++){
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending SUBSCRIBE (Mid: %d, Topic: %s, QoS: %d, Options: 0x%02x)", mosq->id, local_mid, topic[i], topic_qos&0x03, topic_qos&0xFC);
}
#endif
return packet__queue(mosq, packet);
}

@ -0,0 +1,99 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <assert.h>
#include <string.h>
#ifdef WITH_BROKER
# include "mosquitto_broker_internal.h"
#endif
#include "mosquitto.h"
#include "logging_mosq.h"
#include "memory_mosq.h"
#include "mqtt_protocol.h"
#include "packet_mosq.h"
#include "property_mosq.h"
#include "send_mosq.h"
#include "util_mosq.h"
int send__unsubscribe(struct mosquitto *mosq, int *mid, int topic_count, char *const *const topic, const mosquitto_property *properties)
{
/* FIXME - only deals with a single topic */
struct mosquitto__packet *packet = NULL;
uint32_t packetlen;
uint16_t local_mid;
int rc;
int proplen, varbytes;
int i;
assert(mosq);
assert(topic);
packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet));
if(!packet) return MOSQ_ERR_NOMEM;
packetlen = 2;
for(i=0; i<topic_count; i++){
packetlen += 2+strlen(topic[i]);
}
if(mosq->protocol == mosq_p_mqtt5){
proplen = property__get_length_all(properties);
varbytes = packet__varint_bytes(proplen);
packetlen += proplen + varbytes;
}
packet->command = CMD_UNSUBSCRIBE | (1<<1);
packet->remaining_length = packetlen;
rc = packet__alloc(packet);
if(rc){
mosquitto__free(packet);
return rc;
}
/* Variable header */
local_mid = mosquitto__mid_generate(mosq);
if(mid) *mid = (int)local_mid;
packet__write_uint16(packet, local_mid);
if(mosq->protocol == mosq_p_mqtt5){
/* We don't use User Property yet. */
property__write_all(packet, properties, true);
}
/* Payload */
for(i=0; i<topic_count; i++){
packet__write_string(packet, topic[i], strlen(topic[i]));
}
#ifdef WITH_BROKER
# ifdef WITH_BRIDGE
for(i=0; i<topic_count; i++){
log__printf(mosq, MOSQ_LOG_DEBUG, "Bridge %s sending UNSUBSCRIBE (Mid: %d, Topic: %s)", mosq->id, local_mid, topic[i]);
}
# endif
#else
for(i=0; i<topic_count; i++){
log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending UNSUBSCRIBE (Mid: %d, Topic: %s)", mosq->id, local_mid, topic[i]);
}
#endif
return packet__queue(mosq, packet);
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save