options.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920
  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 "mosquitto_ctrl.h"
  33. #include "get_password.h"
  34. #ifdef WITH_SOCKS
  35. static int mosquitto__parse_socks_url(struct mosq_config *cfg, char *url);
  36. #endif
  37. static int client_config_line_proc(struct mosq_config *cfg, int *argc, char **argvp[]);
  38. void init_config(struct mosq_config *cfg)
  39. {
  40. cfg->qos = 1;
  41. cfg->port = PORT_UNDEFINED;
  42. cfg->protocol_version = MQTT_PROTOCOL_V5;
  43. }
  44. void client_config_cleanup(struct mosq_config *cfg)
  45. {
  46. free(cfg->id);
  47. free(cfg->host);
  48. free(cfg->bind_address);
  49. free(cfg->username);
  50. free(cfg->password);
  51. free(cfg->options_file);
  52. #ifdef WITH_TLS
  53. free(cfg->cafile);
  54. free(cfg->capath);
  55. free(cfg->certfile);
  56. free(cfg->keyfile);
  57. free(cfg->ciphers);
  58. free(cfg->tls_alpn);
  59. free(cfg->tls_version);
  60. free(cfg->tls_engine);
  61. free(cfg->tls_engine_kpass_sha1);
  62. free(cfg->keyform);
  63. # ifdef FINAL_WITH_TLS_PSK
  64. free(cfg->psk);
  65. free(cfg->psk_identity);
  66. # endif
  67. #endif
  68. #ifdef WITH_SOCKS
  69. free(cfg->socks5_host);
  70. free(cfg->socks5_username);
  71. free(cfg->socks5_password);
  72. #endif
  73. }
  74. int ctrl_config_parse(struct mosq_config *cfg, int *argc, char **argv[])
  75. {
  76. int rc;
  77. init_config(cfg);
  78. rc = client_config_load(cfg);
  79. if(rc) return rc;
  80. /* Deal with real argc/argv */
  81. rc = client_config_line_proc(cfg, argc, argv);
  82. if(rc) return rc;
  83. #ifdef WITH_TLS
  84. if((cfg->certfile && !cfg->keyfile) || (cfg->keyfile && !cfg->certfile)){
  85. fprintf(stderr, "Error: Both certfile and keyfile must be provided if one of them is set.\n");
  86. return 1;
  87. }
  88. if((cfg->keyform && !cfg->keyfile)){
  89. fprintf(stderr, "Error: If keyform is set, keyfile must be also specified.\n");
  90. return 1;
  91. }
  92. if((cfg->tls_engine_kpass_sha1 && (!cfg->keyform || !cfg->tls_engine))){
  93. fprintf(stderr, "Error: when using tls-engine-kpass-sha1, both tls-engine and keyform must also be provided.\n");
  94. return 1;
  95. }
  96. #endif
  97. #ifdef FINAL_WITH_TLS_PSK
  98. if((cfg->cafile || cfg->capath) && cfg->psk){
  99. fprintf(stderr, "Error: Only one of --psk or --cafile/--capath may be used at once.\n");
  100. return 1;
  101. }
  102. if(cfg->psk && !cfg->psk_identity){
  103. fprintf(stderr, "Error: --psk-identity required if --psk used.\n");
  104. return 1;
  105. }
  106. #endif
  107. if(!cfg->host){
  108. cfg->host = strdup("localhost");
  109. if(!cfg->host){
  110. fprintf(stderr, "Error: Out of memory.\n");
  111. return 1;
  112. }
  113. }
  114. return MOSQ_ERR_SUCCESS;
  115. }
  116. /* Process a tokenised single line from a file or set of real argc/argv */
  117. static int client_config_line_proc(struct mosq_config *cfg, int *argc, char **argvp[])
  118. {
  119. char **argv = *argvp;
  120. while((*argc) && argv[0][0] == '-'){
  121. if(!strcmp(argv[0], "-A")){
  122. if((*argc) == 1){
  123. fprintf(stderr, "Error: -A argument given but no address specified.\n\n");
  124. return 1;
  125. }else{
  126. cfg->bind_address = strdup(argv[1]);
  127. }
  128. argv++;
  129. (*argc)--;
  130. #ifdef WITH_TLS
  131. }else if(!strcmp(argv[0], "--cafile")){
  132. if((*argc) == 1){
  133. fprintf(stderr, "Error: --cafile argument given but no file specified.\n\n");
  134. return 1;
  135. }else{
  136. cfg->cafile = strdup(argv[1]);
  137. }
  138. argv++;
  139. (*argc)--;
  140. }else if(!strcmp(argv[0], "--capath")){
  141. if((*argc) == 1){
  142. fprintf(stderr, "Error: --capath argument given but no directory specified.\n\n");
  143. return 1;
  144. }else{
  145. cfg->capath = strdup(argv[1]);
  146. }
  147. argv++;
  148. (*argc)--;
  149. }else if(!strcmp(argv[0], "--cert")){
  150. if((*argc) == 1){
  151. fprintf(stderr, "Error: --cert argument given but no file specified.\n\n");
  152. return 1;
  153. }else{
  154. cfg->certfile = strdup(argv[1]);
  155. }
  156. argv++;
  157. (*argc)--;
  158. }else if(!strcmp(argv[0], "--ciphers")){
  159. if((*argc) == 1){
  160. fprintf(stderr, "Error: --ciphers argument given but no ciphers specified.\n\n");
  161. return 1;
  162. }else{
  163. cfg->ciphers = strdup(argv[1]);
  164. }
  165. argv++;
  166. (*argc)--;
  167. #endif
  168. }else if(!strcmp(argv[0], "-d") || !strcmp(argv[0], "--debug")){
  169. cfg->debug = true;
  170. }else if(!strcmp(argv[0], "--help")){
  171. return 2;
  172. }else if(!strcmp(argv[0], "-h") || !strcmp(argv[0], "--host")){
  173. if((*argc) == 1){
  174. fprintf(stderr, "Error: -h argument given but no host specified.\n\n");
  175. return 1;
  176. }else{
  177. cfg->host = strdup(argv[1]);
  178. }
  179. argv++;
  180. (*argc)--;
  181. #ifdef WITH_TLS
  182. }else if(!strcmp(argv[0], "--insecure")){
  183. cfg->insecure = true;
  184. #endif
  185. }else if(!strcmp(argv[0], "-i") || !strcmp(argv[0], "--id")){
  186. if((*argc) == 1){
  187. fprintf(stderr, "Error: -i argument given but no id specified.\n\n");
  188. return 1;
  189. }else{
  190. cfg->id = strdup(argv[1]);
  191. }
  192. argv++;
  193. (*argc)--;
  194. #ifdef WITH_TLS
  195. }else if(!strcmp(argv[0], "--key")){
  196. if((*argc) == 1){
  197. fprintf(stderr, "Error: --key argument given but no file specified.\n\n");
  198. return 1;
  199. }else{
  200. cfg->keyfile = strdup(argv[1]);
  201. }
  202. argv++;
  203. (*argc)--;
  204. }else if(!strcmp(argv[0], "--keyform")){
  205. if((*argc) == 1){
  206. fprintf(stderr, "Error: --keyform argument given but no keyform specified.\n\n");
  207. return 1;
  208. }else{
  209. cfg->keyform = strdup(argv[1]);
  210. }
  211. argv++;
  212. (*argc)--;
  213. #endif
  214. }else if(!strcmp(argv[0], "-L") || !strcmp(argv[0], "--url")){
  215. if((*argc) == 1){
  216. fprintf(stderr, "Error: -L argument given but no URL specified.\n\n");
  217. return 1;
  218. } else {
  219. char *url = argv[1];
  220. char *topic;
  221. char *tmp;
  222. if(!strncasecmp(url, "mqtt://", 7)) {
  223. url += 7;
  224. cfg->port = 1883;
  225. } else if(!strncasecmp(url, "mqtts://", 8)) {
  226. url += 8;
  227. cfg->port = 8883;
  228. } else {
  229. fprintf(stderr, "Error: unsupported URL scheme.\n\n");
  230. return 1;
  231. }
  232. topic = strchr(url, '/');
  233. if(!topic){
  234. fprintf(stderr, "Error: Invalid URL for -L argument specified - topic missing.\n");
  235. return 1;
  236. }
  237. *topic++ = 0;
  238. tmp = strchr(url, '@');
  239. if(tmp) {
  240. *tmp++ = 0;
  241. char *colon = strchr(url, ':');
  242. if(colon) {
  243. *colon = 0;
  244. cfg->password = strdup(colon + 1);
  245. }
  246. cfg->username = strdup(url);
  247. url = tmp;
  248. }
  249. cfg->host = url;
  250. tmp = strchr(url, ':');
  251. if(tmp) {
  252. *tmp++ = 0;
  253. cfg->port = atoi(tmp);
  254. }
  255. /* Now we've removed the port, time to get the host on the heap */
  256. cfg->host = strdup(cfg->host);
  257. }
  258. argv++;
  259. (*argc)--;
  260. }else if(!strcmp(argv[0], "-o")){
  261. if((*argc) == 1){
  262. fprintf(stderr, "Error: -o argument given but no options file specified.\n\n");
  263. return 1;
  264. }else{
  265. cfg->options_file = strdup(argv[1]);
  266. }
  267. argv++;
  268. (*argc)--;
  269. }else if(!strcmp(argv[0], "-p") || !strcmp(argv[0], "--port")){
  270. if((*argc) == 1){
  271. fprintf(stderr, "Error: -p argument given but no port specified.\n\n");
  272. return 1;
  273. }else{
  274. cfg->port = atoi(argv[1]);
  275. if(cfg->port<0 || cfg->port>65535){
  276. fprintf(stderr, "Error: Invalid port given: %d\n", cfg->port);
  277. return 1;
  278. }
  279. }
  280. argv++;
  281. (*argc)--;
  282. }else if(!strcmp(argv[0], "-P") || !strcmp(argv[0], "--pw")){
  283. if((*argc) == 1){
  284. fprintf(stderr, "Error: -P argument given but no password specified.\n\n");
  285. return 1;
  286. }else{
  287. cfg->password = strdup(argv[1]);
  288. }
  289. argv++;
  290. (*argc)--;
  291. #ifdef WITH_SOCKS
  292. }else if(!strcmp(argv[0], "--proxy")){
  293. if((*argc) == 1){
  294. fprintf(stderr, "Error: --proxy argument given but no proxy url specified.\n\n");
  295. return 1;
  296. }else{
  297. if(mosquitto__parse_socks_url(cfg, argv[1])){
  298. return 1;
  299. }
  300. }
  301. argv++;
  302. (*argc)--;
  303. #endif
  304. #ifdef FINAL_WITH_TLS_PSK
  305. }else if(!strcmp(argv[0], "--psk")){
  306. if((*argc) == 1){
  307. fprintf(stderr, "Error: --psk argument given but no key specified.\n\n");
  308. return 1;
  309. }else{
  310. cfg->psk = strdup(argv[1]);
  311. }
  312. argv++;
  313. (*argc)--;
  314. }else if(!strcmp(argv[0], "--psk-identity")){
  315. if((*argc) == 1){
  316. fprintf(stderr, "Error: --psk-identity argument given but no identity specified.\n\n");
  317. return 1;
  318. }else{
  319. cfg->psk_identity = strdup(argv[1]);
  320. }
  321. argv++;
  322. (*argc)--;
  323. #endif
  324. }else if(!strcmp(argv[0], "-q") || !strcmp(argv[0], "--qos")){
  325. if((*argc) == 1){
  326. fprintf(stderr, "Error: -q argument given but no QoS specified.\n\n");
  327. return 1;
  328. }else{
  329. cfg->qos = atoi(argv[1]);
  330. if(cfg->qos<0 || cfg->qos>2){
  331. fprintf(stderr, "Error: Invalid QoS given: %d\n", cfg->qos);
  332. return 1;
  333. }
  334. }
  335. argv++;
  336. (*argc)--;
  337. }else if(!strcmp(argv[0], "--quiet")){
  338. cfg->quiet = true;
  339. #ifdef WITH_TLS
  340. }else if(!strcmp(argv[0], "--tls-alpn")){
  341. if((*argc) == 1){
  342. fprintf(stderr, "Error: --tls-alpn argument given but no protocol specified.\n\n");
  343. return 1;
  344. }else{
  345. cfg->tls_alpn = strdup(argv[1]);
  346. }
  347. argv++;
  348. (*argc)--;
  349. }else if(!strcmp(argv[0], "--tls-engine")){
  350. if((*argc) == 1){
  351. fprintf(stderr, "Error: --tls-engine argument given but no engine_id specified.\n\n");
  352. return 1;
  353. }else{
  354. cfg->tls_engine = strdup(argv[1]);
  355. }
  356. argv++;
  357. (*argc)--;
  358. }else if(!strcmp(argv[0], "--tls-engine-kpass-sha1")){
  359. if((*argc) == 1){
  360. fprintf(stderr, "Error: --tls-engine-kpass-sha1 argument given but no kpass sha1 specified.\n\n");
  361. return 1;
  362. }else{
  363. cfg->tls_engine_kpass_sha1 = strdup(argv[1]);
  364. }
  365. argv++;
  366. (*argc)--;
  367. }else if(!strcmp(argv[0], "--tls-version")){
  368. if((*argc) == 1){
  369. fprintf(stderr, "Error: --tls-version argument given but no version specified.\n\n");
  370. return 1;
  371. }else{
  372. cfg->tls_version = strdup(argv[1]);
  373. }
  374. argv++;
  375. (*argc)--;
  376. #endif
  377. }else if(!strcmp(argv[0], "-u") || !strcmp(argv[0], "--username")){
  378. if((*argc) == 1){
  379. fprintf(stderr, "Error: -u argument given but no username specified.\n\n");
  380. return 1;
  381. }else{
  382. cfg->username = strdup(argv[1]);
  383. }
  384. argv++;
  385. (*argc)--;
  386. }else if(!strcmp(argv[0], "--unix")){
  387. if((*argc) == 1){
  388. fprintf(stderr, "Error: --unix argument given but no socket path specified.\n\n");
  389. return 1;
  390. }else{
  391. cfg->host = strdup(argv[1]);
  392. cfg->port = 0;
  393. }
  394. argv++;
  395. (*argc)--;
  396. }else if(!strcmp(argv[0], "-V") || !strcmp(argv[0], "--protocol-version")){
  397. if((*argc) == 1){
  398. fprintf(stderr, "Error: --protocol-version argument given but no version specified.\n\n");
  399. return 1;
  400. }else{
  401. if(!strcmp(argv[1], "mqttv31") || !strcmp(argv[1], "31")){
  402. cfg->protocol_version = MQTT_PROTOCOL_V31;
  403. }else if(!strcmp(argv[1], "mqttv311") || !strcmp(argv[1], "311")){
  404. cfg->protocol_version = MQTT_PROTOCOL_V311;
  405. }else if(!strcmp(argv[1], "mqttv5") || !strcmp(argv[1], "5")){
  406. cfg->protocol_version = MQTT_PROTOCOL_V5;
  407. }else{
  408. fprintf(stderr, "Error: Invalid protocol version argument given.\n\n");
  409. return 1;
  410. }
  411. }
  412. argv++;
  413. (*argc)--;
  414. }else if(!strcmp(argv[0], "-v") || !strcmp(argv[0], "--verbose")){
  415. cfg->verbose = 1;
  416. }else if(!strcmp(argv[0], "--version")){
  417. return 3;
  418. }else{
  419. goto unknown_option;
  420. }
  421. argv++;
  422. (*argc)--;
  423. }
  424. *argvp = argv;
  425. return MOSQ_ERR_SUCCESS;
  426. unknown_option:
  427. fprintf(stderr, "Error: Unknown option '%s'.\n",argv[0]);
  428. return 1;
  429. }
  430. static char *get_default_cfg_location(void)
  431. {
  432. char *loc = NULL;
  433. size_t len;
  434. #ifndef WIN32
  435. char *env;
  436. #else
  437. char env[1024];
  438. int rc;
  439. #endif
  440. #ifndef WIN32
  441. env = getenv("XDG_CONFIG_HOME");
  442. if(env){
  443. len = strlen(env) + strlen("/mosquitto_ctrl") + 1;
  444. loc = malloc(len);
  445. if(!loc){
  446. fprintf(stderr, "Error: Out of memory.\n");
  447. return NULL;
  448. }
  449. snprintf(loc, len, "%s/mosquitto_ctrl", env);
  450. loc[len-1] = '\0';
  451. }else{
  452. env = getenv("HOME");
  453. if(env){
  454. len = strlen(env) + strlen("/.config/mosquitto_ctrl") + 1;
  455. loc = malloc(len);
  456. if(!loc){
  457. fprintf(stderr, "Error: Out of memory.\n");
  458. return NULL;
  459. }
  460. snprintf(loc, len, "%s/.config/mosquitto_ctrl", env);
  461. loc[len-1] = '\0';
  462. }
  463. }
  464. #else
  465. rc = GetEnvironmentVariable("USERPROFILE", env, 1024);
  466. if(rc > 0 && rc < 1024){
  467. len = strlen(env) + strlen("\\mosquitto_ctrl.conf") + 1;
  468. loc = malloc(len);
  469. if(!loc){
  470. fprintf(stderr, "Error: Out of memory.\n");
  471. return NULL;
  472. }
  473. snprintf(loc, len, "%s\\mosquitto_ctrl.conf", env);
  474. loc[len-1] = '\0';
  475. }
  476. #endif
  477. return loc;
  478. }
  479. int client_config_load(struct mosq_config *cfg)
  480. {
  481. int rc;
  482. FILE *fptr = NULL;
  483. char line[1024];
  484. int count;
  485. char **local_args, **args;
  486. char *default_cfg;
  487. if(cfg->options_file){
  488. fptr = fopen(cfg->options_file, "rt");
  489. }else{
  490. default_cfg = get_default_cfg_location();
  491. if(default_cfg){
  492. fptr = fopen(default_cfg, "rt");
  493. free(default_cfg);
  494. }
  495. }
  496. if(fptr){
  497. local_args = malloc(3*sizeof(char *));
  498. if(local_args == NULL){
  499. fprintf(stderr, "Error: Out of memory.\n");
  500. fclose(fptr);
  501. return 1;
  502. }
  503. while(fgets(line, 1024, fptr)){
  504. if(line[0] == '#') continue; /* Comments */
  505. while(line[strlen(line)-1] == 10 || line[strlen(line)-1] == 13){
  506. line[strlen(line)-1] = 0;
  507. }
  508. local_args[0] = strtok(line, " ");
  509. if(local_args[0]){
  510. local_args[1] = strtok(NULL, " ");
  511. if(local_args[1]){
  512. count = 2;
  513. }else{
  514. count = 1;
  515. }
  516. args = local_args;
  517. rc = client_config_line_proc(cfg, &count, &args);
  518. if(rc){
  519. fclose(fptr);
  520. free(local_args);
  521. return rc;
  522. }
  523. }
  524. }
  525. fclose(fptr);
  526. free(local_args);
  527. }
  528. return 0;
  529. }
  530. int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg)
  531. {
  532. int rc;
  533. char prompt[1000];
  534. char password[1000];
  535. mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, cfg->protocol_version);
  536. if(cfg->username && cfg->password == NULL){
  537. /* Ask for password */
  538. snprintf(prompt, sizeof(prompt), "Password for %s: ", cfg->username);
  539. rc = get_password(prompt, NULL, false, password, sizeof(password));
  540. if(rc){
  541. fprintf(stderr, "Error getting password.\n");
  542. mosquitto_lib_cleanup();
  543. return 1;
  544. }
  545. cfg->password = strdup(password);
  546. if(cfg->password == NULL){
  547. fprintf(stderr, "Error: Out of memory.\n");
  548. mosquitto_lib_cleanup();
  549. return 1;
  550. }
  551. }
  552. if((cfg->username || cfg->password) && mosquitto_username_pw_set(mosq, cfg->username, cfg->password)){
  553. fprintf(stderr, "Error: Problem setting username and/or password.\n");
  554. mosquitto_lib_cleanup();
  555. return 1;
  556. }
  557. #ifdef WITH_TLS
  558. if(cfg->cafile || cfg->capath){
  559. rc = mosquitto_tls_set(mosq, cfg->cafile, cfg->capath, cfg->certfile, cfg->keyfile, NULL);
  560. if(rc){
  561. if(rc == MOSQ_ERR_INVAL){
  562. fprintf(stderr, "Error: Problem setting TLS options: File not found.\n");
  563. }else{
  564. fprintf(stderr, "Error: Problem setting TLS options: %s.\n", mosquitto_strerror(rc));
  565. }
  566. mosquitto_lib_cleanup();
  567. return 1;
  568. }
  569. }
  570. if(cfg->insecure && mosquitto_tls_insecure_set(mosq, true)){
  571. fprintf(stderr, "Error: Problem setting TLS insecure option.\n");
  572. mosquitto_lib_cleanup();
  573. return 1;
  574. }
  575. if(cfg->tls_engine && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ENGINE, cfg->tls_engine)){
  576. fprintf(stderr, "Error: Problem setting TLS engine, is %s a valid engine?\n", cfg->tls_engine);
  577. mosquitto_lib_cleanup();
  578. return 1;
  579. }
  580. if(cfg->keyform && mosquitto_string_option(mosq, MOSQ_OPT_TLS_KEYFORM, cfg->keyform)){
  581. fprintf(stderr, "Error: Problem setting key form, it must be one of 'pem' or 'engine'.\n");
  582. mosquitto_lib_cleanup();
  583. return 1;
  584. }
  585. if(cfg->tls_engine_kpass_sha1 && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ENGINE_KPASS_SHA1, cfg->tls_engine_kpass_sha1)){
  586. fprintf(stderr, "Error: Problem setting TLS engine key pass sha, is it a 40 character hex string?\n");
  587. mosquitto_lib_cleanup();
  588. return 1;
  589. }
  590. if(cfg->tls_alpn && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ALPN, cfg->tls_alpn)){
  591. fprintf(stderr, "Error: Problem setting TLS ALPN protocol.\n");
  592. mosquitto_lib_cleanup();
  593. return 1;
  594. }
  595. # ifdef FINAL_WITH_TLS_PSK
  596. if(cfg->psk && mosquitto_tls_psk_set(mosq, cfg->psk, cfg->psk_identity, NULL)){
  597. fprintf(stderr, "Error: Problem setting TLS-PSK options.\n");
  598. mosquitto_lib_cleanup();
  599. return 1;
  600. }
  601. # endif
  602. if((cfg->tls_version || cfg->ciphers) && mosquitto_tls_opts_set(mosq, 1, cfg->tls_version, cfg->ciphers)){
  603. fprintf(stderr, "Error: Problem setting TLS options, check the options are valid.\n");
  604. mosquitto_lib_cleanup();
  605. return 1;
  606. }
  607. #endif
  608. #ifdef WITH_SOCKS
  609. if(cfg->socks5_host){
  610. rc = mosquitto_socks5_set(mosq, cfg->socks5_host, cfg->socks5_port, cfg->socks5_username, cfg->socks5_password);
  611. if(rc){
  612. mosquitto_lib_cleanup();
  613. return rc;
  614. }
  615. }
  616. #endif
  617. return MOSQ_ERR_SUCCESS;
  618. }
  619. int client_connect(struct mosquitto *mosq, struct mosq_config *cfg)
  620. {
  621. #ifndef WIN32
  622. char *err;
  623. #else
  624. char err[1024];
  625. #endif
  626. int rc;
  627. int port;
  628. if(cfg->port == PORT_UNDEFINED){
  629. #ifdef WITH_TLS
  630. if(cfg->cafile || cfg->capath
  631. # ifdef FINAL_WITH_TLS_PSK
  632. || cfg->psk
  633. # endif
  634. ){
  635. port = 8883;
  636. }else
  637. #endif
  638. {
  639. port = 1883;
  640. }
  641. }else{
  642. port = cfg->port;
  643. }
  644. rc = mosquitto_connect_bind_v5(mosq, cfg->host, port, 60, cfg->bind_address, NULL);
  645. if(rc>0){
  646. if(rc == MOSQ_ERR_ERRNO){
  647. #ifndef WIN32
  648. err = strerror(errno);
  649. #else
  650. FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errno, 0, (LPTSTR)&err, 1024, NULL);
  651. #endif
  652. fprintf(stderr, "Error: %s\n", err);
  653. }else{
  654. fprintf(stderr, "Unable to connect (%s).\n", mosquitto_strerror(rc));
  655. }
  656. mosquitto_lib_cleanup();
  657. return rc;
  658. }
  659. return MOSQ_ERR_SUCCESS;
  660. }
  661. #ifdef WITH_SOCKS
  662. /* Convert %25 -> %, %3a, %3A -> :, %40 -> @ */
  663. static int mosquitto__urldecode(char *str)
  664. {
  665. int i, j;
  666. size_t len;
  667. if(!str) return 0;
  668. if(!strchr(str, '%')) return 0;
  669. len = strlen(str);
  670. for(i=0; i<len; i++){
  671. if(str[i] == '%'){
  672. if(i+2 >= len){
  673. return 1;
  674. }
  675. if(str[i+1] == '2' && str[i+2] == '5'){
  676. str[i] = '%';
  677. len -= 2;
  678. for(j=i+1; j<len; j++){
  679. str[j] = str[j+2];
  680. }
  681. str[j] = '\0';
  682. }else if(str[i+1] == '3' && (str[i+2] == 'A' || str[i+2] == 'a')){
  683. str[i] = ':';
  684. len -= 2;
  685. for(j=i+1; j<len; j++){
  686. str[j] = str[j+2];
  687. }
  688. str[j] = '\0';
  689. }else if(str[i+1] == '4' && str[i+2] == '0'){
  690. str[i] = ':';
  691. len -= 2;
  692. for(j=i+1; j<len; j++){
  693. str[j] = str[j+2];
  694. }
  695. str[j] = '\0';
  696. }else{
  697. return 1;
  698. }
  699. }
  700. }
  701. return 0;
  702. }
  703. static int mosquitto__parse_socks_url(struct mosq_config *cfg, char *url)
  704. {
  705. char *str;
  706. size_t i;
  707. char *username = NULL, *password = NULL, *host = NULL, *port = NULL;
  708. char *username_or_host = NULL;
  709. size_t start;
  710. size_t len;
  711. bool have_auth = false;
  712. int port_int;
  713. if(!strncmp(url, "socks5h://", strlen("socks5h://"))){
  714. str = url + strlen("socks5h://");
  715. }else{
  716. fprintf(stderr, "Error: Unsupported proxy protocol: %s\n", url);
  717. return 1;
  718. }
  719. // socks5h://username:password@host:1883
  720. // socks5h://username:password@host
  721. // socks5h://username@host:1883
  722. // socks5h://username@host
  723. // socks5h://host:1883
  724. // socks5h://host
  725. start = 0;
  726. for(i=0; i<strlen(str); i++){
  727. if(str[i] == ':'){
  728. if(i == start){
  729. goto cleanup;
  730. }
  731. if(have_auth){
  732. /* Have already seen a @ , so this must be of form
  733. * socks5h://username[:password]@host:port */
  734. if(host){
  735. /* Already seen a host, must be malformed. */
  736. goto cleanup;
  737. }
  738. len = i-start;
  739. host = malloc(len + 1);
  740. if(!host){
  741. fprintf(stderr, "Error: Out of memory.\n");
  742. goto cleanup;
  743. }
  744. memcpy(host, &(str[start]), len);
  745. host[len] = '\0';
  746. start = i+1;
  747. }else if(!username_or_host){
  748. /* Haven't seen a @ before, so must be of form
  749. * socks5h://host:port or
  750. * socks5h://username:password@host[:port] */
  751. len = i-start;
  752. username_or_host = malloc(len + 1);
  753. if(!username_or_host){
  754. fprintf(stderr, "Error: Out of memory.\n");
  755. goto cleanup;
  756. }
  757. memcpy(username_or_host, &(str[start]), len);
  758. username_or_host[len] = '\0';
  759. start = i+1;
  760. }
  761. }else if(str[i] == '@'){
  762. if(i == start){
  763. goto cleanup;
  764. }
  765. have_auth = true;
  766. if(username_or_host){
  767. /* Must be of form socks5h://username:password@... */
  768. username = username_or_host;
  769. username_or_host = NULL;
  770. len = i-start;
  771. password = malloc(len + 1);
  772. if(!password){
  773. fprintf(stderr, "Error: Out of memory.\n");
  774. goto cleanup;
  775. }
  776. memcpy(password, &(str[start]), len);
  777. password[len] = '\0';
  778. start = i+1;
  779. }else{
  780. /* Haven't seen a : yet, so must be of form
  781. * socks5h://username@... */
  782. if(username){
  783. /* Already got a username, must be malformed. */
  784. goto cleanup;
  785. }
  786. len = i-start;
  787. username = malloc(len + 1);
  788. if(!username){
  789. fprintf(stderr, "Error: Out of memory.\n");
  790. goto cleanup;
  791. }
  792. memcpy(username, &(str[start]), len);
  793. username[len] = '\0';
  794. start = i+1;
  795. }
  796. }
  797. }
  798. /* Deal with remainder */
  799. if(i > start){
  800. len = i-start;
  801. if(host){
  802. /* Have already seen a @ , so this must be of form
  803. * socks5h://username[:password]@host:port */
  804. port = malloc(len + 1);
  805. if(!port){
  806. fprintf(stderr, "Error: Out of memory.\n");
  807. goto cleanup;
  808. }
  809. memcpy(port, &(str[start]), len);
  810. port[len] = '\0';
  811. }else if(username_or_host){
  812. /* Haven't seen a @ before, so must be of form
  813. * socks5h://host:port */
  814. host = username_or_host;
  815. username_or_host = NULL;
  816. port = malloc(len + 1);
  817. if(!port){
  818. fprintf(stderr, "Error: Out of memory.\n");
  819. goto cleanup;
  820. }
  821. memcpy(port, &(str[start]), len);
  822. port[len] = '\0';
  823. }else{
  824. host = malloc(len + 1);
  825. if(!host){
  826. fprintf(stderr, "Error: Out of memory.\n");
  827. goto cleanup;
  828. }
  829. memcpy(host, &(str[start]), len);
  830. host[len] = '\0';
  831. }
  832. }
  833. if(!host){
  834. fprintf(stderr, "Error: Invalid proxy.\n");
  835. goto cleanup;
  836. }
  837. if(mosquitto__urldecode(username)){
  838. goto cleanup;
  839. }
  840. if(mosquitto__urldecode(password)){
  841. goto cleanup;
  842. }
  843. if(port){
  844. port_int = atoi(port);
  845. if(port_int < 1 || port_int > 65535){
  846. fprintf(stderr, "Error: Invalid proxy port %d\n", port_int);
  847. goto cleanup;
  848. }
  849. free(port);
  850. }else{
  851. port_int = 1080;
  852. }
  853. cfg->socks5_username = username;
  854. cfg->socks5_password = password;
  855. cfg->socks5_host = host;
  856. cfg->socks5_port = port_int;
  857. return 0;
  858. cleanup:
  859. free(username_or_host);
  860. free(username);
  861. free(password);
  862. free(host);
  863. free(port);
  864. return 1;
  865. }
  866. #endif