@@ -18,6 +18,7 @@ proc processClient(s: AsyncSocket) {.async.} =
1818 let ctx = MqttCtx ()
1919 ctx.s = s
2020 ctx.state = Connecting
21+ ctx.sslOn = mqttbroker.sslOn
2122
2223 while ctx.state in [Connecting , Connected ]:
2324 try :
@@ -34,15 +35,26 @@ proc processClient(s: AsyncSocket) {.async.} =
3435 ctx.state = Error
3536 break
3637
37- if ctx.state == Error :
38+ if not ctx.beenConnected:
39+ # When the client hasn't been connected to the broker, then we don't
40+ # want to check for LWT, subscriptions etc. - we just want to close
41+ # and remove the socket!
42+ try :
43+ ctx.s.close ()
44+ except :
45+ if mqttbroker.verbosity >= 3 and ctx.sslOn:
46+ wrn (" Someone is trying to connect, properly without SSL." )
47+ return
48+
49+ if ctx.state == Error and ctx.beenConnected:
3850 # This happens on a ungraceful disconnects from the client.
3951 asyncCheck sendWill (ctx)
4052
41- if ctx.state in [Disconnected , Error ]:
53+ if ctx.state in [Disconnected , Error ] and ctx.beenConnected :
4254 # Remove the client from the register for subscribers.
4355 asyncCheck removeSubscriber (ctx)
4456
45- if ctx.state != Disabled :
57+ if ctx.state != Disabled and ctx.beenConnected :
4658 # The clients `state` is set to `Disabled`, if we cannot accept their
4759 # `Connect`-packet and respond with a `ConnAck`. Since we dont accept
4860 # the connection, the client is never added to `mqttbroker.connections`.
@@ -54,24 +66,35 @@ proc processClient(s: AsyncSocket) {.async.} =
5466 if mqttbroker.retained[top].clientid == ctx.clientid:
5567 mqttbroker.retained.del (top)
5668
57- if not ctx.s.isClosed ():
69+ if not ctx.s.isClosed () and ctx.beenConnected :
5870 ctx.s.close ()
5971 ctx.state = Disabled
6072
6173 if mqttbroker.verbosity >= 3 :
6274 verbose (ctx)
6375
6476
65-
6677proc serve (host: string , port: int ) {.async .} =
6778 var broker = newAsyncSocket ()
6879 broker.setSockOpt (OptReuseAddr , true )
6980 broker.bindAddr (Port (port), host)
7081 broker.listen ()
7182
72- while true :
73- let client = await broker.accept ()
74- asyncCheck processClient (client)
83+ if mqttbroker.sslOn:
84+ if not fileExists (mqttbroker.sslCert) or not fileExists (mqttbroker.sslKey):
85+ echo " SSL cert or key does not exist. Check the path or generate them:\n " &
86+ " openssl req -x509 -nodes -days 365 -newkey rsa:4096 -keyout mykey.pem -out mycert.pem"
87+ var sslCtx = newContext (certFile = mqttbroker.sslCert, keyFile = mqttbroker.sslKey)
88+ wrapSocket (sslCtx, broker)
89+ while true :
90+ let client = await broker.accept ()
91+ wrapConnectedSocket (sslCtx, client, handshakeAsServer)
92+ asyncCheck processClient (client)
93+
94+ else :
95+ while true :
96+ let client = await broker.accept ()
97+ asyncCheck processClient (client)
7598
7699
77100proc showConf (mb: MqttBroker , configfile: string ) =
@@ -143,6 +166,12 @@ proc loadConf(mb: MqttBroker, config: string) =
143166 mqttbroker.passClientId = parseBool (dict.getSectionValue (" " ," clientid_pass" ))
144167 mqttbroker.clientKickOld = parseBool (dict.getSectionValue (" " ," client_kickold" ))
145168 mqttbroker.maxConnections = parseInt (dict.getSectionValue (" " ," max_conn" ))
169+ mqttbroker.sslCert = dict.getSectionValue (" " ," ssl_certificate" )
170+ mqttbroker.sslKey = dict.getSectionValue (" " ," ssl_key" )
171+
172+ # If both certificate and key are present use SSL.
173+ if mqttbroker.sslCert != " " and mqttbroker.sslKey != " " :
174+ mqttbroker.sslOn = true
146175
147176 # If anonymous login is allowed return
148177 if not parseBool (dict.getSectionValue (" " ," allow_anonymous" )):
@@ -154,20 +183,18 @@ proc loadConf(mb: MqttBroker, config: string) =
154183
155184proc handler () {.noconv .} =
156185 # # Catch ctrl+c from user
186+ echo " "
157187 if mqttbroker.verbosity >= 3 :
158- echo " "
159188 verbose (mqttbroker)
160- echo " \n Quitting..\n "
161189 quit ()
162- setControlCHook (handler)
163190
164191
165192proc nmqttBroker (config= " " , host= " 127.0.0.1" , port= 1883 , verbosity= 0 , max_conn= 0 ,
166193 clientid_maxlen= 60 , clientid_spaces= false , clientid_empty= false ,
167- client_kickold= false , clientid_pass= false , password_file= " "
194+ client_kickold= false , clientid_pass= false , password_file= " " ,
195+ ssl= false , ssl_cert= " " , ssl_key= " "
168196 ) {.async .} =
169197 # # CLI tool for a MQTT broker
170-
171198 if config != " " :
172199 loadConf (mqttbroker, config)
173200 else :
@@ -181,30 +208,33 @@ proc nmqttBroker(config="", host="127.0.0.1", port=1883, verbosity=0, max_conn=0
181208 mqttbroker.clientKickOld = client_kickold
182209 mqttbroker.passClientId = clientid_pass
183210 mqttbroker.maxConnections = max_conn
184- # mqttbroker.retainExpire = 3600
185211
186212 if password_file != " " :
187213 loadPasswords (password_file)
188- echo mqttbroker.passwords
189214
190- # if ssl:
191- # newContext()
192- # path to Key and Cert
193- # if passwords:
194- # password & username
215+ if ssl:
216+ mqttbroker.sslOn = true
217+ mqttbroker.sslCert = ssl_cert
218+ mqttbroker.sslKey = ssl_key
195219
196220 if mqttbroker.verbosity >= 1 :
197221 showConf (mqttbroker, config)
198222
199- # let broker = newAsyncSocket()
200-
201223 asyncCheck serve (host, port)
202224
225+ setControlCHook (handler)
226+
227+ runForever ()
228+
203229
204230
205231when isMainModule :
206232
207- let topLvlUse = """ ${doc}
233+ let topLvlUse = """ nmqtt version """ & nmqttVersion & """
234+
235+
236+ nmqtt is a MQTT v3.1.1 broker
237+
208238USAGE
209239 $command [options]
210240 $command [-c /path/to/config.conf]
@@ -225,7 +255,6 @@ $options
225255 clCfg.hTabCols = @ [clOptKeys, clDescrip]
226256
227257 dispatchGen (nmqttBroker,
228- doc= " nmqtt is a MQTT v3.1.1 broker" ,
229258 cmdName= " nmqtt" ,
230259 help= {
231260 " config" : " absolute path to the config file. Overrides all other options." ,
@@ -238,16 +267,20 @@ $options
238267 " clientid-empty" : " allow empty clientid and assign random id. Defaults to false." ,
239268 " client-kickold" : " kick old client, if new client has same clientid. Defaults to false." ,
240269 " clientid-pass" : " pass clientid in payload {clientid:payload}. Defaults to false." ,
241- " password-file" : " absolute path to the password file"
270+ " password-file" : " absolute path to the password file" ,
271+ " ssl" : " activate ssl for the broker - requires --ssl-cert and --ssl-key." ,
272+ " ssl-cert" : " absolute path to the ssl certificate." ,
273+ " ssl-key" : " absolute path to the ssl key."
242274 },
243275 short= {
244276 " help" : '?' ,
245- " max-conn" : '\0 '
277+ " max-conn" : '\0 ' ,
278+ " ssl" : '\0 ' ,
279+ " ssl-cert" : '\0 ' ,
280+ " ssl-key" : '\0 '
246281 },
247282 usage= topLvlUse,
248283 dispatchName= " brokerCli"
249284 )
250285
251- asyncCheck brokerCli (skipHelp= true )
252-
253- runForever ()
286+ cligenQuit brokerCli (skipHelp= true )
0 commit comments