support empty clientid
This commit is contained in:
parent
7bdacc6395
commit
dccbee2905
|
@ -31,7 +31,7 @@
|
||||||
|
|
||||||
-export([parse/2, serialise/1]).
|
-export([parse/2, serialise/1]).
|
||||||
|
|
||||||
-export([dump/1]).
|
-export([validate/2, dump/1]).
|
||||||
|
|
||||||
-define(MAX_LEN, 16#fffffff).
|
-define(MAX_LEN, 16#fffffff).
|
||||||
-define(HIGHBIT, 2#10000000).
|
-define(HIGHBIT, 2#10000000).
|
||||||
|
@ -259,6 +259,21 @@ opt(X) when is_integer(X) -> X.
|
||||||
protocol_name_approved(Ver, Name) ->
|
protocol_name_approved(Ver, Name) ->
|
||||||
lists:member({Ver, Name}, ?PROTOCOL_NAMES).
|
lists:member({Ver, Name}, ?PROTOCOL_NAMES).
|
||||||
|
|
||||||
|
validate(protocol, {Ver, Name}) ->
|
||||||
|
protocol_name_approved(Ver, Name);
|
||||||
|
|
||||||
|
validate(clientid, {_, ClientId}) when ( size(ClientId) >= 1 )
|
||||||
|
andalso ( size(ClientId) >= ?MAX_CLIENTID_LEN ) ->
|
||||||
|
true;
|
||||||
|
|
||||||
|
%% MQTT3.1.1 allow null clientId.
|
||||||
|
validate(clientid, {?MQTT_PROTO_V311, ClientId})
|
||||||
|
when size(ClientId) =:= 0 ->
|
||||||
|
true;
|
||||||
|
|
||||||
|
validate(clientid, {_, _}) ->
|
||||||
|
false.
|
||||||
|
|
||||||
dump(#mqtt_packet{header = Header, variable = Variable, payload = Payload}) when
|
dump(#mqtt_packet{header = Header, variable = Variable, payload = Payload}) when
|
||||||
Payload =:= undefined orelse Payload =:= <<>> ->
|
Payload =:= undefined orelse Payload =:= <<>> ->
|
||||||
dump_header(Header, dump_variable(Variable));
|
dump_header(Header, dump_variable(Variable));
|
||||||
|
|
|
@ -109,35 +109,31 @@ handle_packet(?CONNECT, Packet = #mqtt_packet {
|
||||||
variable = #mqtt_packet_connect {
|
variable = #mqtt_packet_connect {
|
||||||
username = Username,
|
username = Username,
|
||||||
password = Password,
|
password = Password,
|
||||||
proto_ver = ProtoVersion,
|
|
||||||
clean_sess = CleanSess,
|
clean_sess = CleanSess,
|
||||||
keep_alive = KeepAlive,
|
keep_alive = KeepAlive,
|
||||||
client_id = ClientId } = Var },
|
client_id = ClientId } = Var },
|
||||||
State = #proto_state{ peer_name = PeerName } ) ->
|
State = #proto_state{ peer_name = PeerName } ) ->
|
||||||
lager:info("RECV from ~s@~s: ~s", [ClientId, PeerName, emqtt_packet:dump(Packet)]),
|
lager:info("RECV from ~s@~s: ~s", [ClientId, PeerName, emqtt_packet:dump(Packet)]),
|
||||||
{ReturnCode, State1} =
|
{ReturnCode1, State1} =
|
||||||
case {lists:member(ProtoVersion, proplists:get_keys(?PROTOCOL_NAMES)),
|
case validate_connect(Var) of
|
||||||
valid_client_id(ClientId)} of
|
?CONNACK_ACCEPT ->
|
||||||
{false, _} ->
|
|
||||||
{?CONNACK_PROTO_VER, State#proto_state{client_id = ClientId}};
|
|
||||||
{_, false} ->
|
|
||||||
{?CONNACK_INVALID_ID, State#proto_state{client_id = ClientId}};
|
|
||||||
_ ->
|
|
||||||
case emqtt_auth:check(Username, Password) of
|
case emqtt_auth:check(Username, Password) of
|
||||||
false ->
|
|
||||||
lager:error("MQTT login failed - no credentials"),
|
|
||||||
{?CONNACK_CREDENTIALS, State#proto_state{client_id = ClientId}};
|
|
||||||
true ->
|
true ->
|
||||||
|
ClientId1 = clientid(ClientId, State),
|
||||||
start_keepalive(KeepAlive),
|
start_keepalive(KeepAlive),
|
||||||
emqtt_cm:register(ClientId, self()),
|
emqtt_cm:register(ClientId1, self()),
|
||||||
{?CONNACK_ACCEPT,
|
{?CONNACK_ACCEPT,
|
||||||
State #proto_state{ will_msg = make_will_msg(Var),
|
State#proto_state{ will_msg = make_will_msg(Var), client_id = ClientId1 }};
|
||||||
client_id = ClientId }}
|
false ->
|
||||||
end
|
lager:error("~s@~s: username '~s' login failed - no credentials", [ClientId, PeerName, Username]),
|
||||||
|
{?CONNACK_CREDENTIALS, State#proto_state{client_id = ClientId}}
|
||||||
|
end;
|
||||||
|
ReturnCode ->
|
||||||
|
{ReturnCode, State#proto_state{client_id = ClientId}}
|
||||||
end,
|
end,
|
||||||
send_packet( #mqtt_packet {
|
send_packet( #mqtt_packet {
|
||||||
header = #mqtt_packet_header { type = ?CONNACK },
|
header = #mqtt_packet_header { type = ?CONNACK },
|
||||||
variable = #mqtt_packet_connack{ return_code = ReturnCode }}, State1 ),
|
variable = #mqtt_packet_connack{ return_code = ReturnCode1 }}, State1 ),
|
||||||
{ok, State1};
|
{ok, State1};
|
||||||
|
|
||||||
handle_packet(?PUBLISH, Packet = #mqtt_packet {
|
handle_packet(?PUBLISH, Packet = #mqtt_packet {
|
||||||
|
@ -320,9 +316,22 @@ next_packet_id(State = #proto_state{ packet_id = 16#ffff }) ->
|
||||||
next_packet_id(State = #proto_state{ packet_id = PacketId }) ->
|
next_packet_id(State = #proto_state{ packet_id = PacketId }) ->
|
||||||
State #proto_state{ packet_id = PacketId + 1 }.
|
State #proto_state{ packet_id = PacketId + 1 }.
|
||||||
|
|
||||||
valid_client_id(ClientId) ->
|
|
||||||
ClientIdLen = size(ClientId),
|
validate_connect( #mqtt_packet_connect {
|
||||||
1 =< ClientIdLen andalso ClientIdLen =< ?MAX_CLIENTID_LEN.
|
proto_ver = Ver,
|
||||||
|
proto_name = Name,
|
||||||
|
client_id = ClientId } ) ->
|
||||||
|
case emqtt_packet:validate(protocol, {Ver, Name}) of
|
||||||
|
true ->
|
||||||
|
case emqtt_packet:validate(clientid, {Ver, ClientId}) of
|
||||||
|
true ->
|
||||||
|
?CONNACK_ACCEPT;
|
||||||
|
false ->
|
||||||
|
?CONNACK_INVALID_ID
|
||||||
|
end;
|
||||||
|
false ->
|
||||||
|
?CONNACK_PROTO_VER
|
||||||
|
end.
|
||||||
|
|
||||||
validate_packet(?PUBLISH, #mqtt_packet {
|
validate_packet(?PUBLISH, #mqtt_packet {
|
||||||
variable = #mqtt_packet_publish{
|
variable = #mqtt_packet_publish{
|
||||||
|
@ -353,6 +362,11 @@ validate_packet(?SUBSCRIBE, #mqtt_packet{variable = #mqtt_packet_subscribe{topic
|
||||||
validate_packet(_Type, _Frame) ->
|
validate_packet(_Type, _Frame) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
clientid(<<>>, #proto_state{peer_name = PeerName}) ->
|
||||||
|
<<"eMQTT/", (base64:encode(PeerName))/binary>>;
|
||||||
|
|
||||||
|
clientid(ClientId, _State) -> ClientId.
|
||||||
|
|
||||||
maybe_clean_sess(false, _Conn, _ClientId) ->
|
maybe_clean_sess(false, _Conn, _ClientId) ->
|
||||||
% todo: establish subscription to deliver old unacknowledged messages
|
% todo: establish subscription to deliver old unacknowledged messages
|
||||||
ok.
|
ok.
|
||||||
|
|
|
@ -56,6 +56,18 @@ parse_connect_test() ->
|
||||||
clean_sess = true,
|
clean_sess = true,
|
||||||
keep_alive = 60 } }, <<>>}, parse(V311ConnBin, State)),
|
keep_alive = 60 } }, <<>>}, parse(V311ConnBin, State)),
|
||||||
|
|
||||||
|
%% CONNECT(Qos=0, Retain=false, Dup=false, ClientId="", ProtoName=MQTT, ProtoVsn=4, CleanSess=true, KeepAlive=60)
|
||||||
|
V311ConnWithoutClientId = <<16,12,0,4,77,81,84,84,4,2,0,60,0,0>>,
|
||||||
|
?assertMatch({ok, #mqtt_packet{
|
||||||
|
header = #mqtt_packet_header { type = ?CONNECT,
|
||||||
|
dup = false,
|
||||||
|
qos = 0,
|
||||||
|
retain = false},
|
||||||
|
variable = #mqtt_packet_connect { proto_ver = 4,
|
||||||
|
proto_name = <<"MQTT">>,
|
||||||
|
client_id = <<>>,
|
||||||
|
clean_sess = true,
|
||||||
|
keep_alive = 60 } }, <<>>}, parse(V311ConnWithoutClientId, State)),
|
||||||
%%CONNECT(Qos=0, Retain=false, Dup=false, ClientId=mosqpub/10452-iMac.loca, ProtoName=MQIsdp, ProtoVsn=3, CleanSess=true, KeepAlive=60, Username=test, Password=******, Will(Qos=1, Retain=false, Topic=/will, Msg=willmsg))
|
%%CONNECT(Qos=0, Retain=false, Dup=false, ClientId=mosqpub/10452-iMac.loca, ProtoName=MQIsdp, ProtoVsn=3, CleanSess=true, KeepAlive=60, Username=test, Password=******, Will(Qos=1, Retain=false, Topic=/will, Msg=willmsg))
|
||||||
ConnBinWithWill = <<16,67,0,6,77,81,73,115,100,112,3,206,0,60,0,23,109,111,115,113,112,117,98,47,49,48,52,53,50,45,105,77,97,99,46,108,111,99,97,0,5,47,119,105,108,108,0,7,119,105,108,108,109,115,103,0,4,116,101,115,116,0,6,112,117,98,108,105,99>>,
|
ConnBinWithWill = <<16,67,0,6,77,81,73,115,100,112,3,206,0,60,0,23,109,111,115,113,112,117,98,47,49,48,52,53,50,45,105,77,97,99,46,108,111,99,97,0,5,47,119,105,108,108,0,7,119,105,108,108,109,115,103,0,4,116,101,115,116,0,6,112,117,98,108,105,99>>,
|
||||||
?assertMatch({ok, #mqtt_packet{
|
?assertMatch({ok, #mqtt_packet{
|
||||||
|
|
Loading…
Reference in New Issue