client_shared.c 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633
  1. /*
  2. Copyright (c) 2014-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 <errno.h>
  16. #include <fcntl.h>
  17. #include <stdarg.h>
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #ifndef WIN32
  22. #include <unistd.h>
  23. #include <strings.h>
  24. #else
  25. #include <process.h>
  26. #include <winsock2.h>
  27. #define snprintf sprintf_s
  28. #define strncasecmp _strnicmp
  29. #endif
  30. #include <mosquitto.h>
  31. #include <mqtt_protocol.h>
  32. #include "client_shared.h"
  33. #ifdef WITH_SOCKS
  34. static int mosquitto__parse_socks_url(struct mosq_config *cfg, char *url);
  35. #endif
  36. static int client_config_line_proc(struct mosq_config *cfg, int pub_or_sub, int argc, char *argv[]);
  37. static int check_format(const char *str)
  38. {
  39. size_t i;
  40. size_t len;
  41. len = strlen(str);
  42. for(i=0; i<len; i++){
  43. if(str[i] == '%'){
  44. if(i == len-1){
  45. /* error */
  46. fprintf(stderr, "Error: Incomplete format specifier.\n");
  47. return 1;
  48. }else{
  49. if(str[i+1] == '0' || str[i+1] == '-'){
  50. /* Flag characters */
  51. i++;
  52. if(i == len-1){
  53. /* error */
  54. fprintf(stderr, "Error: Incomplete format specifier.\n");
  55. return 1;
  56. }
  57. }
  58. /* Field width */
  59. while(str[i+1] >= '0' && str[i+1] <= '9'){
  60. i++;
  61. if(i == len-1){
  62. /* error */
  63. fprintf(stderr, "Error: Incomplete format specifier.\n");
  64. return 1;
  65. }
  66. }
  67. if(str[i+1] == '.'){
  68. /* Precision specifier */
  69. i++;
  70. if(i == len-1){
  71. /* error */
  72. fprintf(stderr, "Error: Incomplete format specifier.\n");
  73. return 1;
  74. }
  75. /* Precision */
  76. while(str[i+1] >= '0' && str[i+1] <= '9'){
  77. i++;
  78. if(i == len-1){
  79. /* error */
  80. fprintf(stderr, "Error: Incomplete format specifier.\n");
  81. return 1;
  82. }
  83. }
  84. }
  85. if(str[i+1] == '%'){
  86. /* Print %, ignore */
  87. }else if(str[i+1] == 'A'){
  88. /* MQTT v5 property topic-alias */
  89. }else if(str[i+1] == 'C'){
  90. /* MQTT v5 property content-type */
  91. }else if(str[i+1] == 'D'){
  92. /* MQTT v5 property correlation-data */
  93. }else if(str[i+1] == 'E'){
  94. /* MQTT v5 property message-expiry-interval */
  95. }else if(str[i+1] == 'F'){
  96. /* MQTT v5 property payload-format-indicator */
  97. }else if(str[i+1] == 'I'){
  98. /* ISO 8601 date+time */
  99. }else if(str[i+1] == 'l'){
  100. /* payload length */
  101. }else if(str[i+1] == 'm'){
  102. /* mid */
  103. }else if(str[i+1] == 'P'){
  104. /* MQTT v5 property user-property */
  105. }else if(str[i+1] == 'p'){
  106. /* payload */
  107. }else if(str[i+1] == 'q'){
  108. /* qos */
  109. }else if(str[i+1] == 'R'){
  110. /* MQTT v5 property response-topic */
  111. }else if(str[i+1] == 'S'){
  112. /* MQTT v5 property subscription-identifier */
  113. }else if(str[i+1] == 'r'){
  114. /* retain */
  115. }else if(str[i+1] == 't'){
  116. /* topic */
  117. }else if(str[i+1] == 'j'){
  118. /* JSON output, escaped payload */
  119. }else if(str[i+1] == 'J'){
  120. /* JSON output, assuming JSON payload */
  121. }else if(str[i+1] == 'U'){
  122. /* Unix time+nanoseconds */
  123. #ifdef WIN32
  124. fprintf(stderr, "Error: The %%U format option is not supported on Windows.\n");
  125. return 1;
  126. #endif
  127. }else if(str[i+1] == 'x' || str[i+1] == 'X'){
  128. /* payload in hex */
  129. }else{
  130. fprintf(stderr, "Error: Invalid format specifier '%c'.\n", str[i+1]);
  131. return 1;
  132. }
  133. i++;
  134. }
  135. }else if(str[i] == '@'){
  136. if(i == len-1){
  137. /* error */
  138. fprintf(stderr, "Error: Incomplete format specifier.\n");
  139. return 1;
  140. }
  141. i++;
  142. }else if(str[i] == '\\'){
  143. if(i == len-1){
  144. /* error */
  145. fprintf(stderr, "Error: Incomplete escape specifier.\n");
  146. return 1;
  147. }else{
  148. switch(str[i+1]){
  149. case '\\': /* '\' */
  150. case '0': /* 0 (NULL) */
  151. case 'a': /* alert */
  152. case 'e': /* escape */
  153. case 'n': /* new line */
  154. case 'r': /* carriage return */
  155. case 't': /* horizontal tab */
  156. case 'v': /* vertical tab */
  157. break;
  158. default:
  159. fprintf(stderr, "Error: Invalid escape specifier '%c'.\n", str[i+1]);
  160. return 1;
  161. }
  162. i++;
  163. }
  164. }
  165. }
  166. return 0;
  167. }
  168. static void init_config(struct mosq_config *cfg, int pub_or_sub)
  169. {
  170. memset(cfg, 0, sizeof(*cfg));
  171. cfg->port = PORT_UNDEFINED;
  172. cfg->max_inflight = 20;
  173. cfg->keepalive = 60;
  174. cfg->clean_session = true;
  175. cfg->eol = true;
  176. cfg->repeat_count = 1;
  177. cfg->repeat_delay.tv_sec = 0;
  178. cfg->repeat_delay.tv_usec = 0;
  179. cfg->random_filter = 10000;
  180. if(pub_or_sub == CLIENT_RR){
  181. cfg->protocol_version = MQTT_PROTOCOL_V5;
  182. cfg->msg_count = 1;
  183. }else{
  184. cfg->protocol_version = MQTT_PROTOCOL_V311;
  185. }
  186. cfg->session_expiry_interval = -1; /* -1 means unset here, the user can't set it to -1. */
  187. }
  188. void client_config_cleanup(struct mosq_config *cfg)
  189. {
  190. int i;
  191. free(cfg->id);
  192. free(cfg->id_prefix);
  193. free(cfg->host);
  194. free(cfg->file_input);
  195. free(cfg->message);
  196. free(cfg->topic);
  197. free(cfg->bind_address);
  198. free(cfg->username);
  199. free(cfg->password);
  200. free(cfg->will_topic);
  201. free(cfg->will_payload);
  202. free(cfg->format);
  203. free(cfg->response_topic);
  204. #ifdef WITH_TLS
  205. free(cfg->cafile);
  206. free(cfg->capath);
  207. free(cfg->certfile);
  208. free(cfg->keyfile);
  209. free(cfg->ciphers);
  210. free(cfg->tls_alpn);
  211. free(cfg->tls_version);
  212. free(cfg->tls_engine);
  213. free(cfg->tls_engine_kpass_sha1);
  214. free(cfg->keyform);
  215. # ifdef FINAL_WITH_TLS_PSK
  216. free(cfg->psk);
  217. free(cfg->psk_identity);
  218. # endif
  219. #endif
  220. if(cfg->topics){
  221. for(i=0; i<cfg->topic_count; i++){
  222. free(cfg->topics[i]);
  223. }
  224. free(cfg->topics);
  225. }
  226. if(cfg->filter_outs){
  227. for(i=0; i<cfg->filter_out_count; i++){
  228. free(cfg->filter_outs[i]);
  229. }
  230. free(cfg->filter_outs);
  231. }
  232. if(cfg->unsub_topics){
  233. for(i=0; i<cfg->unsub_topic_count; i++){
  234. free(cfg->unsub_topics[i]);
  235. }
  236. free(cfg->unsub_topics);
  237. }
  238. #ifdef WITH_SOCKS
  239. free(cfg->socks5_host);
  240. free(cfg->socks5_username);
  241. free(cfg->socks5_password);
  242. #endif
  243. mosquitto_property_free_all(&cfg->connect_props);
  244. mosquitto_property_free_all(&cfg->publish_props);
  245. mosquitto_property_free_all(&cfg->subscribe_props);
  246. mosquitto_property_free_all(&cfg->unsubscribe_props);
  247. mosquitto_property_free_all(&cfg->disconnect_props);
  248. mosquitto_property_free_all(&cfg->will_props);
  249. }
  250. int client_config_load(struct mosq_config *cfg, int pub_or_sub, int argc, char *argv[])
  251. {
  252. int rc;
  253. FILE *fptr;
  254. char line[1024];
  255. int count;
  256. char *loc = NULL;
  257. size_t len;
  258. char *args[3];
  259. #ifndef WIN32
  260. char *env;
  261. #else
  262. char env[1024];
  263. #endif
  264. args[0] = NULL;
  265. init_config(cfg, pub_or_sub);
  266. /* Default config file */
  267. #ifndef WIN32
  268. env = getenv("XDG_CONFIG_HOME");
  269. if(env){
  270. len = strlen(env) + strlen("/mosquitto_pub") + 1;
  271. loc = malloc(len);
  272. if(!loc){
  273. err_printf(cfg, "Error: Out of memory.\n");
  274. return 1;
  275. }
  276. if(pub_or_sub == CLIENT_PUB){
  277. snprintf(loc, len, "%s/mosquitto_pub", env);
  278. }else if(pub_or_sub == CLIENT_SUB){
  279. snprintf(loc, len, "%s/mosquitto_sub", env);
  280. }else{
  281. snprintf(loc, len, "%s/mosquitto_rr", env);
  282. }
  283. loc[len-1] = '\0';
  284. }else{
  285. env = getenv("HOME");
  286. if(env){
  287. len = strlen(env) + strlen("/.config/mosquitto_pub") + 1;
  288. loc = malloc(len);
  289. if(!loc){
  290. err_printf(cfg, "Error: Out of memory.\n");
  291. return 1;
  292. }
  293. if(pub_or_sub == CLIENT_PUB){
  294. snprintf(loc, len, "%s/.config/mosquitto_pub", env);
  295. }else if(pub_or_sub == CLIENT_SUB){
  296. snprintf(loc, len, "%s/.config/mosquitto_sub", env);
  297. }else{
  298. snprintf(loc, len, "%s/.config/mosquitto_rr", env);
  299. }
  300. loc[len-1] = '\0';
  301. }
  302. }
  303. #else
  304. rc = GetEnvironmentVariable("USERPROFILE", env, 1024);
  305. if(rc > 0 && rc < 1024){
  306. len = strlen(env) + strlen("\\mosquitto_pub.conf") + 1;
  307. loc = malloc(len);
  308. if(!loc){
  309. err_printf(cfg, "Error: Out of memory.\n");
  310. return 1;
  311. }
  312. if(pub_or_sub == CLIENT_PUB){
  313. snprintf(loc, len, "%s\\mosquitto_pub.conf", env);
  314. }else if(pub_or_sub == CLIENT_SUB){
  315. snprintf(loc, len, "%s\\mosquitto_sub.conf", env);
  316. }else{
  317. snprintf(loc, len, "%s\\mosquitto_rr.conf", env);
  318. }
  319. loc[len-1] = '\0';
  320. }
  321. #endif
  322. if(loc){
  323. fptr = fopen(loc, "rt");
  324. if(fptr){
  325. while(fgets(line, 1024, fptr)){
  326. if(line[0] == '#') continue; /* Comments */
  327. while(line[strlen(line)-1] == 10 || line[strlen(line)-1] == 13){
  328. line[strlen(line)-1] = 0;
  329. }
  330. /* All offset by one "args" here, because real argc/argv has
  331. * program name as the first entry. */
  332. args[1] = strtok(line, " ");
  333. if(args[1]){
  334. args[2] = strtok(NULL, "");
  335. if(args[2]){
  336. count = 3;
  337. }else{
  338. count = 2;
  339. }
  340. rc = client_config_line_proc(cfg, pub_or_sub, count, args);
  341. if(rc){
  342. fclose(fptr);
  343. free(loc);
  344. return rc;
  345. }
  346. }
  347. }
  348. fclose(fptr);
  349. }
  350. free(loc);
  351. }
  352. /* Deal with real argc/argv */
  353. rc = client_config_line_proc(cfg, pub_or_sub, argc, argv);
  354. if(rc) return rc;
  355. if(cfg->will_payload && !cfg->will_topic){
  356. fprintf(stderr, "Error: Will payload given, but no will topic given.\n");
  357. return 1;
  358. }
  359. if(cfg->will_retain && !cfg->will_topic){
  360. fprintf(stderr, "Error: Will retain given, but no will topic given.\n");
  361. return 1;
  362. }
  363. #ifdef WITH_TLS
  364. if((cfg->certfile && !cfg->keyfile) || (cfg->keyfile && !cfg->certfile)){
  365. fprintf(stderr, "Error: Both certfile and keyfile must be provided if one of them is set.\n");
  366. return 1;
  367. }
  368. if((cfg->keyform && !cfg->keyfile)){
  369. fprintf(stderr, "Error: If keyform is set, keyfile must be also specified.\n");
  370. return 1;
  371. }
  372. if((cfg->tls_engine_kpass_sha1 && (!cfg->keyform || !cfg->tls_engine))){
  373. fprintf(stderr, "Error: when using tls-engine-kpass-sha1, both tls-engine and keyform must also be provided.\n");
  374. return 1;
  375. }
  376. #endif
  377. #ifdef FINAL_WITH_TLS_PSK
  378. if((cfg->cafile || cfg->capath) && cfg->psk){
  379. fprintf(stderr, "Error: Only one of --psk or --cafile/--capath may be used at once.\n");
  380. return 1;
  381. }
  382. if(cfg->psk && !cfg->psk_identity){
  383. fprintf(stderr, "Error: --psk-identity required if --psk used.\n");
  384. return 1;
  385. }
  386. #endif
  387. if(cfg->protocol_version == 5){
  388. if(cfg->clean_session == false && cfg->session_expiry_interval == -1){
  389. /* User hasn't set session-expiry-interval, but has cleared clean
  390. * session so default to persistent session. */
  391. cfg->session_expiry_interval = UINT32_MAX;
  392. }
  393. if(cfg->session_expiry_interval > 0){
  394. if(cfg->session_expiry_interval == UINT32_MAX && (cfg->id_prefix || !cfg->id)){
  395. fprintf(stderr, "Error: You must provide a client id if you are using an infinite session expiry interval.\n");
  396. return 1;
  397. }
  398. rc = mosquitto_property_add_int32(&cfg->connect_props, MQTT_PROP_SESSION_EXPIRY_INTERVAL, (uint32_t )cfg->session_expiry_interval);
  399. if(rc){
  400. fprintf(stderr, "Error adding property session-expiry-interval\n");
  401. }
  402. }
  403. }else{
  404. if(cfg->clean_session == false && (cfg->id_prefix || !cfg->id)){
  405. fprintf(stderr, "Error: You must provide a client id if you are using the -c option.\n");
  406. return 1;
  407. }
  408. }
  409. if(pub_or_sub == CLIENT_SUB){
  410. if(cfg->topic_count == 0){
  411. fprintf(stderr, "Error: You must specify a topic to subscribe to.\n");
  412. return 1;
  413. }
  414. }
  415. if(!cfg->host){
  416. cfg->host = strdup("localhost");
  417. if(!cfg->host){
  418. err_printf(cfg, "Error: Out of memory.\n");
  419. return 1;
  420. }
  421. }
  422. rc = mosquitto_property_check_all(CMD_CONNECT, cfg->connect_props);
  423. if(rc){
  424. err_printf(cfg, "Error in CONNECT properties: %s\n", mosquitto_strerror(rc));
  425. return 1;
  426. }
  427. rc = mosquitto_property_check_all(CMD_PUBLISH, cfg->publish_props);
  428. if(rc){
  429. err_printf(cfg, "Error in PUBLISH properties: %s\n", mosquitto_strerror(rc));
  430. return 1;
  431. }
  432. rc = mosquitto_property_check_all(CMD_SUBSCRIBE, cfg->subscribe_props);
  433. if(rc){
  434. err_printf(cfg, "Error in SUBSCRIBE properties: %s\n", mosquitto_strerror(rc));
  435. return 1;
  436. }
  437. rc = mosquitto_property_check_all(CMD_UNSUBSCRIBE, cfg->unsubscribe_props);
  438. if(rc){
  439. err_printf(cfg, "Error in UNSUBSCRIBE properties: %s\n", mosquitto_strerror(rc));
  440. return 1;
  441. }
  442. rc = mosquitto_property_check_all(CMD_DISCONNECT, cfg->disconnect_props);
  443. if(rc){
  444. err_printf(cfg, "Error in DISCONNECT properties: %s\n", mosquitto_strerror(rc));
  445. return 1;
  446. }
  447. rc = mosquitto_property_check_all(CMD_WILL, cfg->will_props);
  448. if(rc){
  449. err_printf(cfg, "Error in Will properties: %s\n", mosquitto_strerror(rc));
  450. return 1;
  451. }
  452. return MOSQ_ERR_SUCCESS;
  453. }
  454. static int cfg_add_topic(struct mosq_config *cfg, int type, char *topic, const char *arg)
  455. {
  456. if(mosquitto_validate_utf8(topic, (int )strlen(topic))){
  457. fprintf(stderr, "Error: Malformed UTF-8 in %s argument.\n\n", arg);
  458. return 1;
  459. }
  460. if(type == CLIENT_PUB || type == CLIENT_RR){
  461. if(mosquitto_pub_topic_check(topic) == MOSQ_ERR_INVAL){
  462. fprintf(stderr, "Error: Invalid publish topic '%s', does it contain '+' or '#'?\n", topic);
  463. return 1;
  464. }
  465. cfg->topic = strdup(topic);
  466. }else if(type == CLIENT_RESPONSE_TOPIC){
  467. if(mosquitto_pub_topic_check(topic) == MOSQ_ERR_INVAL){
  468. fprintf(stderr, "Error: Invalid response topic '%s', does it contain '+' or '#'?\n", topic);
  469. return 1;
  470. }
  471. cfg->response_topic = strdup(topic);
  472. }else{
  473. if(mosquitto_sub_topic_check(topic) == MOSQ_ERR_INVAL){
  474. fprintf(stderr, "Error: Invalid subscription topic '%s', are all '+' and '#' wildcards correct?\n", topic);
  475. return 1;
  476. }
  477. cfg->topic_count++;
  478. cfg->topics = realloc(cfg->topics, (size_t )cfg->topic_count*sizeof(char *));
  479. if(!cfg->topics){
  480. err_printf(cfg, "Error: Out of memory.\n");
  481. return 1;
  482. }
  483. cfg->topics[cfg->topic_count-1] = strdup(topic);
  484. }
  485. return 0;
  486. }
  487. /* Process a tokenised single line from a file or set of real argc/argv */
  488. int client_config_line_proc(struct mosq_config *cfg, int pub_or_sub, int argc, char *argv[])
  489. {
  490. int i;
  491. int tmpi;
  492. float f;
  493. size_t szt;
  494. for(i=1; i<argc; i++){
  495. if(!strcmp(argv[i], "-A")){
  496. if(i==argc-1){
  497. fprintf(stderr, "Error: -A argument given but no address specified.\n\n");
  498. return 1;
  499. }else{
  500. cfg->bind_address = strdup(argv[i+1]);
  501. }
  502. i++;
  503. #ifdef WITH_TLS
  504. }else if(!strcmp(argv[i], "--cafile")){
  505. if(i==argc-1){
  506. fprintf(stderr, "Error: --cafile argument given but no file specified.\n\n");
  507. return 1;
  508. }else{
  509. cfg->cafile = strdup(argv[i+1]);
  510. }
  511. i++;
  512. }else if(!strcmp(argv[i], "--capath")){
  513. if(i==argc-1){
  514. fprintf(stderr, "Error: --capath argument given but no directory specified.\n\n");
  515. return 1;
  516. }else{
  517. cfg->capath = strdup(argv[i+1]);
  518. }
  519. i++;
  520. }else if(!strcmp(argv[i], "--cert")){
  521. if(i==argc-1){
  522. fprintf(stderr, "Error: --cert argument given but no file specified.\n\n");
  523. return 1;
  524. }else{
  525. cfg->certfile = strdup(argv[i+1]);
  526. }
  527. i++;
  528. }else if(!strcmp(argv[i], "--ciphers")){
  529. if(i==argc-1){
  530. fprintf(stderr, "Error: --ciphers argument given but no ciphers specified.\n\n");
  531. return 1;
  532. }else{
  533. cfg->ciphers = strdup(argv[i+1]);
  534. }
  535. i++;
  536. #endif
  537. }else if(!strcmp(argv[i], "-C")){
  538. if(pub_or_sub != CLIENT_SUB){
  539. goto unknown_option;
  540. }else{
  541. if(i==argc-1){
  542. fprintf(stderr, "Error: -C argument given but no count specified.\n\n");
  543. return 1;
  544. }else{
  545. cfg->msg_count = atoi(argv[i+1]);
  546. if(cfg->msg_count < 1){
  547. fprintf(stderr, "Error: Invalid message count \"%d\".\n\n", cfg->msg_count);
  548. return 1;
  549. }
  550. }
  551. i++;
  552. }
  553. }else if(!strcmp(argv[i], "-c") || !strcmp(argv[i], "--disable-clean-session")){
  554. cfg->clean_session = false;
  555. }else if(!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")){
  556. cfg->debug = true;
  557. }else if(!strcmp(argv[i], "-D") || !strcmp(argv[i], "--property")){
  558. i++;
  559. if(cfg_parse_property(cfg, argc, argv, &i)){
  560. return 1;
  561. }
  562. cfg->protocol_version = MQTT_PROTOCOL_V5;
  563. }else if(!strcmp(argv[i], "-e")){
  564. if(pub_or_sub != CLIENT_RR){
  565. goto unknown_option;
  566. }
  567. if(i==argc-1){
  568. fprintf(stderr, "Error: -e argument given but no response topic specified.\n\n");
  569. return 1;
  570. }else{
  571. if(cfg_add_topic(cfg, CLIENT_RESPONSE_TOPIC, argv[i+1], "-e")){
  572. return 1;
  573. }
  574. }
  575. i++;
  576. }else if(!strcmp(argv[i], "-E")){
  577. if(pub_or_sub != CLIENT_SUB){
  578. goto unknown_option;
  579. }
  580. cfg->exit_after_sub = true;
  581. }else if(!strcmp(argv[i], "-f") || !strcmp(argv[i], "--file")){
  582. if(pub_or_sub == CLIENT_SUB){
  583. goto unknown_option;
  584. }
  585. if(cfg->pub_mode != MSGMODE_NONE){
  586. fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n");
  587. return 1;
  588. }else if(i==argc-1){
  589. fprintf(stderr, "Error: -f argument given but no file specified.\n\n");
  590. return 1;
  591. }else{
  592. cfg->pub_mode = MSGMODE_FILE;
  593. cfg->file_input = strdup(argv[i+1]);
  594. if(!cfg->file_input){
  595. err_printf(cfg, "Error: Out of memory.\n");
  596. return 1;
  597. }
  598. }
  599. i++;
  600. }else if(!strcmp(argv[i], "-F")){
  601. if(pub_or_sub == CLIENT_PUB){
  602. goto unknown_option;
  603. }
  604. if(i==argc-1){
  605. fprintf(stderr, "Error: -F argument given but no format specified.\n\n");
  606. return 1;
  607. }else{
  608. cfg->format = strdup(argv[i+1]);
  609. if(!cfg->format){
  610. fprintf(stderr, "Error: Out of memory.\n");
  611. return 1;
  612. }
  613. if(check_format(cfg->format)){
  614. return 1;
  615. }
  616. }
  617. i++;
  618. }else if(!strcmp(argv[i], "--help")){
  619. return 2;
  620. }else if(!strcmp(argv[i], "-h") || !strcmp(argv[i], "--host")){
  621. if(i==argc-1){
  622. fprintf(stderr, "Error: -h argument given but no host specified.\n\n");
  623. return 1;
  624. }else{
  625. cfg->host = strdup(argv[i+1]);
  626. }
  627. i++;
  628. #ifdef WITH_TLS
  629. }else if(!strcmp(argv[i], "--insecure")){
  630. cfg->insecure = true;
  631. #endif
  632. }else if(!strcmp(argv[i], "-i") || !strcmp(argv[i], "--id")){
  633. if(cfg->id_prefix){
  634. fprintf(stderr, "Error: -i and -I argument cannot be used together.\n\n");
  635. return 1;
  636. }
  637. if(i==argc-1){
  638. fprintf(stderr, "Error: -i argument given but no id specified.\n\n");
  639. return 1;
  640. }else{
  641. cfg->id = strdup(argv[i+1]);
  642. }
  643. i++;
  644. }else if(!strcmp(argv[i], "-I") || !strcmp(argv[i], "--id-prefix")){
  645. if(cfg->id){
  646. fprintf(stderr, "Error: -i and -I argument cannot be used together.\n\n");
  647. return 1;
  648. }
  649. if(i==argc-1){
  650. fprintf(stderr, "Error: -I argument given but no id prefix specified.\n\n");
  651. return 1;
  652. }else{
  653. cfg->id_prefix = strdup(argv[i+1]);
  654. }
  655. i++;
  656. }else if(!strcmp(argv[i], "-k") || !strcmp(argv[i], "--keepalive")){
  657. if(i==argc-1){
  658. fprintf(stderr, "Error: -k argument given but no keepalive specified.\n\n");
  659. return 1;
  660. }else{
  661. cfg->keepalive = atoi(argv[i+1]);
  662. if(cfg->keepalive<5 || cfg->keepalive>UINT16_MAX){
  663. fprintf(stderr, "Error: Invalid keepalive given, it must be between 5 and 65535 inclusive.\n\n");
  664. return 1;
  665. }
  666. }
  667. i++;
  668. #ifdef WITH_TLS
  669. }else if(!strcmp(argv[i], "--key")){
  670. if(i==argc-1){
  671. fprintf(stderr, "Error: --key argument given but no file specified.\n\n");
  672. return 1;
  673. }else{
  674. cfg->keyfile = strdup(argv[i+1]);
  675. }
  676. i++;
  677. }else if(!strcmp(argv[i], "--keyform")){
  678. if(i==argc-1){
  679. fprintf(stderr, "Error: --keyform argument given but no keyform specified.\n\n");
  680. return 1;
  681. }else{
  682. cfg->keyform = strdup(argv[i+1]);
  683. }
  684. i++;
  685. #endif
  686. }else if(!strcmp(argv[i], "-L") || !strcmp(argv[i], "--url")){
  687. if(i==argc-1){
  688. fprintf(stderr, "Error: -L argument given but no URL specified.\n\n");
  689. return 1;
  690. } else {
  691. char *url = argv[i+1];
  692. char *topic;
  693. char *tmp;
  694. if(!strncasecmp(url, "mqtt://", 7)) {
  695. url += 7;
  696. cfg->port = 1883;
  697. } else if(!strncasecmp(url, "mqtts://", 8)) {
  698. #ifdef WITH_TLS
  699. url += 8;
  700. cfg->port = 8883;
  701. cfg->tls_use_os_certs = true;
  702. #else
  703. fprintf(stderr, "Error: TLS support not available.\n\n");
  704. return 1;
  705. #endif
  706. } else {
  707. fprintf(stderr, "Error: unsupported URL scheme.\n\n");
  708. return 1;
  709. }
  710. topic = strchr(url, '/');
  711. if(!topic){
  712. fprintf(stderr, "Error: Invalid URL for -L argument specified - topic missing.\n");
  713. return 1;
  714. }
  715. *topic++ = 0;
  716. if(cfg_add_topic(cfg, pub_or_sub, topic, "-L topic"))
  717. return 1;
  718. tmp = strchr(url, '@');
  719. if(tmp) {
  720. char *colon;
  721. *tmp++ = 0;
  722. colon = strchr(url, ':');
  723. if(colon) {
  724. *colon = 0;
  725. cfg->password = strdup(colon + 1);
  726. }
  727. cfg->username = strdup(url);
  728. url = tmp;
  729. }
  730. cfg->host = url;
  731. tmp = strchr(url, ':');
  732. if(tmp) {
  733. *tmp++ = 0;
  734. cfg->port = atoi(tmp);
  735. }
  736. /* Now we've removed the port, time to get the host on the heap */
  737. cfg->host = strdup(cfg->host);
  738. }
  739. i++;
  740. }else if(!strcmp(argv[i], "-l") || !strcmp(argv[i], "--stdin-line")){
  741. if(pub_or_sub != CLIENT_PUB){
  742. goto unknown_option;
  743. }
  744. if(cfg->pub_mode != MSGMODE_NONE){
  745. fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n");
  746. return 1;
  747. }else{
  748. cfg->pub_mode = MSGMODE_STDIN_LINE;
  749. }
  750. }else if(!strcmp(argv[i], "-m") || !strcmp(argv[i], "--message")){
  751. if(pub_or_sub == CLIENT_SUB){
  752. goto unknown_option;
  753. }
  754. if(cfg->pub_mode != MSGMODE_NONE){
  755. fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n");
  756. return 1;
  757. }else if(i==argc-1){
  758. fprintf(stderr, "Error: -m argument given but no message specified.\n\n");
  759. return 1;
  760. }else{
  761. cfg->message = strdup(argv[i+1]);
  762. if(cfg->message == NULL){
  763. fprintf(stderr, "Error: Out of memory.\n\n");
  764. return 1;
  765. }
  766. szt = strlen(cfg->message);
  767. if(szt > MQTT_MAX_PAYLOAD){
  768. fprintf(stderr, "Error: Message length must be less than %u bytes.\n\n", MQTT_MAX_PAYLOAD);
  769. return 1;
  770. }
  771. cfg->msglen = (int )szt;
  772. cfg->pub_mode = MSGMODE_CMD;
  773. }
  774. i++;
  775. }else if(!strcmp(argv[i], "-M")){
  776. if(i==argc-1){
  777. fprintf(stderr, "Error: -M argument given but max_inflight not specified.\n\n");
  778. return 1;
  779. }else{
  780. tmpi = atoi(argv[i+1]);
  781. if(tmpi < 1){
  782. fprintf(stderr, "Error: Maximum inflight messages must be greater than 0.\n\n");
  783. return 1;
  784. }
  785. cfg->max_inflight = (unsigned int )tmpi;
  786. }
  787. i++;
  788. }else if(!strcmp(argv[i], "--nodelay")){
  789. cfg->tcp_nodelay = true;
  790. }else if(!strcmp(argv[i], "-n") || !strcmp(argv[i], "--null-message")){
  791. if(pub_or_sub == CLIENT_SUB){
  792. goto unknown_option;
  793. }
  794. if(cfg->pub_mode != MSGMODE_NONE){
  795. fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n");
  796. return 1;
  797. }else{
  798. cfg->pub_mode = MSGMODE_NULL;
  799. }
  800. }else if(!strcmp(argv[i], "-N")){
  801. if(pub_or_sub == CLIENT_PUB){
  802. goto unknown_option;
  803. }
  804. cfg->eol = false;
  805. }else if(!strcmp(argv[i], "-p") || !strcmp(argv[i], "--port")){
  806. if(i==argc-1){
  807. fprintf(stderr, "Error: -p argument given but no port specified.\n\n");
  808. return 1;
  809. }else{
  810. cfg->port = atoi(argv[i+1]);
  811. if(cfg->port<0 || cfg->port>65535){
  812. fprintf(stderr, "Error: Invalid port given: %d\n", cfg->port);
  813. return 1;
  814. }
  815. }
  816. i++;
  817. }else if(!strcmp(argv[i], "--pretty")){
  818. if(pub_or_sub == CLIENT_PUB){
  819. goto unknown_option;
  820. }
  821. cfg->pretty = true;
  822. }else if(!strcmp(argv[i], "-P") || !strcmp(argv[i], "--pw")){
  823. if(i==argc-1){
  824. fprintf(stderr, "Error: -P argument given but no password specified.\n\n");
  825. return 1;
  826. }else{
  827. cfg->password = strdup(argv[i+1]);
  828. }
  829. i++;
  830. #ifdef WITH_SOCKS
  831. }else if(!strcmp(argv[i], "--proxy")){
  832. if(i==argc-1){
  833. fprintf(stderr, "Error: --proxy argument given but no proxy url specified.\n\n");
  834. return 1;
  835. }else{
  836. if(mosquitto__parse_socks_url(cfg, argv[i+1])){
  837. return 1;
  838. }
  839. i++;
  840. }
  841. #endif
  842. #ifdef FINAL_WITH_TLS_PSK
  843. }else if(!strcmp(argv[i], "--psk")){
  844. if(i==argc-1){
  845. fprintf(stderr, "Error: --psk argument given but no key specified.\n\n");
  846. return 1;
  847. }else{
  848. cfg->psk = strdup(argv[i+1]);
  849. }
  850. i++;
  851. }else if(!strcmp(argv[i], "--psk-identity")){
  852. if(i==argc-1){
  853. fprintf(stderr, "Error: --psk-identity argument given but no identity specified.\n\n");
  854. return 1;
  855. }else{
  856. cfg->psk_identity = strdup(argv[i+1]);
  857. }
  858. i++;
  859. #endif
  860. }else if(!strcmp(argv[i], "-q") || !strcmp(argv[i], "--qos")){
  861. if(i==argc-1){
  862. fprintf(stderr, "Error: -q argument given but no QoS specified.\n\n");
  863. return 1;
  864. }else{
  865. cfg->qos = atoi(argv[i+1]);
  866. if(cfg->qos<0 || cfg->qos>2){
  867. fprintf(stderr, "Error: Invalid QoS given: %d\n", cfg->qos);
  868. return 1;
  869. }
  870. }
  871. i++;
  872. }else if(!strcmp(argv[i], "--quiet")){
  873. cfg->quiet = true;
  874. }else if(!strcmp(argv[i], "-r") || !strcmp(argv[i], "--retain")){
  875. if(pub_or_sub != CLIENT_PUB){
  876. goto unknown_option;
  877. }
  878. cfg->retain = 1;
  879. }else if(!strcmp(argv[i], "-R")){
  880. if(pub_or_sub == CLIENT_PUB){
  881. goto unknown_option;
  882. }
  883. cfg->no_retain = true;
  884. cfg->sub_opts |= MQTT_SUB_OPT_SEND_RETAIN_NEVER;
  885. }else if(!strcmp(argv[i], "--random-filter")){
  886. if(pub_or_sub != CLIENT_SUB){
  887. goto unknown_option;
  888. }
  889. if(i==argc-1){
  890. fprintf(stderr, "Error: --random-filter argument given but no chance specified.\n\n");
  891. return 1;
  892. }else{
  893. cfg->random_filter = (int)(10.0*atof(argv[i+1]));
  894. if(cfg->random_filter > 10000 || cfg->random_filter < 1){
  895. fprintf(stderr, "Error: --random-filter chance must be between 0.1-100.0\n\n");
  896. return 1;
  897. }
  898. }
  899. i++;
  900. }else if(!strcmp(argv[i], "--remove-retained")){
  901. if(pub_or_sub != CLIENT_SUB){
  902. goto unknown_option;
  903. }
  904. cfg->remove_retained = true;
  905. }else if(!strcmp(argv[i], "--repeat")){
  906. if(pub_or_sub != CLIENT_PUB){
  907. goto unknown_option;
  908. }
  909. if(i==argc-1){
  910. fprintf(stderr, "Error: --repeat argument given but no count specified.\n\n");
  911. return 1;
  912. }else{
  913. cfg->repeat_count = atoi(argv[i+1]);
  914. if(cfg->repeat_count < 1){
  915. fprintf(stderr, "Error: --repeat argument must be >0.\n\n");
  916. return 1;
  917. }
  918. }
  919. i++;
  920. }else if(!strcmp(argv[i], "--repeat-delay")){
  921. if(pub_or_sub != CLIENT_PUB){
  922. goto unknown_option;
  923. }
  924. if(i==argc-1){
  925. fprintf(stderr, "Error: --repeat-delay argument given but no time specified.\n\n");
  926. return 1;
  927. }else{
  928. f = (float )atof(argv[i+1]);
  929. if(f < 0.0f){
  930. fprintf(stderr, "Error: --repeat-delay argument must be >=0.0.\n\n");
  931. return 1;
  932. }
  933. f *= 1.0e6f;
  934. cfg->repeat_delay.tv_sec = (int)f/1000000;
  935. cfg->repeat_delay.tv_usec = (int)f%1000000;
  936. }
  937. i++;
  938. }else if(!strcmp(argv[i], "--retain-as-published")){
  939. if(pub_or_sub == CLIENT_PUB){
  940. goto unknown_option;
  941. }
  942. cfg->sub_opts |= MQTT_SUB_OPT_RETAIN_AS_PUBLISHED;
  943. }else if(!strcmp(argv[i], "--retained-only")){
  944. if(pub_or_sub != CLIENT_SUB){
  945. goto unknown_option;
  946. }
  947. cfg->retained_only = true;
  948. }else if(!strcmp(argv[i], "-s") || !strcmp(argv[i], "--stdin-file")){
  949. if(pub_or_sub == CLIENT_SUB){
  950. goto unknown_option;
  951. }
  952. if(cfg->pub_mode != MSGMODE_NONE){
  953. fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n");
  954. return 1;
  955. }else{
  956. cfg->pub_mode = MSGMODE_STDIN_FILE;
  957. }
  958. #ifdef WITH_SRV
  959. }else if(!strcmp(argv[i], "-S")){
  960. cfg->use_srv = true;
  961. #endif
  962. }else if(!strcmp(argv[i], "-t") || !strcmp(argv[i], "--topic")){
  963. if(i==argc-1){
  964. fprintf(stderr, "Error: -t argument given but no topic specified.\n\n");
  965. return 1;
  966. }else{
  967. if(cfg_add_topic(cfg, pub_or_sub, argv[i + 1], "-t"))
  968. return 1;
  969. i++;
  970. }
  971. }else if(!strcmp(argv[i], "-T") || !strcmp(argv[i], "--filter-out")){
  972. if(pub_or_sub != CLIENT_SUB){
  973. goto unknown_option;
  974. }
  975. if(i==argc-1){
  976. fprintf(stderr, "Error: -T argument given but no topic filter specified.\n\n");
  977. return 1;
  978. }else{
  979. if(mosquitto_validate_utf8(argv[i+1], (int )strlen(argv[i+1]))){
  980. fprintf(stderr, "Error: Malformed UTF-8 in -T argument.\n\n");
  981. return 1;
  982. }
  983. if(mosquitto_sub_topic_check(argv[i+1]) == MOSQ_ERR_INVAL){
  984. fprintf(stderr, "Error: Invalid filter topic '%s', are all '+' and '#' wildcards correct?\n", argv[i+1]);
  985. return 1;
  986. }
  987. cfg->filter_out_count++;
  988. cfg->filter_outs = realloc(cfg->filter_outs, (size_t )cfg->filter_out_count*sizeof(char *));
  989. if(!cfg->filter_outs){
  990. fprintf(stderr, "Error: Out of memory.\n");
  991. return 1;
  992. }
  993. cfg->filter_outs[cfg->filter_out_count-1] = strdup(argv[i+1]);
  994. }
  995. i++;
  996. #ifdef WITH_TLS
  997. }else if(!strcmp(argv[i], "--tls-alpn")){
  998. if(i==argc-1){
  999. fprintf(stderr, "Error: --tls-alpn argument given but no protocol specified.\n\n");
  1000. return 1;
  1001. }else{
  1002. cfg->tls_alpn = strdup(argv[i+1]);
  1003. }
  1004. i++;
  1005. }else if(!strcmp(argv[i], "--tls-engine")){
  1006. if(i==argc-1){
  1007. fprintf(stderr, "Error: --tls-engine argument given but no engine_id specified.\n\n");
  1008. return 1;
  1009. }else{
  1010. cfg->tls_engine = strdup(argv[i+1]);
  1011. }
  1012. i++;
  1013. }else if(!strcmp(argv[i], "--tls-engine-kpass-sha1")){
  1014. if(i==argc-1){
  1015. fprintf(stderr, "Error: --tls-engine-kpass-sha1 argument given but no kpass sha1 specified.\n\n");
  1016. return 1;
  1017. }else{
  1018. cfg->tls_engine_kpass_sha1 = strdup(argv[i+1]);
  1019. }
  1020. i++;
  1021. }else if(!strcmp(argv[i], "--tls-use-os-certs")){
  1022. cfg->tls_use_os_certs = true;
  1023. }else if(!strcmp(argv[i], "--tls-version")){
  1024. if(i==argc-1){
  1025. fprintf(stderr, "Error: --tls-version argument given but no version specified.\n\n");
  1026. return 1;
  1027. }else{
  1028. cfg->tls_version = strdup(argv[i+1]);
  1029. }
  1030. i++;
  1031. #endif
  1032. }else if(!strcmp(argv[i], "-U") || !strcmp(argv[i], "--unsubscribe")){
  1033. if(pub_or_sub != CLIENT_SUB){
  1034. goto unknown_option;
  1035. }
  1036. if(i==argc-1){
  1037. fprintf(stderr, "Error: -U argument given but no unsubscribe topic specified.\n\n");
  1038. return 1;
  1039. }else{
  1040. if(mosquitto_validate_utf8(argv[i+1], (int )strlen(argv[i+1]))){
  1041. fprintf(stderr, "Error: Malformed UTF-8 in -U argument.\n\n");
  1042. return 1;
  1043. }
  1044. if(mosquitto_sub_topic_check(argv[i+1]) == MOSQ_ERR_INVAL){
  1045. fprintf(stderr, "Error: Invalid unsubscribe topic '%s', are all '+' and '#' wildcards correct?\n", argv[i+1]);
  1046. return 1;
  1047. }
  1048. cfg->unsub_topic_count++;
  1049. cfg->unsub_topics = realloc(cfg->unsub_topics, (size_t )cfg->unsub_topic_count*sizeof(char *));
  1050. if(!cfg->unsub_topics){
  1051. fprintf(stderr, "Error: Out of memory.\n");
  1052. return 1;
  1053. }
  1054. cfg->unsub_topics[cfg->unsub_topic_count-1] = strdup(argv[i+1]);
  1055. }
  1056. i++;
  1057. }else if(!strcmp(argv[i], "-u") || !strcmp(argv[i], "--username")){
  1058. if(i==argc-1){
  1059. fprintf(stderr, "Error: -u argument given but no username specified.\n\n");
  1060. return 1;
  1061. }else{
  1062. cfg->username = strdup(argv[i+1]);
  1063. }
  1064. i++;
  1065. }else if(!strcmp(argv[i], "--unix")){
  1066. if(i==argc-1){
  1067. fprintf(stderr, "Error: --unix argument given but no socket path specified.\n\n");
  1068. return 1;
  1069. }else{
  1070. cfg->host = strdup(argv[i+1]);
  1071. cfg->port = 0;
  1072. }
  1073. i++;
  1074. }else if(!strcmp(argv[i], "-V") || !strcmp(argv[i], "--protocol-version")){
  1075. if(i==argc-1){
  1076. fprintf(stderr, "Error: --protocol-version argument given but no version specified.\n\n");
  1077. return 1;
  1078. }else{
  1079. if(!strcmp(argv[i+1], "mqttv31") || !strcmp(argv[i+1], "31")){
  1080. cfg->protocol_version = MQTT_PROTOCOL_V31;
  1081. }else if(!strcmp(argv[i+1], "mqttv311") || !strcmp(argv[i+1], "311")){
  1082. cfg->protocol_version = MQTT_PROTOCOL_V311;
  1083. }else if(!strcmp(argv[i+1], "mqttv5") || !strcmp(argv[i+1], "5")){
  1084. cfg->protocol_version = MQTT_PROTOCOL_V5;
  1085. }else{
  1086. fprintf(stderr, "Error: Invalid protocol version argument given.\n\n");
  1087. return 1;
  1088. }
  1089. i++;
  1090. }
  1091. }else if(!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")){
  1092. if(pub_or_sub == CLIENT_PUB){
  1093. goto unknown_option;
  1094. }
  1095. cfg->verbose = 1;
  1096. }else if(!strcmp(argv[i], "--version")){
  1097. return 3;
  1098. }else if(!strcmp(argv[i], "-W")){
  1099. if(pub_or_sub == CLIENT_PUB){
  1100. goto unknown_option;
  1101. }else{
  1102. if(i==argc-1){
  1103. fprintf(stderr, "Error: -W argument given but no timeout specified.\n\n");
  1104. return 1;
  1105. }else{
  1106. tmpi = atoi(argv[i+1]);
  1107. if(tmpi < 1){
  1108. fprintf(stderr, "Error: Invalid timeout \"%d\".\n\n", tmpi);
  1109. return 1;
  1110. }
  1111. cfg->timeout = (unsigned int )tmpi;
  1112. }
  1113. i++;
  1114. }
  1115. }else if(!strcmp(argv[i], "--will-payload")){
  1116. if(i==argc-1){
  1117. fprintf(stderr, "Error: --will-payload argument given but no will payload specified.\n\n");
  1118. return 1;
  1119. }else{
  1120. cfg->will_payload = strdup(argv[i+1]);
  1121. cfg->will_payloadlen = (int )strlen(cfg->will_payload);
  1122. }
  1123. i++;
  1124. }else if(!strcmp(argv[i], "--will-qos")){
  1125. if(i==argc-1){
  1126. fprintf(stderr, "Error: --will-qos argument given but no will QoS specified.\n\n");
  1127. return 1;
  1128. }else{
  1129. cfg->will_qos = atoi(argv[i+1]);
  1130. if(cfg->will_qos < 0 || cfg->will_qos > 2){
  1131. fprintf(stderr, "Error: Invalid will QoS %d.\n\n", cfg->will_qos);
  1132. return 1;
  1133. }
  1134. }
  1135. i++;
  1136. }else if(!strcmp(argv[i], "--will-retain")){
  1137. cfg->will_retain = true;
  1138. }else if(!strcmp(argv[i], "--will-topic")){
  1139. if(i==argc-1){
  1140. fprintf(stderr, "Error: --will-topic argument given but no will topic specified.\n\n");
  1141. return 1;
  1142. }else{
  1143. if(mosquitto_validate_utf8(argv[i+1], (int )strlen(argv[i+1]))){
  1144. fprintf(stderr, "Error: Malformed UTF-8 in --will-topic argument.\n\n");
  1145. return 1;
  1146. }
  1147. if(mosquitto_pub_topic_check(argv[i+1]) == MOSQ_ERR_INVAL){
  1148. fprintf(stderr, "Error: Invalid will topic '%s', does it contain '+' or '#'?\n", argv[i+1]);
  1149. return 1;
  1150. }
  1151. cfg->will_topic = strdup(argv[i+1]);
  1152. }
  1153. i++;
  1154. }else if(!strcmp(argv[i], "-x")){
  1155. if(i==argc-1){
  1156. fprintf(stderr, "Error: -x argument given but no session expiry interval specified.\n\n");
  1157. return 1;
  1158. }else{
  1159. if(!strcmp(argv[i+1], "∞")){
  1160. cfg->session_expiry_interval = UINT32_MAX;
  1161. }else{
  1162. char *endptr = NULL;
  1163. cfg->session_expiry_interval = strtol(argv[i+1], &endptr, 0);
  1164. if(endptr == argv[i+1] || endptr[0] != '\0'){
  1165. /* Entirety of argument wasn't a number */
  1166. fprintf(stderr, "Error: session-expiry-interval not a number.\n\n");
  1167. return 1;
  1168. }
  1169. if(cfg->session_expiry_interval > UINT32_MAX || cfg->session_expiry_interval < -1){
  1170. fprintf(stderr, "Error: session-expiry-interval out of range.\n\n");
  1171. return 1;
  1172. }
  1173. if(cfg->session_expiry_interval == -1){
  1174. /* Convenience value for infinity. */
  1175. cfg->session_expiry_interval = UINT32_MAX;
  1176. }
  1177. }
  1178. }
  1179. i++;
  1180. }else{
  1181. goto unknown_option;
  1182. }
  1183. }
  1184. return MOSQ_ERR_SUCCESS;
  1185. unknown_option:
  1186. fprintf(stderr, "Error: Unknown option '%s'.\n",argv[i]);
  1187. return 1;
  1188. }
  1189. int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg)
  1190. {
  1191. #if defined(WITH_TLS) || defined(WITH_SOCKS)
  1192. int rc;
  1193. #endif
  1194. mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, cfg->protocol_version);
  1195. if(cfg->will_topic && mosquitto_will_set_v5(mosq, cfg->will_topic,
  1196. cfg->will_payloadlen, cfg->will_payload, cfg->will_qos,
  1197. cfg->will_retain, cfg->will_props)){
  1198. err_printf(cfg, "Error: Problem setting will.\n");
  1199. mosquitto_lib_cleanup();
  1200. return 1;
  1201. }
  1202. cfg->will_props = NULL;
  1203. if((cfg->username || cfg->password) && mosquitto_username_pw_set(mosq, cfg->username, cfg->password)){
  1204. err_printf(cfg, "Error: Problem setting username and/or password.\n");
  1205. mosquitto_lib_cleanup();
  1206. return 1;
  1207. }
  1208. #ifdef WITH_TLS
  1209. if(cfg->cafile || cfg->capath){
  1210. rc = mosquitto_tls_set(mosq, cfg->cafile, cfg->capath, cfg->certfile, cfg->keyfile, NULL);
  1211. if(rc){
  1212. if(rc == MOSQ_ERR_INVAL){
  1213. err_printf(cfg, "Error: Problem setting TLS options: File not found.\n");
  1214. }else{
  1215. err_printf(cfg, "Error: Problem setting TLS options: %s.\n", mosquitto_strerror(rc));
  1216. }
  1217. mosquitto_lib_cleanup();
  1218. return 1;
  1219. }
  1220. # ifdef FINAL_WITH_TLS_PSK
  1221. }else if(cfg->psk){
  1222. if(mosquitto_tls_psk_set(mosq, cfg->psk, cfg->psk_identity, NULL)){
  1223. err_printf(cfg, "Error: Problem setting TLS-PSK options.\n");
  1224. mosquitto_lib_cleanup();
  1225. return 1;
  1226. }
  1227. # endif
  1228. }else if(cfg->port == 8883){
  1229. mosquitto_int_option(mosq, MOSQ_OPT_TLS_USE_OS_CERTS, 1);
  1230. }
  1231. if(cfg->tls_use_os_certs){
  1232. mosquitto_int_option(mosq, MOSQ_OPT_TLS_USE_OS_CERTS, 1);
  1233. }
  1234. if(cfg->insecure && mosquitto_tls_insecure_set(mosq, true)){
  1235. err_printf(cfg, "Error: Problem setting TLS insecure option.\n");
  1236. mosquitto_lib_cleanup();
  1237. return 1;
  1238. }
  1239. if(cfg->tls_engine && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ENGINE, cfg->tls_engine)){
  1240. err_printf(cfg, "Error: Problem setting TLS engine, is %s a valid engine?\n", cfg->tls_engine);
  1241. mosquitto_lib_cleanup();
  1242. return 1;
  1243. }
  1244. if(cfg->keyform && mosquitto_string_option(mosq, MOSQ_OPT_TLS_KEYFORM, cfg->keyform)){
  1245. err_printf(cfg, "Error: Problem setting key form, it must be one of 'pem' or 'engine'.\n");
  1246. mosquitto_lib_cleanup();
  1247. return 1;
  1248. }
  1249. if(cfg->tls_engine_kpass_sha1 && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ENGINE_KPASS_SHA1, cfg->tls_engine_kpass_sha1)){
  1250. err_printf(cfg, "Error: Problem setting TLS engine key pass sha, is it a 40 character hex string?\n");
  1251. mosquitto_lib_cleanup();
  1252. return 1;
  1253. }
  1254. if(cfg->tls_alpn && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ALPN, cfg->tls_alpn)){
  1255. err_printf(cfg, "Error: Problem setting TLS ALPN protocol.\n");
  1256. mosquitto_lib_cleanup();
  1257. return 1;
  1258. }
  1259. if((cfg->tls_version || cfg->ciphers) && mosquitto_tls_opts_set(mosq, 1, cfg->tls_version, cfg->ciphers)){
  1260. err_printf(cfg, "Error: Problem setting TLS options, check the options are valid.\n");
  1261. mosquitto_lib_cleanup();
  1262. return 1;
  1263. }
  1264. #endif
  1265. mosquitto_max_inflight_messages_set(mosq, cfg->max_inflight);
  1266. #ifdef WITH_SOCKS
  1267. if(cfg->socks5_host){
  1268. rc = mosquitto_socks5_set(mosq, cfg->socks5_host, cfg->socks5_port, cfg->socks5_username, cfg->socks5_password);
  1269. if(rc){
  1270. mosquitto_lib_cleanup();
  1271. return rc;
  1272. }
  1273. }
  1274. #endif
  1275. if(cfg->tcp_nodelay){
  1276. mosquitto_int_option(mosq, MOSQ_OPT_TCP_NODELAY, 1);
  1277. }
  1278. if(cfg->msg_count > 0 && cfg->msg_count < 20){
  1279. /* 20 is the default "receive maximum"
  1280. * If we don't set this, then we can receive > msg_count messages
  1281. * before we quit.*/
  1282. mosquitto_int_option(mosq, MOSQ_OPT_RECEIVE_MAXIMUM, cfg->msg_count);
  1283. }
  1284. return MOSQ_ERR_SUCCESS;
  1285. }
  1286. int client_id_generate(struct mosq_config *cfg)
  1287. {
  1288. if(cfg->id_prefix){
  1289. cfg->id = malloc(strlen(cfg->id_prefix)+10);
  1290. if(!cfg->id){
  1291. err_printf(cfg, "Error: Out of memory.\n");
  1292. mosquitto_lib_cleanup();
  1293. return 1;
  1294. }
  1295. snprintf(cfg->id, strlen(cfg->id_prefix)+10, "%s%d", cfg->id_prefix, getpid());
  1296. }
  1297. return MOSQ_ERR_SUCCESS;
  1298. }
  1299. int client_connect(struct mosquitto *mosq, struct mosq_config *cfg)
  1300. {
  1301. #ifndef WIN32
  1302. char *err;
  1303. #else
  1304. char err[1024];
  1305. #endif
  1306. int rc;
  1307. int port;
  1308. if(cfg->port == PORT_UNDEFINED){
  1309. #ifdef WITH_TLS
  1310. if(cfg->cafile || cfg->capath
  1311. # ifdef FINAL_WITH_TLS_PSK
  1312. || cfg->psk
  1313. # endif
  1314. ){
  1315. port = 8883;
  1316. }else
  1317. #endif
  1318. {
  1319. port = 1883;
  1320. }
  1321. }else{
  1322. port = cfg->port;
  1323. }
  1324. #ifdef WITH_SRV
  1325. if(cfg->use_srv){
  1326. rc = mosquitto_connect_srv(mosq, cfg->host, cfg->keepalive, cfg->bind_address);
  1327. }else{
  1328. rc = mosquitto_connect_bind_v5(mosq, cfg->host, port, cfg->keepalive, cfg->bind_address, cfg->connect_props);
  1329. }
  1330. #else
  1331. rc = mosquitto_connect_bind_v5(mosq, cfg->host, port, cfg->keepalive, cfg->bind_address, cfg->connect_props);
  1332. #endif
  1333. if(rc>0){
  1334. if(rc == MOSQ_ERR_ERRNO){
  1335. #ifndef WIN32
  1336. err = strerror(errno);
  1337. #else
  1338. FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errno, 0, (LPTSTR)&err, 1024, NULL);
  1339. #endif
  1340. err_printf(cfg, "Error: %s\n", err);
  1341. }else{
  1342. err_printf(cfg, "Unable to connect (%s).\n", mosquitto_strerror(rc));
  1343. }
  1344. mosquitto_lib_cleanup();
  1345. return rc;
  1346. }
  1347. return MOSQ_ERR_SUCCESS;
  1348. }
  1349. #ifdef WITH_SOCKS
  1350. /* Convert %25 -> %, %3a, %3A -> :, %40 -> @ */
  1351. static int mosquitto__urldecode(char *str)
  1352. {
  1353. size_t i, j;
  1354. size_t len;
  1355. if(!str) return 0;
  1356. if(!strchr(str, '%')) return 0;
  1357. len = strlen(str);
  1358. for(i=0; i<len; i++){
  1359. if(str[i] == '%'){
  1360. if(i+2 >= len){
  1361. return 1;
  1362. }
  1363. if(str[i+1] == '2' && str[i+2] == '5'){
  1364. str[i] = '%';
  1365. len -= 2;
  1366. for(j=i+1; j<len; j++){
  1367. str[j] = str[j+2];
  1368. }
  1369. str[j] = '\0';
  1370. }else if(str[i+1] == '3' && (str[i+2] == 'A' || str[i+2] == 'a')){
  1371. str[i] = ':';
  1372. len -= 2;
  1373. for(j=i+1; j<len; j++){
  1374. str[j] = str[j+2];
  1375. }
  1376. str[j] = '\0';
  1377. }else if(str[i+1] == '4' && str[i+2] == '0'){
  1378. str[i] = ':';
  1379. len -= 2;
  1380. for(j=i+1; j<len; j++){
  1381. str[j] = str[j+2];
  1382. }
  1383. str[j] = '\0';
  1384. }else{
  1385. return 1;
  1386. }
  1387. }
  1388. }
  1389. return 0;
  1390. }
  1391. static int mosquitto__parse_socks_url(struct mosq_config *cfg, char *url)
  1392. {
  1393. char *str;
  1394. size_t i;
  1395. char *username = NULL, *password = NULL, *host = NULL, *port = NULL;
  1396. char *username_or_host = NULL;
  1397. size_t start;
  1398. size_t len;
  1399. bool have_auth = false;
  1400. int port_int;
  1401. if(!strncmp(url, "socks5h://", strlen("socks5h://"))){
  1402. str = url + strlen("socks5h://");
  1403. }else{
  1404. err_printf(cfg, "Error: Unsupported proxy protocol: %s\n", url);
  1405. return 1;
  1406. }
  1407. /* socks5h://username:password@host:1883
  1408. * socks5h://username:password@host
  1409. * socks5h://username@host:1883
  1410. * socks5h://username@host
  1411. * socks5h://host:1883
  1412. * socks5h://host
  1413. */
  1414. start = 0;
  1415. for(i=0; i<strlen(str); i++){
  1416. if(str[i] == ':'){
  1417. if(i == start){
  1418. goto cleanup;
  1419. }
  1420. if(have_auth){
  1421. /* Have already seen a @ , so this must be of form
  1422. * socks5h://username[:password]@host:port */
  1423. if(host){
  1424. /* Already seen a host, must be malformed. */
  1425. goto cleanup;
  1426. }
  1427. len = i-start;
  1428. host = malloc(len + 1);
  1429. if(!host){
  1430. err_printf(cfg, "Error: Out of memory.\n");
  1431. goto cleanup;
  1432. }
  1433. memcpy(host, &(str[start]), len);
  1434. host[len] = '\0';
  1435. start = i+1;
  1436. }else if(!username_or_host){
  1437. /* Haven't seen a @ before, so must be of form
  1438. * socks5h://host:port or
  1439. * socks5h://username:password@host[:port] */
  1440. len = i-start;
  1441. username_or_host = malloc(len + 1);
  1442. if(!username_or_host){
  1443. err_printf(cfg, "Error: Out of memory.\n");
  1444. goto cleanup;
  1445. }
  1446. memcpy(username_or_host, &(str[start]), len);
  1447. username_or_host[len] = '\0';
  1448. start = i+1;
  1449. }
  1450. }else if(str[i] == '@'){
  1451. if(i == start){
  1452. goto cleanup;
  1453. }
  1454. have_auth = true;
  1455. if(username_or_host){
  1456. /* Must be of form socks5h://username:password@... */
  1457. username = username_or_host;
  1458. username_or_host = NULL;
  1459. len = i-start;
  1460. password = malloc(len + 1);
  1461. if(!password){
  1462. err_printf(cfg, "Error: Out of memory.\n");
  1463. goto cleanup;
  1464. }
  1465. memcpy(password, &(str[start]), len);
  1466. password[len] = '\0';
  1467. start = i+1;
  1468. }else{
  1469. /* Haven't seen a : yet, so must be of form
  1470. * socks5h://username@... */
  1471. if(username){
  1472. /* Already got a username, must be malformed. */
  1473. goto cleanup;
  1474. }
  1475. len = i-start;
  1476. username = malloc(len + 1);
  1477. if(!username){
  1478. err_printf(cfg, "Error: Out of memory.\n");
  1479. goto cleanup;
  1480. }
  1481. memcpy(username, &(str[start]), len);
  1482. username[len] = '\0';
  1483. start = i+1;
  1484. }
  1485. }
  1486. }
  1487. /* Deal with remainder */
  1488. if(i > start){
  1489. len = i-start;
  1490. if(host){
  1491. /* Have already seen a @ , so this must be of form
  1492. * socks5h://username[:password]@host:port */
  1493. port = malloc(len + 1);
  1494. if(!port){
  1495. err_printf(cfg, "Error: Out of memory.\n");
  1496. goto cleanup;
  1497. }
  1498. memcpy(port, &(str[start]), len);
  1499. port[len] = '\0';
  1500. }else if(username_or_host){
  1501. /* Haven't seen a @ before, so must be of form
  1502. * socks5h://host:port */
  1503. host = username_or_host;
  1504. username_or_host = NULL;
  1505. port = malloc(len + 1);
  1506. if(!port){
  1507. err_printf(cfg, "Error: Out of memory.\n");
  1508. goto cleanup;
  1509. }
  1510. memcpy(port, &(str[start]), len);
  1511. port[len] = '\0';
  1512. }else{
  1513. host = malloc(len + 1);
  1514. if(!host){
  1515. err_printf(cfg, "Error: Out of memory.\n");
  1516. goto cleanup;
  1517. }
  1518. memcpy(host, &(str[start]), len);
  1519. host[len] = '\0';
  1520. }
  1521. }
  1522. if(!host){
  1523. err_printf(cfg, "Error: Invalid proxy.\n");
  1524. goto cleanup;
  1525. }
  1526. if(mosquitto__urldecode(username)){
  1527. goto cleanup;
  1528. }
  1529. if(mosquitto__urldecode(password)){
  1530. goto cleanup;
  1531. }
  1532. if(port){
  1533. port_int = atoi(port);
  1534. if(port_int < 1 || port_int > 65535){
  1535. err_printf(cfg, "Error: Invalid proxy port %d\n", port_int);
  1536. goto cleanup;
  1537. }
  1538. free(port);
  1539. }else{
  1540. port_int = 1080;
  1541. }
  1542. cfg->socks5_username = username;
  1543. cfg->socks5_password = password;
  1544. cfg->socks5_host = host;
  1545. cfg->socks5_port = port_int;
  1546. return 0;
  1547. cleanup:
  1548. free(username_or_host);
  1549. free(username);
  1550. free(password);
  1551. free(host);
  1552. free(port);
  1553. return 1;
  1554. }
  1555. #endif
  1556. void err_printf(const struct mosq_config *cfg, const char *fmt, ...)
  1557. {
  1558. va_list va;
  1559. if(cfg->quiet) return;
  1560. va_start(va, fmt);
  1561. vfprintf(stderr, fmt, va);
  1562. va_end(va);
  1563. }