util_topic.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. /*
  2. Copyright (c) 2009-2020 Roger Light <roger@atchoo.org>
  3. All rights reserved. This program and the accompanying materials
  4. are made available under the terms of the Eclipse Public License 2.0
  5. and Eclipse Distribution License v1.0 which accompany this distribution.
  6. The Eclipse Public License is available at
  7. https://www.eclipse.org/legal/epl-2.0/
  8. and the Eclipse Distribution License is available at
  9. http://www.eclipse.org/org/documents/edl-v10.php.
  10. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
  11. Contributors:
  12. Roger Light - initial implementation and documentation.
  13. */
  14. #include "config.h"
  15. #include <assert.h>
  16. #include <string.h>
  17. #ifdef WIN32
  18. # include <winsock2.h>
  19. # include <aclapi.h>
  20. # include <io.h>
  21. # include <lmcons.h>
  22. #else
  23. # include <sys/stat.h>
  24. #endif
  25. #ifdef WITH_BROKER
  26. #include "mosquitto_broker_internal.h"
  27. #endif
  28. #include "mosquitto.h"
  29. #include "memory_mosq.h"
  30. #include "net_mosq.h"
  31. #include "send_mosq.h"
  32. #include "time_mosq.h"
  33. #include "tls_mosq.h"
  34. #include "util_mosq.h"
  35. /* Check that a topic used for publishing is valid.
  36. * Search for + or # in a topic. Return MOSQ_ERR_INVAL if found.
  37. * Also returns MOSQ_ERR_INVAL if the topic string is too long.
  38. * Returns MOSQ_ERR_SUCCESS if everything is fine.
  39. */
  40. int mosquitto_pub_topic_check(const char *str)
  41. {
  42. int len = 0;
  43. #ifdef WITH_BROKER
  44. int hier_count = 0;
  45. #endif
  46. if(str == NULL){
  47. return MOSQ_ERR_INVAL;
  48. }
  49. while(str && str[0]){
  50. if(str[0] == '+' || str[0] == '#'){
  51. return MOSQ_ERR_INVAL;
  52. }
  53. #ifdef WITH_BROKER
  54. else if(str[0] == '/'){
  55. hier_count++;
  56. }
  57. #endif
  58. len++;
  59. str = &str[1];
  60. }
  61. if(len > 65535) return MOSQ_ERR_INVAL;
  62. #ifdef WITH_BROKER
  63. if(hier_count > TOPIC_HIERARCHY_LIMIT) return MOSQ_ERR_INVAL;
  64. #endif
  65. return MOSQ_ERR_SUCCESS;
  66. }
  67. int mosquitto_pub_topic_check2(const char *str, size_t len)
  68. {
  69. size_t i;
  70. #ifdef WITH_BROKER
  71. int hier_count = 0;
  72. #endif
  73. if(str == NULL || len > 65535){
  74. return MOSQ_ERR_INVAL;
  75. }
  76. for(i=0; i<len; i++){
  77. if(str[i] == '+' || str[i] == '#'){
  78. return MOSQ_ERR_INVAL;
  79. }
  80. #ifdef WITH_BROKER
  81. else if(str[i] == '/'){
  82. hier_count++;
  83. }
  84. #endif
  85. }
  86. #ifdef WITH_BROKER
  87. if(hier_count > TOPIC_HIERARCHY_LIMIT) return MOSQ_ERR_INVAL;
  88. #endif
  89. return MOSQ_ERR_SUCCESS;
  90. }
  91. /* Check that a topic used for subscriptions is valid.
  92. * Search for + or # in a topic, check they aren't in invalid positions such as
  93. * foo/#/bar, foo/+bar or foo/bar#.
  94. * Return MOSQ_ERR_INVAL if invalid position found.
  95. * Also returns MOSQ_ERR_INVAL if the topic string is too long.
  96. * Returns MOSQ_ERR_SUCCESS if everything is fine.
  97. */
  98. int mosquitto_sub_topic_check(const char *str)
  99. {
  100. char c = '\0';
  101. int len = 0;
  102. #ifdef WITH_BROKER
  103. int hier_count = 0;
  104. #endif
  105. if(str == NULL){
  106. return MOSQ_ERR_INVAL;
  107. }
  108. while(str[0]){
  109. if(str[0] == '+'){
  110. if((c != '\0' && c != '/') || (str[1] != '\0' && str[1] != '/')){
  111. return MOSQ_ERR_INVAL;
  112. }
  113. }else if(str[0] == '#'){
  114. if((c != '\0' && c != '/') || str[1] != '\0'){
  115. return MOSQ_ERR_INVAL;
  116. }
  117. }
  118. #ifdef WITH_BROKER
  119. else if(str[0] == '/'){
  120. hier_count++;
  121. }
  122. #endif
  123. len++;
  124. c = str[0];
  125. str = &str[1];
  126. }
  127. if(len > 65535) return MOSQ_ERR_INVAL;
  128. #ifdef WITH_BROKER
  129. if(hier_count > TOPIC_HIERARCHY_LIMIT) return MOSQ_ERR_INVAL;
  130. #endif
  131. return MOSQ_ERR_SUCCESS;
  132. }
  133. int mosquitto_sub_topic_check2(const char *str, size_t len)
  134. {
  135. char c = '\0';
  136. size_t i;
  137. #ifdef WITH_BROKER
  138. int hier_count = 0;
  139. #endif
  140. if(str == NULL || len > 65535){
  141. return MOSQ_ERR_INVAL;
  142. }
  143. for(i=0; i<len; i++){
  144. if(str[i] == '+'){
  145. if((c != '\0' && c != '/') || (i<len-1 && str[i+1] != '/')){
  146. return MOSQ_ERR_INVAL;
  147. }
  148. }else if(str[i] == '#'){
  149. if((c != '\0' && c != '/') || i<len-1){
  150. return MOSQ_ERR_INVAL;
  151. }
  152. }
  153. #ifdef WITH_BROKER
  154. else if(str[i] == '/'){
  155. hier_count++;
  156. }
  157. #endif
  158. c = str[i];
  159. }
  160. #ifdef WITH_BROKER
  161. if(hier_count > TOPIC_HIERARCHY_LIMIT) return MOSQ_ERR_INVAL;
  162. #endif
  163. return MOSQ_ERR_SUCCESS;
  164. }
  165. int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result)
  166. {
  167. return mosquitto_topic_matches_sub2(sub, 0, topic, 0, result);
  168. }
  169. /* Does a topic match a subscription? */
  170. int mosquitto_topic_matches_sub2(const char *sub, size_t sublen, const char *topic, size_t topiclen, bool *result)
  171. {
  172. size_t spos;
  173. UNUSED(sublen);
  174. UNUSED(topiclen);
  175. if(!result) return MOSQ_ERR_INVAL;
  176. *result = false;
  177. if(!sub || !topic || sub[0] == 0 || topic[0] == 0){
  178. return MOSQ_ERR_INVAL;
  179. }
  180. if((sub[0] == '$' && topic[0] != '$')
  181. || (topic[0] == '$' && sub[0] != '$')){
  182. return MOSQ_ERR_SUCCESS;
  183. }
  184. spos = 0;
  185. while(sub[0] != 0){
  186. if(topic[0] == '+' || topic[0] == '#'){
  187. return MOSQ_ERR_INVAL;
  188. }
  189. if(sub[0] != topic[0] || topic[0] == 0){ /* Check for wildcard matches */
  190. if(sub[0] == '+'){
  191. /* Check for bad "+foo" or "a/+foo" subscription */
  192. if(spos > 0 && sub[-1] != '/'){
  193. return MOSQ_ERR_INVAL;
  194. }
  195. /* Check for bad "foo+" or "foo+/a" subscription */
  196. if(sub[1] != 0 && sub[1] != '/'){
  197. return MOSQ_ERR_INVAL;
  198. }
  199. spos++;
  200. sub++;
  201. while(topic[0] != 0 && topic[0] != '/'){
  202. if(topic[0] == '+' || topic[0] == '#'){
  203. return MOSQ_ERR_INVAL;
  204. }
  205. topic++;
  206. }
  207. if(topic[0] == 0 && sub[0] == 0){
  208. *result = true;
  209. return MOSQ_ERR_SUCCESS;
  210. }
  211. }else if(sub[0] == '#'){
  212. /* Check for bad "foo#" subscription */
  213. if(spos > 0 && sub[-1] != '/'){
  214. return MOSQ_ERR_INVAL;
  215. }
  216. /* Check for # not the final character of the sub, e.g. "#foo" */
  217. if(sub[1] != 0){
  218. return MOSQ_ERR_INVAL;
  219. }else{
  220. while(topic[0] != 0){
  221. if(topic[0] == '+' || topic[0] == '#'){
  222. return MOSQ_ERR_INVAL;
  223. }
  224. topic++;
  225. }
  226. *result = true;
  227. return MOSQ_ERR_SUCCESS;
  228. }
  229. }else{
  230. /* Check for e.g. foo/bar matching foo/+/# */
  231. if(topic[0] == 0
  232. && spos > 0
  233. && sub[-1] == '+'
  234. && sub[0] == '/'
  235. && sub[1] == '#')
  236. {
  237. *result = true;
  238. return MOSQ_ERR_SUCCESS;
  239. }
  240. /* There is no match at this point, but is the sub invalid? */
  241. while(sub[0] != 0){
  242. if(sub[0] == '#' && sub[1] != 0){
  243. return MOSQ_ERR_INVAL;
  244. }
  245. spos++;
  246. sub++;
  247. }
  248. /* Valid input, but no match */
  249. return MOSQ_ERR_SUCCESS;
  250. }
  251. }else{
  252. /* sub[spos] == topic[tpos] */
  253. if(topic[1] == 0){
  254. /* Check for e.g. foo matching foo/# */
  255. if(sub[1] == '/'
  256. && sub[2] == '#'
  257. && sub[3] == 0){
  258. *result = true;
  259. return MOSQ_ERR_SUCCESS;
  260. }
  261. }
  262. spos++;
  263. sub++;
  264. topic++;
  265. if(sub[0] == 0 && topic[0] == 0){
  266. *result = true;
  267. return MOSQ_ERR_SUCCESS;
  268. }else if(topic[0] == 0 && sub[0] == '+' && sub[1] == 0){
  269. if(spos > 0 && sub[-1] != '/'){
  270. return MOSQ_ERR_INVAL;
  271. }
  272. spos++;
  273. sub++;
  274. *result = true;
  275. return MOSQ_ERR_SUCCESS;
  276. }
  277. }
  278. }
  279. if((topic[0] != 0 || sub[0] != 0)){
  280. *result = false;
  281. }
  282. while(topic[0] != 0){
  283. if(topic[0] == '+' || topic[0] == '#'){
  284. return MOSQ_ERR_INVAL;
  285. }
  286. topic++;
  287. }
  288. return MOSQ_ERR_SUCCESS;
  289. }