int mosquitto_unsubscribe(struct mosquitto *mosq, int *mid, const char *sub) { if(!mosq) return MOSQ_ERR_INVAL; if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN; if(mosquitto_sub_topic_check(sub)) return MOSQ_ERR_INVAL; return _mosquitto_send_unsubscribe(mosq, mid, sub); }
int mqtt3_handle_unsubscribe(struct mosquitto_db *db, struct mosquitto *context) { uint16_t mid; char *sub; if(!context) return MOSQ_ERR_INVAL; _mosquitto_log_printf(NULL, MOSQ_LOG_DEBUG, "Received UNSUBSCRIBE from %s", context->id); if(context->protocol == mosq_p_mqtt311){ if((context->in_packet.command&0x0F) != 0x02){ return MOSQ_ERR_PROTOCOL; } } if(_mosquitto_read_uint16(&context->in_packet, &mid)) return 1; g_epoll_unsubscribe_num++; while(context->in_packet.pos < context->in_packet.remaining_length){ sub = NULL; if(_mosquitto_read_string(&context->in_packet, &sub)){ return 1; } if(sub){ if(!strlen(sub)){ _mosquitto_log_printf(NULL, MOSQ_LOG_INFO, "Empty unsubscription string from %s, disconnecting.", context->id); _mosquitto_free(sub); return 1; } if(mosquitto_sub_topic_check(sub)){ _mosquitto_log_printf(NULL, MOSQ_LOG_INFO, "Invalid unsubscription string from %s, disconnecting.", context->id); _mosquitto_free(sub); return 1; } _mosquitto_log_printf(NULL, MOSQ_LOG_DEBUG, "\t%s", sub); mqtt3_sub_remove(db, context, sub, &db->subs); _mosquitto_log_printf(NULL, MOSQ_LOG_UNSUBSCRIBE, "%s %s", context->id, sub); _mosquitto_free(sub); } } #ifdef WITH_PERSISTENCE db->persistence_changes++; #endif return _mosquitto_send_command_with_mid(context, UNSUBACK, mid, false); }
/* Process a tokenised single line from a file or set of real argc/argv */ int client_config_line_proc(struct mosq_config *cfg, int pub_or_sub, int argc, char *argv[]) { int i; for(i=1; i<argc; i++) { if(!strcmp(argv[i], "-p") || !strcmp(argv[i], "--port")) { if(i==argc-1) { fprintf(stderr, "Error: -p argument given but no port specified.\n\n"); return 1; } else { cfg->port = atoi(argv[i+1]); if(cfg->port<1 || cfg->port>65535) { fprintf(stderr, "Error: Invalid port given: %d\n", cfg->port); return 1; } } i++; } else if(!strcmp(argv[i], "-A")) { if(i==argc-1) { fprintf(stderr, "Error: -A argument given but no address specified.\n\n"); return 1; } else { cfg->bind_address = strdup(argv[i+1]); } i++; #ifdef WITH_TLS } else if(!strcmp(argv[i], "--cafile")) { if(i==argc-1) { fprintf(stderr, "Error: --cafile argument given but no file specified.\n\n"); return 1; } else { cfg->cafile = strdup(argv[i+1]); } i++; } else if(!strcmp(argv[i], "--capath")) { if(i==argc-1) { fprintf(stderr, "Error: --capath argument given but no directory specified.\n\n"); return 1; } else { cfg->capath = strdup(argv[i+1]); } i++; } else if(!strcmp(argv[i], "--cert")) { if(i==argc-1) { fprintf(stderr, "Error: --cert argument given but no file specified.\n\n"); return 1; } else { cfg->certfile = strdup(argv[i+1]); } i++; } else if(!strcmp(argv[i], "--ciphers")) { if(i==argc-1) { fprintf(stderr, "Error: --ciphers argument given but no ciphers specified.\n\n"); return 1; } else { cfg->ciphers = strdup(argv[i+1]); } i++; #endif } else if(!strcmp(argv[i], "-C")) { if(pub_or_sub == CLIENT_PUB) { goto unknown_option; } else { if(i==argc-1) { fprintf(stderr, "Error: -C argument given but no count specified.\n\n"); return 1; } else { cfg->msg_count = atoi(argv[i+1]); if(cfg->msg_count < 1) { fprintf(stderr, "Error: Invalid message count \"%d\".\n\n", cfg->msg_count); return 1; } } i++; } } else if(!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) { cfg->debug = true; } else if(!strcmp(argv[i], "-f") || !strcmp(argv[i], "--file")) { if(pub_or_sub == CLIENT_SUB) { goto unknown_option; } if(cfg->pub_mode != MSGMODE_NONE) { fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n"); return 1; } else if(i==argc-1) { fprintf(stderr, "Error: -f argument given but no file specified.\n\n"); return 1; } else { cfg->pub_mode = MSGMODE_FILE; cfg->file_input = strdup(argv[i+1]); } i++; } else if(!strcmp(argv[i], "--help")) { return 2; } else if(!strcmp(argv[i], "-h") || !strcmp(argv[i], "--host")) { if(i==argc-1) { fprintf(stderr, "Error: -h argument given but no host specified.\n\n"); return 1; } else { cfg->host = strdup(argv[i+1]); } i++; #ifdef WITH_TLS } else if(!strcmp(argv[i], "--insecure")) { cfg->insecure = true; #endif } else if(!strcmp(argv[i], "-i") || !strcmp(argv[i], "--id")) { if(cfg->id_prefix) { fprintf(stderr, "Error: -i and -I argument cannot be used together.\n\n"); return 1; } if(i==argc-1) { fprintf(stderr, "Error: -i argument given but no id specified.\n\n"); return 1; } else { cfg->id = strdup(argv[i+1]); } i++; } else if(!strcmp(argv[i], "-I") || !strcmp(argv[i], "--id-prefix")) { if(cfg->id) { fprintf(stderr, "Error: -i and -I argument cannot be used together.\n\n"); return 1; } if(i==argc-1) { fprintf(stderr, "Error: -I argument given but no id prefix specified.\n\n"); return 1; } else { cfg->id_prefix = strdup(argv[i+1]); } i++; } else if(!strcmp(argv[i], "-k") || !strcmp(argv[i], "--keepalive")) { if(i==argc-1) { fprintf(stderr, "Error: -k argument given but no keepalive specified.\n\n"); return 1; } else { cfg->keepalive = atoi(argv[i+1]); if(cfg->keepalive>65535) { fprintf(stderr, "Error: Invalid keepalive given: %d\n", cfg->keepalive); return 1; } } i++; #ifdef WITH_TLS } else if(!strcmp(argv[i], "--key")) { if(i==argc-1) { fprintf(stderr, "Error: --key argument given but no file specified.\n\n"); return 1; } else { cfg->keyfile = strdup(argv[i+1]); } i++; #endif } else if(!strcmp(argv[i], "-l") || !strcmp(argv[i], "--stdin-line")) { if(pub_or_sub == CLIENT_SUB) { goto unknown_option; } if(cfg->pub_mode != MSGMODE_NONE) { fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n"); return 1; } else { cfg->pub_mode = MSGMODE_STDIN_LINE; } } else if(!strcmp(argv[i], "-m") || !strcmp(argv[i], "--message")) { if(pub_or_sub == CLIENT_SUB) { goto unknown_option; } if(cfg->pub_mode != MSGMODE_NONE) { fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n"); return 1; } else if(i==argc-1) { fprintf(stderr, "Error: -m argument given but no message specified.\n\n"); return 1; } else { cfg->message = strdup(argv[i+1]); cfg->msglen = strlen(cfg->message); cfg->pub_mode = MSGMODE_CMD; } i++; } else if(!strcmp(argv[i], "-M")) { if(i==argc-1) { fprintf(stderr, "Error: -M argument given but max_inflight not specified.\n\n"); return 1; } else { cfg->max_inflight = atoi(argv[i+1]); } i++; } else if(!strcmp(argv[i], "-n") || !strcmp(argv[i], "--null-message")) { if(pub_or_sub == CLIENT_SUB) { goto unknown_option; } if(cfg->pub_mode != MSGMODE_NONE) { fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n"); return 1; } else { cfg->pub_mode = MSGMODE_NULL; } } else if(!strcmp(argv[i], "-V") || !strcmp(argv[i], "--protocol-version")) { if(i==argc-1) { fprintf(stderr, "Error: --protocol-version argument given but no version specified.\n\n"); return 1; } else { if(!strcmp(argv[i+1], "mqttv31")) { cfg->protocol_version = MQTT_PROTOCOL_V31; } else if(!strcmp(argv[i+1], "mqttv311")) { cfg->protocol_version = MQTT_PROTOCOL_V311; } else { fprintf(stderr, "Error: Invalid protocol version argument given.\n\n"); return 1; } i++; } #ifdef WITH_SOCKS } else if(!strcmp(argv[i], "--proxy")) { if(i==argc-1) { fprintf(stderr, "Error: --proxy argument given but no proxy url specified.\n\n"); return 1; } else { if(mosquitto__parse_socks_url(cfg, argv[i+1])) { return 1; } i++; } #endif #ifdef WITH_TLS_PSK } else if(!strcmp(argv[i], "--psk")) { if(i==argc-1) { fprintf(stderr, "Error: --psk argument given but no key specified.\n\n"); return 1; } else { cfg->psk = strdup(argv[i+1]); } i++; } else if(!strcmp(argv[i], "--psk-identity")) { if(i==argc-1) { fprintf(stderr, "Error: --psk-identity argument given but no identity specified.\n\n"); return 1; } else { cfg->psk_identity = strdup(argv[i+1]); } i++; #endif } else if(!strcmp(argv[i], "-q") || !strcmp(argv[i], "--qos")) { if(i==argc-1) { fprintf(stderr, "Error: -q argument given but no QoS specified.\n\n"); return 1; } else { cfg->qos = atoi(argv[i+1]); if(cfg->qos<0 || cfg->qos>2) { fprintf(stderr, "Error: Invalid QoS given: %d\n", cfg->qos); return 1; } } i++; } else if(!strcmp(argv[i], "--quiet")) { cfg->quiet = true; } else if(!strcmp(argv[i], "-r") || !strcmp(argv[i], "--retain")) { if(pub_or_sub == CLIENT_SUB) { goto unknown_option; } cfg->retain = 1; } else if(!strcmp(argv[i], "-s") || !strcmp(argv[i], "--stdin-file")) { if(pub_or_sub == CLIENT_SUB) { goto unknown_option; } if(cfg->pub_mode != MSGMODE_NONE) { fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n"); return 1; } else { cfg->pub_mode = MSGMODE_STDIN_FILE; } #ifdef WITH_SRV } else if(!strcmp(argv[i], "-S")) { cfg->use_srv = true; #endif } else if(!strcmp(argv[i], "-t") || !strcmp(argv[i], "--topic")) { if(i==argc-1) { fprintf(stderr, "Error: -t argument given but no topic specified.\n\n"); return 1; } else { if(pub_or_sub == CLIENT_PUB) { if(mosquitto_pub_topic_check(argv[i+1]) == MOSQ_ERR_INVAL) { fprintf(stderr, "Error: Invalid publish topic '%s', does it contain '+' or '#'?\n", argv[i+1]); return 1; } cfg->topic = strdup(argv[i+1]); } else { if(mosquitto_sub_topic_check(argv[i+1]) == MOSQ_ERR_INVAL) { fprintf(stderr, "Error: Invalid subscription topic '%s', are all '+' and '#' wildcards correct?\n", argv[i+1]); return 1; } cfg->topic_count++; cfg->topics = realloc(cfg->topics, cfg->topic_count*sizeof(char *)); cfg->topics[cfg->topic_count-1] = strdup(argv[i+1]); } i++; } } else if(!strcmp(argv[i], "-T") || !strcmp(argv[i], "--filter-out")) { if(pub_or_sub == CLIENT_PUB) { goto unknown_option; } if(i==argc-1) { fprintf(stderr, "Error: -T argument given but no topic filter specified.\n\n"); return 1; } else { if(mosquitto_sub_topic_check(argv[i+1]) == MOSQ_ERR_INVAL) { fprintf(stderr, "Error: Invalid filter topic '%s', are all '+' and '#' wildcards correct?\n", argv[i+1]); return 1; } cfg->filter_out_count++; cfg->filter_outs = realloc(cfg->filter_outs, cfg->filter_out_count*sizeof(char *)); cfg->filter_outs[cfg->filter_out_count-1] = strdup(argv[i+1]); } i++; #ifdef WITH_TLS } else if(!strcmp(argv[i], "--tls-version")) { if(i==argc-1) { fprintf(stderr, "Error: --tls-version argument given but no version specified.\n\n"); return 1; } else { cfg->tls_version = strdup(argv[i+1]); } i++; #endif } else if(!strcmp(argv[i], "-u") || !strcmp(argv[i], "--username")) { if(i==argc-1) { fprintf(stderr, "Error: -u argument given but no username specified.\n\n"); return 1; } else { cfg->username = strdup(argv[i+1]); } i++; } else if(!strcmp(argv[i], "-P") || !strcmp(argv[i], "--pw")) { if(i==argc-1) { fprintf(stderr, "Error: -P argument given but no password specified.\n\n"); return 1; } else { cfg->password = strdup(argv[i+1]); } i++; } else if(!strcmp(argv[i], "--will-payload")) { if(i==argc-1) { fprintf(stderr, "Error: --will-payload argument given but no will payload specified.\n\n"); return 1; } else { cfg->will_payload = strdup(argv[i+1]); cfg->will_payloadlen = strlen(cfg->will_payload); } i++; } else if(!strcmp(argv[i], "--will-qos")) { if(i==argc-1) { fprintf(stderr, "Error: --will-qos argument given but no will QoS specified.\n\n"); return 1; } else { cfg->will_qos = atoi(argv[i+1]); if(cfg->will_qos < 0 || cfg->will_qos > 2) { fprintf(stderr, "Error: Invalid will QoS %d.\n\n", cfg->will_qos); return 1; } } i++; } else if(!strcmp(argv[i], "--will-retain")) { cfg->will_retain = true; } else if(!strcmp(argv[i], "--will-topic")) { if(i==argc-1) { fprintf(stderr, "Error: --will-topic argument given but no will topic specified.\n\n"); return 1; } else { if(mosquitto_pub_topic_check(argv[i+1]) == MOSQ_ERR_INVAL) { fprintf(stderr, "Error: Invalid will topic '%s', does it contain '+' or '#'?\n", argv[i+1]); return 1; } cfg->will_topic = strdup(argv[i+1]); } i++; } else if(!strcmp(argv[i], "-c") || !strcmp(argv[i], "--disable-clean-session")) { if(pub_or_sub == CLIENT_PUB) { goto unknown_option; } cfg->clean_session = false; } else if(!strcmp(argv[i], "-N")) { if(pub_or_sub == CLIENT_PUB) { goto unknown_option; } cfg->eol = false; } else if(!strcmp(argv[i], "-R")) { if(pub_or_sub == CLIENT_PUB) { goto unknown_option; } cfg->no_retain = true; } else if(!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")) { if(pub_or_sub == CLIENT_PUB) { goto unknown_option; } cfg->verbose = 1; } else { goto unknown_option; } } return MOSQ_ERR_SUCCESS; unknown_option: fprintf(stderr, "Error: Unknown option '%s'.\n",argv[i]); return 1; }
int mqtt3_handle_subscribe(struct mosquitto_db *db, struct mosquitto *context) { int rc = 0; int rc2; uint16_t mid; char *sub; uint8_t qos; uint8_t *payload = NULL, *tmp_payload; uint32_t payloadlen = 0; int len; char *sub_mount; if(!context) return MOSQ_ERR_INVAL; _mosquitto_log_printf(NULL, MOSQ_LOG_DEBUG, "Received SUBSCRIBE from %s", context->id); /* FIXME - plenty of potential for memory leaks here */ if(context->protocol == mosq_p_mqtt311){ if((context->in_packet.command&0x0F) != 0x02){ return MOSQ_ERR_PROTOCOL; } } if(_mosquitto_read_uint16(&context->in_packet, &mid)) return 1; g_epoll_subscribe_num++; while(context->in_packet.pos < context->in_packet.remaining_length){ sub = NULL; if(_mosquitto_read_string(&context->in_packet, &sub)){ if(payload) _mosquitto_free(payload); return 1; } if(sub){ if(!strlen(sub)){ _mosquitto_log_printf(NULL, MOSQ_LOG_INFO, "Empty subscription string from %s, disconnecting.", context->address); _mosquitto_free(sub); if(payload) _mosquitto_free(payload); return 1; } if(mosquitto_sub_topic_check(sub)){ _mosquitto_log_printf(NULL, MOSQ_LOG_INFO, "Invalid subscription string from %s, disconnecting.", context->address); _mosquitto_free(sub); if(payload) _mosquitto_free(payload); return 1; } if(_mosquitto_read_byte(&context->in_packet, &qos)){ _mosquitto_free(sub); if(payload) _mosquitto_free(payload); return 1; } if(qos > 2){ _mosquitto_log_printf(NULL, MOSQ_LOG_INFO, "Invalid QoS in subscription command from %s, disconnecting.", context->address); _mosquitto_free(sub); if(payload) _mosquitto_free(payload); return 1; } if(context->listener && context->listener->mount_point){ len = strlen(context->listener->mount_point) + strlen(sub) + 1; sub_mount = _mosquitto_malloc(len+1); if(!sub_mount){ _mosquitto_free(sub); if(payload) _mosquitto_free(payload); return MOSQ_ERR_NOMEM; } snprintf(sub_mount, len, "%s%s", context->listener->mount_point, sub); sub_mount[len] = '\0'; _mosquitto_free(sub); sub = sub_mount; } _mosquitto_log_printf(NULL, MOSQ_LOG_DEBUG, "\t%s (QoS %d)", sub, qos); #if 0 /* FIXME * This section has been disabled temporarily. mosquitto_acl_check * calls mosquitto_topic_matches_sub, which can't cope with * checking subscriptions that have wildcards against ACLs that * have wildcards. Bug #1374291 is related. * * It's a very difficult problem when an ACL looks like foo/+/bar * and a subscription request to foo/# is made. * * This should be changed to using MOSQ_ACL_SUBSCRIPTION in the * future anyway. */ if(context->protocol == mosq_p_mqtt311){ rc = mosquitto_acl_check(db, context, sub, MOSQ_ACL_READ); switch(rc){ case MOSQ_ERR_SUCCESS: break; case MOSQ_ERR_ACL_DENIED: qos = 0x80; break; default: _mosquitto_free(sub); return rc; } } #endif if(qos != 0x80){ rc2 = mqtt3_sub_add(db, context, sub, qos, &db->subs); if(rc2 == MOSQ_ERR_SUCCESS){ if(mqtt3_retain_queue(db, context, sub, qos)) rc = 1; }else if(rc2 != -1){ rc = rc2; } _mosquitto_log_printf(NULL, MOSQ_LOG_SUBSCRIBE, "%s %d %s", context->id, qos, sub); } _mosquitto_free(sub); tmp_payload = _mosquitto_realloc(payload, payloadlen + 1); if(tmp_payload){ payload = tmp_payload; payload[payloadlen] = qos; payloadlen++; }else{ if(payload) _mosquitto_free(payload); return MOSQ_ERR_NOMEM; } } } if(context->protocol == mosq_p_mqtt311){ if(payloadlen == 0){ /* No subscriptions specified, protocol error. */ return MOSQ_ERR_PROTOCOL; } } if(_mosquitto_send_suback(context, mid, payloadlen, payload)) rc = 1; _mosquitto_free(payload); #ifdef WITH_PERSISTENCE db->persistence_changes++; #endif return rc; }
int handle__unsubscribe(struct mosquitto_db *db, struct mosquitto *context) { uint16_t mid; char *sub; int slen; int rc; uint8_t reason; int reason_code_count = 0; int reason_code_max; uint8_t *reason_codes = NULL, *reason_tmp; mosquitto_property *properties = NULL; if(!context) return MOSQ_ERR_INVAL; if(context->state != mosq_cs_connected){ return MOSQ_ERR_PROTOCOL; } log__printf(NULL, MOSQ_LOG_DEBUG, "Received UNSUBSCRIBE from %s", context->id); if(context->protocol != mosq_p_mqtt31){ if((context->in_packet.command&0x0F) != 0x02){ return MOSQ_ERR_PROTOCOL; } } if(packet__read_uint16(&context->in_packet, &mid)) return 1; if(mid == 0) return MOSQ_ERR_PROTOCOL; if(context->protocol == mosq_p_mqtt5){ rc = property__read_all(CMD_UNSUBSCRIBE, &context->in_packet, &properties); if(rc) return rc; /* Immediately free, we don't do anything with User Property at the moment */ mosquitto_property_free_all(&properties); } if(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt5){ if(context->in_packet.pos == context->in_packet.remaining_length){ /* No topic specified, protocol error. */ return MOSQ_ERR_PROTOCOL; } } reason_code_max = 10; reason_codes = mosquitto__malloc(reason_code_max); if(!reason_codes){ return MOSQ_ERR_NOMEM; } while(context->in_packet.pos < context->in_packet.remaining_length){ sub = NULL; if(packet__read_string(&context->in_packet, &sub, &slen)){ return 1; } if(!slen){ log__printf(NULL, MOSQ_LOG_INFO, "Empty unsubscription string from %s, disconnecting.", context->id); mosquitto__free(sub); return 1; } if(mosquitto_sub_topic_check(sub)){ log__printf(NULL, MOSQ_LOG_INFO, "Invalid unsubscription string from %s, disconnecting.", context->id); mosquitto__free(sub); return 1; } log__printf(NULL, MOSQ_LOG_DEBUG, "\t%s", sub); rc = sub__remove(db, context, sub, db->subs, &reason); log__printf(NULL, MOSQ_LOG_UNSUBSCRIBE, "%s %s", context->id, sub); mosquitto__free(sub); if(rc) return rc; reason_codes[reason_code_count] = reason; reason_code_count++; if(reason_code_count == reason_code_max){ reason_tmp = mosquitto__realloc(reason_codes, reason_code_max*2); if(!reason_tmp){ mosquitto__free(reason_codes); return MOSQ_ERR_NOMEM; } reason_codes = reason_tmp; reason_code_max *= 2; } } #ifdef WITH_PERSISTENCE db->persistence_changes++; #endif log__printf(NULL, MOSQ_LOG_DEBUG, "Sending UNSUBACK to %s", context->id); /* We don't use Reason String or User Property yet. */ rc = send__unsuback(context, mid, reason_code_count, reason_codes, NULL); mosquitto__free(reason_codes); return rc; }