[MQTT / Mosca] Mqtt Packet 에 Command Type 추가하기 (for authentication) #1
[MQTT / Mosca] Mqtt Packet 에 Command Type 추가하기 (for authentication) #2
[MQTT / Mosca] Mqtt Packet 에 Command Type 추가하기 (for authentication) #3
흐흐흐 드디어 네번째 글이다
정말 정리하기 넘 힘들군..
그래도 이번엔 드디어 3단계로 넘어간다!!
경축경축!!!
#MQTT Packet Command 추가하기
Authentication 을 위한 Packet 흐름
1. Connect Packet (Device -> Server)
2. Authenticate Packet (Server -> Device)
3. Authenticate Packet (Device -> Server)
4. Connect ACK Packet (Server -> Device)
크으으 이제 무려 3단계다
2단계에서 워낙 삽질을 많이 해서
3단계는 좀 수월하게 할 수 있다.
일단 2단계에서의 접근과 동일하게,
이번엔 Device 에서 Packet 을 generate 하는 부분을 봐야 되니까!
Device 쪽 mqtt 라이브러리를 봐야한다.
#5. mqtt (send)
내가 원하는 건
publish 할 때 처럼!!
그냥 client.authenticate(~) 해서 authenticate 요청을 날려버리는 거다!
그러니 publish function 이 어떻게 정의되어 있는지부터 한번 보자.
(mqtt/lib/client.js)
/**
* publish - publish <message> to <topic>
*
* @param {String} topic - topic to publish to
* @param {String, Buffer} message - message to publish
* @param {Object} [opts] - publish options, includes:
* {Number} qos - qos level to publish on
* {Boolean} retain - whether or not to retain the message
* @param {Function} [callback] - function(err){}
* called when publish succeeds or fails
* @returns {MqttClient} this - for chaining
* @api public
*
* @example client.publish('topic', 'message');
* @example
* client.publish('topic', 'message', {qos: 1, retain: true});
* @example client.publish('topic', 'message', console.log);
*/
MqttClient.prototype.publish = function (topic, message, opts, callback) {
var packet;
// .publish(topic, payload, cb);
if ('function' === typeof opts) {
callback = opts;
opts = null;
}
// Default opts
if (!opts) {
opts = {qos: 0, retain: false};
}
if (this._checkDisconnecting(callback)) {
return this;
}
packet = {
cmd: 'publish',
topic: topic,
payload: message,
qos: opts.qos,
retain: opts.retain,
messageId: this._nextId()
};
switch (opts.qos) {
case 1:
case 2:
// Add to callbacks
this.outgoing[packet.messageId] = callback || nop;
this._sendPacket(packet);
break;
default:
this._sendPacket(packet, callback);
break;
}
return this;
};
안녕? publish function 아!
나는 너를 복붙해다가, 겁나 고쳐댈거야!
말 그대로다.
그냥 얘 복붙해서 맘에 들도록 짜깁기하면 된다.
2편에서 generate 할 때 그랬던 것처럼!
짜깁기 하여 완성된 코드▼
MqttClient.prototype.authenticate = function (clientId, username, secretKey, callback) {
var packet;
if (this._checkDisconnecting(callback)) {
return this;
}
packet = {
cmd: 'authenticate',
keepalive: 1,
qos: 0,
retain: false,
clientId: clientId,
username: username,
password: secretKey
};
this._sendPacket(packet, callback);
return this;
};
비교적 매우 심플하다.
왜냐면 별 처리를 안 했거든..
근데 여기서는 packet에 들어갈 정보들만 명시해준 다음,
_sendPacket 이란 function 으로 넘겨준다.
그럼 저 function 이 뭘 하는 앤지 정도는 살펴봐야겠지?
저기서 막 또 cmd case 별로 나눠서 뭔 짓 해주면 어떡해..
보러 가자!
/**
* _sendPacket - send or queue a packet
* @param {String} type - packet type (see `protocol`)
* @param {Object} packet - packet options
* @param {Function} cb - callback when the packet is sent
* @api private
*/
MqttClient.prototype._sendPacket = function (packet, cb) {
if (!this.connected) {
if (((packet.qos || 0) === 0 && this.queueQoSZero) || packet.cmd !== 'publish') {
this.queue.push({ packet: packet, cb: cb })
} else if (packet.qos > 0) {
this.outgoingStore.put(packet, function (err) {
if (err) {
return cb && cb(err)
}
})
} else if (cb) {
cb(new Error('No connection to broker'))
}
return
}
// When sending a packet, reschedule the ping timer
this._shiftPingInterval()
if (packet.cmd !== 'publish') {
sendPacket(this, packet, cb)
return
}
switch (packet.qos) {
case 2:
case 1:
storeAndSend(this, packet, cb)
break
/**
* no need of case here since it will be caught by default
* and jshint comply that before default it must be a break
* anyway it will result in -1 evaluation
*/
case 0:
/* falls through */
default:
sendPacket(this, packet, cb)
break
}
}
거봐 확인 안 했으면 어쩔뻔 했어
나는 authenticate packet 을 client 가 connect 되기 전에 요청한다.
근데 저 function 에 따르면, connected 상태에 따라 처리해주는 부분이 있어버려서,
제대로 sendPacket 까지 도달하지 못하는 것 같다. (실제로 이 function 을 처리 안 해주고 그냥 돌리면 에러가 난다. 테스트 해봄)
이 코드를 수정하는 데에는 여러가지 방법이 있겠지만,
코드 하나하나 뜯어보고 무슨 의미인지 알아내기엔 이미 삽질을 너무 많이 해서
그냥 가장 간단한 방법으로 고쳤다.
/**
* _sendPacket - send or queue a packet
* @param {String} type - packet type (see `protocol`)
* @param {Object} packet - packet options
* @param {Function} cb - callback when the packet is sent
* @api private
*/
MqttClient.prototype._sendPacket = function (packet, cb) {
if (!this.connected && packet.cmd !== 'authenticate') {
if (0 < packet.qos || 'publish' !== packet.cmd || this.queueQoSZero) {
this.queue.push({ packet: packet, cb: cb });
} else if (cb) {
cb(new Error('No connection to broker'));
}
return;
}
// When sending a packet, reschedule the ping timer
this._shiftPingInterval();
switch (packet.qos) {
case 2:
case 1:
storeAndSend(this, packet, cb);
break;
/**
* no need of case here since it will be caught by default
* and jshint comply that before default it must be a break
* anyway it will result in -1 evaluation
*/
case 0:
/* falls through */
default:
sendPacket(this, packet, cb);
break;
}
};
다른 그림찾기 넘 고난이도일까봐 bold 처리 했다.
저렇게 그냥 단순히 cmd가 authenticate일 경우에는 connected 가 false 여도 다음 로직으로 넘어가도록 해버렸다.
이제 여기까지 하면!!
거의 다 됐는데!!!
이전편처럼 project directory 가 구분되어 있는 사람들은
Deivce project 쪽 mqtt-packet library 에서도 authenticate type packet generate 하는 부분을 다시 추가해줘야 한다.
근데 나만 그런 건지는 모르겠는데,
npm install mosca 로 설치된 Server project 쪽 mqtt-packet library 와
npm install mqtt 로 설치된 Device project 쪽 mqtt-packet library 가
코드 구성이 조금 상이하다.
주의할 것.. 아마 버전 차이인 것 같다.
npm install mqtt 로 설치된 mqtt-packet library 에서는
generate 하는 코드를 mqtt-packet/writeToStream.js 파일에서 찾을 수 있다.
방식이 좀 다르긴 한데
stream 이란 두번째 parameter 가 추가되었다.
그래도 이전에 했던 것처럼 connect 복붙해서 적당히 수정하면 된다.
진짜 똑같이 수정하면 됨!! 괜히 이상한 부분만 안 건드리면..
대신 진짜진짜 주의할 부분이 있다!!!!
나도 원래는 같은 project directory 에서 Server, Device process 둘 다 돌리면서 테스트 하고 분리했었는데,
이걸 안 건드려서 한참 삽질을 또 했다.
(Device side mqtt-packet/constants.js)
/* Authenticate */
protocol.AUTHENTICATE_HEADER = Buffer.from([protocol.codes['authenticate'] << protocol.CMD_SHIFT])
저 파일을 열고 밑으로 스크롤 죽죽 내려보면
HEADER 를 generate 해서 명시해두는 부분이 있는데,
이게 Server side 에서 쓰는 mqtt-packet library 버전이랑 달라서 ㅠㅠ
Server 에서는 그냥 connect function 적당히 수정하면 HEADER parsing 도 같이 처리됐었는데
여기선 따로 저렇게 constants.js 파일에서 관리한다.
꼭꼭 명시해줘야됨..
저걸 모르고 그냥 돌렸더니 세상에
authenticate 형식으로 잘 파싱해놓고
보낼 때 connect로 보내버린다.
저 코드를 constants.js 에 추가해줬다면
다시 writeToStream.js 파일로 돌아와서 복붙해서 짜깁기해둔 authenticate parsing function 에다가
// Generate header
stream.write(protocol.AUTHENTICATE_HEADER)
라고 수정해주면 된다.
여기까지하면 device에서 authenticate packet 을 보내는 것도 잘 된다!
Server 에서 저 authenticate packet 을 parsing 해서 받는 부분은
Device 에서 받을 때와 동일한 파일들을 동일하게 수정해주면 된다.
다행히 이 부분은 버전별 차이가 없다 ㅠㅠ 다행
길고 긴 여정이 끝났다!
이렇게 하면
Mqtt Packet 에 Command Type 추가하기 완료!!!
흐하항행복해
이제 추가한 타입을 멋대로 가져다 쓰면 된다
이벤트도 받고 패킷도 쏘고~~~ 앗싸링!!
아 근데 글 하나 더 있다. ㅎ
'Programming > Security' 카테고리의 다른 글
[MQTT / Mosca] Mqtt Packet 에 Command Type 추가하기 (for authentication) #5 [마지막] (1) | 2017.10.21 |
---|---|
[MQTT / Mosca] Mqtt Packet 에 Command Type 추가하기 (for authentication) #3 (0) | 2017.10.20 |
[MQTT / Mosca] Mqtt Packet 에 Command Type 추가하기 (for authentication) #2 (0) | 2017.10.20 |
[MQTT / Mosca] Mqtt Packet 에 Command Type 추가하기 (for authentication) #1 (0) | 2017.10.19 |
대칭키와 비대칭키 (0) | 2017.09.13 |